[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n"
  },
  {
    "path": "README.md",
    "content": "# 基于 MVP 的 Android 组件化开发框架实践\n\n## 一、背景\n\n当我们的项目变得越来越大，代码变得越来越臃肿，耦合会越来越多，编译速度越来越慢，开发效率也会变得越来越低，怎么办？这个时候我们就需要对旧项目进行重构，即是模块的拆分，官方的说法就是组件化。\n\n## 二、简介\n\n那什么是组件化呢？其基本理念是：把常用的功能、控件、基础类、第三方库、权限等公共部分抽离封装，我们称之为基础组件（`baselibs`）；把业务分成 N 个模块进行独立的管理，每一个模块我们称之为业务组件；而所有的业务组件都需要依赖于封装的基础组件，业务组件之间不做依赖，这样的目的是为了让每一个业务模块都能单独运行。而在 APP 层对整个项目的模块进行封装。\n\n> 业务模块之间的跳转可以通过路由（`Arouter`）实现；业务模块之间的通信可以通过消息（`EventBus`）来实现。\n\n## 三、基础搭建 \n\n#### 1、组件框架图\n\n![](/art/00.png)\n\n#### 2、根据组件框架图搭建的项目结构图\n\n![项目结构图](/art/01.png)\n\n#### 3、接下来介绍每个模块\n\n项目中总共有五个 `module` ，包括 3 个业务模块、一个基础模块和一个 `APP` 壳模块。\n\n在建好项目之后我们需要给 3 个 `module` 配置 “集成开发模式” 和 “组件开发模式” 的切换开关，可以在 `gradle.properties` 文件中定义变量 `isModel` ，`isModel=false` 代表是 “集成开发模式” , `isModel=true` 代表是 “组件开发模式” （**注：每次修改isModel的值后一定要Sysn才会生效**）。\n\n![](/art/02.png)\n\n**1）APP 壳模块**\n\n主要就是集成每一个模块，最终打包成一个完整的 `apk` ，其中 `gradle` 做了如下配置，根据配置文件中的 `isModel` 字段来依赖不同的业务组件；\n\n![](/art/03.png)\n\n**2）baselibs 模块**\n\n主要负责封装公共部分，如 `MVP` 架构、 `BaseView` 的封装、网络请求库、图片加载库、工具类以及自定义控件等；\n> 为了防止重复依赖，所有的第三方库都放在这个模块，业务模块不做任何第三方依赖，只依赖于 `baselibs` 模块。\n\n`baselibs` 模块的结构如下：\n\n![](/art/04.png)\n\n在 `baselibs` 模块的 `gradle` 中引入的库\n\n```\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    configurations {\n        all*.exclude group: 'com.android.support', module: 'support-v13'\n    }\n    testImplementation rootProject.ext.testDeps[\"junit\"]\n    androidTestImplementation rootProject.ext.testDeps[\"runner\"]\n    androidTestImplementation rootProject.ext.testDeps[\"espresso-core\"]\n    //leakCanary\n    debugApi rootProject.ext.testDeps[\"leakcanary-debug\"]\n    releaseApi rootProject.ext.testDeps[\"leakcanary-release\"]\n    // Support库\n    api rootProject.ext.supportLibs\n    // 网络请求库\n    api rootProject.ext.networkLibs\n    // RxJava2\n    api rootProject.ext.rxJavaLibs\n    // commonLibs\n    api rootProject.ext.commonLibs\n    kapt rootProject.ext.otherDeps[\"arouter-compiler\"]\n}\n```\n\n**3）业务模块（module_news、module_video、module_me）**\n\n每一个业务模块在 “集成开发模式” 下以 `library` 的形式存在；在 “组件开发模式” 下以 `application` 的形式存在，可以单独运行。\n\n由于每个业务模块的配置文件都差不多，下面就以 `module_news` 模块为例；\n\n以下是 `module_news` 模块的 `gradle` 配置文件：\n\n```\nif (isModule.toBoolean()) {\n    apply plugin: 'com.android.application'\n} else {\n    apply plugin: 'com.android.library'\n}\nandroid {\n    if (isModule.toBoolean()) {\n        applicationId \"com.cxz.module.me\"\n    }\n    compileSdkVersion rootProject.ext.android.compileSdkVersion\n    defaultConfig {\n        minSdkVersion rootProject.ext.android.minSdkVersion\n        targetSdkVersion rootProject.ext.android.targetSdkVersion\n        versionCode 1\n        versionName \"1.0\"\n    }\n}\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    testImplementation rootProject.ext.testDeps[\"junit\"]\n    androidTestImplementation rootProject.ext.testDeps[\"runner\"]\n    androidTestImplementation rootProject.ext.testDeps[\"espresso-core\"]\n    implementation project(':baselibs')\n    kapt rootProject.ext.otherDeps[\"arouter-compiler\"]\n}\n```\n\n**4）配置文件 `config.gradle` ，对项目中的第三库、 `app` 的版本等配置**\n\n```\next {\n    android = [\n            compileSdkVersion: 28,\n            buildToolsVersion: \"28.0.3\",\n            minSdkVersion    : 16,\n            targetSdkVersion : 27,\n            versionCode      : 1,\n            versionName      : \"1.0.0\"\n    ]\n    dependVersion = [\n            androidSupportSdkVersion: \"28.0.0\",\n            espressoSdkVersion      : \"3.0.2\",\n            retrofitSdkVersion      : \"2.4.0\",\n            glideSdkVersion         : \"4.8.0\",\n            rxJava                  : \"2.2.2\",\n            rxAndroid               : \"2.1.0\",\n            rxKotlin                : \"2.3.0\",\n            anko                    : \"0.10.7\"\n    ]\n    supportDeps = [\n            \"supportv4\"        : \"com.android.support:support-v4:${dependVersion.androidSupportSdkVersion}\",\n            \"appcompatv7\"      : \"com.android.support:appcompat-v7:${dependVersion.androidSupportSdkVersion}\",\n            \"cardview\"         : \"com.android.support:cardview-v7:${dependVersion.androidSupportSdkVersion}\",\n            \"design\"           : \"com.android.support:design:${dependVersion.androidSupportSdkVersion}\",\n            \"constraint-layout\": \"com.android.support.constraint:constraint-layout:1.1.3\",\n            \"annotations\"      : \"com.android.support:support-annotations:${dependVersion.androidSupportSdkVersion}\"\n    ]\n    retrofit = [\n            \"retrofit\"                : \"com.squareup.retrofit2:retrofit:${dependVersion.retrofitSdkVersion}\",\n            \"retrofitConverterGson\"   : \"com.squareup.retrofit2:converter-gson:${dependVersion.retrofitSdkVersion}\",\n            \"retrofitAdapterRxjava2\"  : \"com.squareup.retrofit2:adapter-rxjava2:${dependVersion.retrofitSdkVersion}\",\n            \"okhttp3LoggerInterceptor\": 'com.squareup.okhttp3:logging-interceptor:3.11.0',\n            \"retrofitConverterMoshi\"  : 'com.squareup.retrofit2:converter-moshi:2.4.0',\n            \"retrofitKotlinMoshi\"     : \"com.squareup.moshi:moshi-kotlin:1.7.0\"\n    ]\n    rxJava = [\n            \"rxJava\"   : \"io.reactivex.rxjava2:rxjava:${dependVersion.rxJava}\",\n            \"rxAndroid\": \"io.reactivex.rxjava2:rxandroid:${dependVersion.rxAndroid}\",\n            \"rxKotlin\" : \"io.reactivex.rxjava2:rxkotlin:${dependVersion.rxKotlin}\",\n            \"anko\"     : \"org.jetbrains.anko:anko:${dependVersion.anko}\"\n    ]\n    testDeps = [\n            \"junit\"                    : 'junit:junit:4.12',\n            \"runner\"                   : 'com.android.support.test:runner:1.0.2',\n            \"espresso-core\"            : \"com.android.support.test.espresso:espresso-core:${dependVersion.espressoSdkVersion}\",\n            \"espresso-contrib\"         : \"com.android.support.test.espresso:espresso-contrib:${dependVersion.espressoSdkVersion}\",\n            \"espresso-intents\"         : \"com.android.support.test.espresso:espresso-intents:${dependVersion.espressoSdkVersion}\",\n            \"leakcanary-debug\"         : 'com.squareup.leakcanary:leakcanary-android:1.6.1',\n            \"leakcanary-release\"       : 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1',\n            \"leakcanary-debug-fragment\": 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.1',\n            \"debug-db\"                 : 'com.amitshekhar.android:debug-db:1.0.4'\n    ]\n    commonDeps = [\n            \"multidex\": 'com.android.support:multidex:1.0.3',\n            \"logger\"  : 'com.orhanobut:logger:2.2.0',\n            \"glide\"   : 'com.github.bumptech.glide:glide:4.8.0',\n            \"eventbus\": 'org.greenrobot:eventbus:3.1.1',\n            \"spinkit\" : 'com.github.ybq:Android-SpinKit:1.2.0',\n            \"arouter\" : 'com.alibaba:arouter-api:1.4.0'\n    ]\n    otherDeps = [\n            \"arouter-compiler\": 'com.alibaba:arouter-compiler:1.2.1'\n    ]\n    supportLibs = supportDeps.values()\n    networkLibs = retrofit.values()\n    rxJavaLibs = rxJava.values()\n    commonLibs = commonDeps.values()\n}\n\n```\n\n**最后别忘记在工程的中 `build.gradle` 引入该配置文件**\n\n```\napply from: \"config.gradle\"\n```\n\n## 四、业务模块之间交互\n\n> 业务模块之间的跳转可以通过路由（`Arouter`）实现；业务模块之间的通信可以通过消息（`EventBus`）来实现。\n\n#### 1、Arouter 实现业务模块之间的跳转\n\n我们在之前已经依赖了 `Arouter` （详细用法参照：[https://github.com/alibaba/ARouter](https://github.com/alibaba/ARouter)），用它来实现跳转只需要以下两步：\n\n**第一步**\n\n- `gradle` 配置\n\n```\nkapt {\n    arguments {\n        arg(\"AROUTER_MODULE_NAME\", project.getName())\n    }\n    generateStubs = true\n}\ndependencies {\n...\n    kapt rootProject.ext.otherDeps[\"arouter-compiler\"]\n}\n```\n\n**第二步**\n\n- 需要指明目标页面以及要带的参数，然后在调用 `navigation()` 方法；\n\n![](/art/05.png)\n\n**第三步**\n\n- 首先在 `onCreate` 方法调用 `ARouter.getInstance().inject(this)` 注入；\n- 然后要用 `@Route` 注解标注页面，并在 `path` 变量中给页面定义一个路径；\n- 最后对于传送过来的变量我们直接定义一个同名的字段用 `@Autowired` 变量标注，`Arouter` 会对该字段自动赋值\n\n![](/art/06.png)\n\n#### 2、EventBus 实现业务模块之间的通讯\n\n利用第三方如 `EventBus` 对消息进行管理。在 `baselibs` 组件中的 `BaseActivity` 、 `BaseFragment` 类做了对消息的简单封装，子类只需要重写 `useEventBus()` 返回 `true` 即可对事件的注册。\n\n## 五、搭建过程中遇到的问题\n\n#### 1、AndroidManifest \n\n我们知道 `APP` 在打包的时候最后会把所有的 `AndroidManifest` 进行合并，所以每个业务组件的 `Activity` 只需要在各自的模块中注册即可。\n\n如果业务组件要单独运行，则需要单独的一个 `AndroidManifest` ，在 `gradle` 的 `sourceSets` 加载不同的 `AndroidManifest` 即可。\n\n![](/art/07.png) \n\n**gradle 配置**\n\n```\nandroid {\n...\n    sourceSets {\n        main {\n            if (isModule.toBoolean()) {\n                manifest.srcFile 'src/main/module/AndroidManifest.xml'\n            } else {\n                manifest.srcFile 'src/main/AndroidManifest.xml'\n                //集成开发模式下排除debug文件夹中的所有Java文件\n                java {\n                    exclude 'debug/**'\n                }\n                kotlin {\n                    exclude 'debug/**'\n                }\n            }\n        }\n    }\n...\n}\n```\n\n**注意：集成模式下的 AndroidManifest 不需要配置 Application ，组件模式下的 AndroidManifest 需要单独配置 Application 。**\n\n#### 2、资源文件冲突的问题\n\n不同业务组件里的资源文件的名称可能相同，所以就可能出现资源文件冲突的问题，我们可以通过设置资源的前缀来防止资源文件的冲突。\n\n![](/art/08.png)\n\n**gradle 配置，以 module_news 模块为例**\n\n```\nandroid {\n...\n    resourcePrefix \"news_\"\n...\n}\n```\n\n这样配置以后，如果我们在命名资源文件没有加前缀的时候，编译器就会提示我们没加前缀。\n\n**至此， `Android` 基本组件化框架已经搭建完成，如有错误之处还请指正。**\n\n## 五、最后\n\n**完整的项目地址：[https://github.com/iceCola7/AndroidModuleSamples](https://github.com/iceCola7/AndroidModuleSamples)**\n\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\napply plugin: 'kotlin-kapt'\n\nkapt {\n    arguments {\n        arg(\"AROUTER_MODULE_NAME\", project.getName())\n    }\n    generateStubs = true\n}\n\nandroid {\n    compileSdkVersion rootProject.ext.android.compileSdkVersion\n    defaultConfig {\n        applicationId \"com.cxz.module.samples\"\n        minSdkVersion rootProject.ext.android.minSdkVersion\n        targetSdkVersion rootProject.ext.android.targetSdkVersion\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'\n\n        multiDexEnabled true\n\n    }\n\n    resourcePrefix \"app_\"\n\n    buildTypes {\n        debug {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n\n        release {\n            minifyEnabled true\n            shrinkResources true\n            zipAlignEnabled true\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n\n    testImplementation rootProject.ext.testDeps[\"junit\"]\n    androidTestImplementation rootProject.ext.testDeps[\"runner\"]\n    androidTestImplementation rootProject.ext.testDeps[\"espresso-core\"]\n\n    // leakCanary\n    debugImplementation rootProject.ext.testDeps[\"leakcanary-debug\"]\n    releaseImplementation rootProject.ext.testDeps[\"leakcanary-release\"]\n\n    kapt rootProject.ext.annotationProcessorDeps[\"arouter-compiler\"]\n\n    // implementation project(':baselibs')\n//    if (!isModule.toBoolean()) {\n//        implementation project(':module_news')\n//        implementation project(':module_video')\n//        implementation project(':module_me')\n//    }\n    if (!isMeModule.toBoolean()){\n        implementation project(':module_me')\n    }\n    if (!isNewsModule.toBoolean()){\n        implementation project(':module_news')\n    }\n    if (!isVideoModule.toBoolean()){\n        implementation project(':module_video')\n    }\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "app/src/androidTest/java/com/cxz/module/samples/ExampleInstrumentedTest.kt",
    "content": "package com.cxz.module.samples\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport org.junit.Assert.*\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ExampleInstrumentedTest {\n    @Test\n    fun useAppContext() {\n        // Context of the app under test.\n        val appContext = InstrumentationRegistry.getTargetContext()\n        assertEquals(\"com.cxz.module.samples\", appContext.packageName)\n    }\n}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.cxz.module.samples\">\n\n    <application\n        android:name=\".app.App\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\".MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n\n                <action android:name=\"android.intent.action.VIEW\" />\n            </intent-filter>\n        </activity>\n        <activity android:name=\".OtherActivity\" />\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/cxz/module/samples/MainActivity.kt",
    "content": "package com.cxz.module.samples\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.cxz.kotlin.baselibs.ext.showToast\nimport com.cxz.kotlin.baselibs.provider.NewsService\nimport kotlinx.android.synthetic.main.app_activity_main.*\n\n@Route(path = \"/app/main\")\nclass MainActivity : AppCompatActivity() {\n\n    @Autowired(name = \"/news/service\")\n    @JvmField\n    var newsService: NewsService? = null\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        ARouter.getInstance().inject(this)\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.app_activity_main)\n\n        btn_news.setOnClickListener {\n            ARouter.getInstance().build(\"/news/main\") // 目标页面\n                .withString(\"key1\", \"test_key1\")  // 参数\n                .withString(\"key2\", \"test_key2\")  // 参数\n                .navigation()\n        }\n        btn_video.setOnClickListener {\n            val bundle = Bundle()\n            bundle.putString(\"key1\", \"test_key1\")\n            bundle.putString(\"key2\", \"test_key2\")\n            ARouter.getInstance().build(\"/video/main\")\n                .with(bundle)\n                .navigation()\n        }\n        btn_me.setOnClickListener {\n            ARouter.getInstance().build(\"/me/main\")\n                .navigation()\n        }\n        btn_news_service.setOnClickListener {\n            showToast(newsService?.getNewsName().toString())\n        }\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/cxz/module/samples/OtherActivity.kt",
    "content": "package com.cxz.module.samples\n\nimport androidx.appcompat.app.AppCompatActivity\nimport android.os.Bundle\nimport com.alibaba.android.arouter.facade.annotation.Route\n\n@Route(path = \"/app/other\")\nclass OtherActivity : AppCompatActivity() {\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.app_activity_other)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/cxz/module/samples/app/App.kt",
    "content": "package com.cxz.module.samples.app\n\nimport android.app.Activity\nimport android.app.Application\nimport android.content.Context\nimport android.os.Bundle\nimport androidx.multidex.MultiDex\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.cxz.kotlin.baselibs.BuildConfig\nimport com.cxz.kotlin.baselibs.config.AppConfig\nimport com.cxz.kotlin.baselibs.utils.NLog\nimport com.squareup.leakcanary.LeakCanary\nimport com.squareup.leakcanary.RefWatcher\n\n/**\n * @author chenxz\n * @date 2018/12/22\n * @desc\n */\nclass App : Application() {\n\n    private val TAG = \"App\"\n\n    private var refWatcher: RefWatcher? = null\n\n    companion object {\n        fun getRefWatcher(context: Context): RefWatcher? {\n            val app = context.applicationContext as App\n            return app.refWatcher\n        }\n    }\n\n    override fun onCreate() {\n        super.onCreate()\n        AppConfig.init(this)\n        initLeakCanary()\n        initRouter()\n    }\n\n    private fun initLeakCanary() {\n        refWatcher = if (LeakCanary.isInAnalyzerProcess(this))\n            RefWatcher.DISABLED\n        else LeakCanary.install(this)\n\n        registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks)\n    }\n\n    private fun initRouter() {\n        if (BuildConfig.DEBUG) {\n            ARouter.openLog()\n            ARouter.openDebug()\n        }\n        ARouter.init(this)\n    }\n\n    override fun attachBaseContext(base: Context?) {\n        super.attachBaseContext(base)\n        MultiDex.install(this)\n    }\n\n    private val mActivityLifecycleCallbacks = object : ActivityLifecycleCallbacks {\n        override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {\n            NLog.d(TAG, \"onCreated: \" + activity.componentName.className)\n        }\n\n        override fun onActivityStarted(activity: Activity) {\n            NLog.d(TAG, \"onStart: \" + activity.componentName.className)\n        }\n\n        override fun onActivityResumed(activity: Activity) {\n            NLog.d(TAG, \"onResume: \" + activity.componentName.className)\n        }\n\n        override fun onActivityPaused(activity: Activity) {\n            NLog.d(TAG, \"onPause: \" + activity.componentName.className)\n        }\n\n        override fun onActivityStopped(activity: Activity) {\n            NLog.d(TAG, \"onStop: \" + activity.componentName.className)\n        }\n\n        override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {\n            NLog.d(TAG, \"onSaveInstanceState: \" + activity.componentName.className)\n        }\n\n        override fun onActivityDestroyed(activity: Activity) {\n            NLog.d(TAG, \"onDestroy: \" + activity.componentName.className)\n        }\n    }\n\n}"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:height=\"108dp\"\n        android:width=\"108dp\"\n        android:viewportHeight=\"108\"\n        android:viewportWidth=\"108\">\n    <path android:fillColor=\"#008577\"\n          android:pathData=\"M0,0h108v108h-108z\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M9,0L9,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,0L19,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M29,0L29,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M39,0L39,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M49,0L49,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M59,0L59,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M69,0L69,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M79,0L79,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M89,0L89,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M99,0L99,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,9L108,9\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,19L108,19\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,29L108,29\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,39L108,39\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,49L108,49\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,59L108,59\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,69L108,69\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,79L108,79\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,89L108,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,99L108,99\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,29L89,29\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,39L89,39\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,49L89,49\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,59L89,59\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,69L89,69\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,79L89,79\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M29,19L29,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M39,19L39,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M49,19L49,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M59,19L59,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M69,19L69,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M79,19L79,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        xmlns:aapt=\"http://schemas.android.com/aapt\"\n        android:width=\"108dp\"\n        android:height=\"108dp\"\n        android:viewportHeight=\"108\"\n        android:viewportWidth=\"108\">\n    <path\n            android:fillType=\"evenOdd\"\n            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\"\n            android:strokeColor=\"#00000000\"\n            android:strokeWidth=\"1\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                    android:endX=\"78.5885\"\n                    android:endY=\"90.9159\"\n                    android:startX=\"48.7653\"\n                    android:startY=\"61.0927\"\n                    android:type=\"linear\">\n                <item\n                        android:color=\"#44000000\"\n                        android:offset=\"0.0\"/>\n                <item\n                        android:color=\"#00000000\"\n                        android:offset=\"1.0\"/>\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n            android:fillColor=\"#FFFFFF\"\n            android:fillType=\"nonZero\"\n            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\"\n            android:strokeColor=\"#00000000\"\n            android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/layout/app_activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    tools:context=\".MainActivity\">\n\n    <Button\n        android:id=\"@+id/btn_news\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"News\" />\n\n    <Button\n        android:id=\"@+id/btn_video\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Video\" />\n\n    <Button\n        android:id=\"@+id/btn_me\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Me\" />\n\n    <Button\n        android:id=\"@+id/btn_news_service\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"测试NewsService\" />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/app_activity_other.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".OtherActivity\">\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\"/>\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\"/>\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#008577</color>\n    <color name=\"colorPrimaryDark\">#00574B</color>\n    <color name=\"colorAccent\">#D81B60</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">AndroidModuleSamples</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/test/java/com/cxz/module/samples/ExampleUnitTest.kt",
    "content": "package com.cxz.module.samples\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}\n"
  },
  {
    "path": "baselibs/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "baselibs/build.gradle",
    "content": "apply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\napply plugin: 'kotlin-kapt'\n\nkapt {\n    arguments {\n        arg(\"AROUTER_MODULE_NAME\", project.getName())\n    }\n}\nandroid {\n    compileSdkVersion rootProject.ext.android.compileSdkVersion\n    defaultConfig {\n        minSdkVersion rootProject.ext.android.minSdkVersion\n        targetSdkVersion rootProject.ext.android.targetSdkVersion\n        versionCode 1\n        versionName \"1.0.0\"\n\n        multiDexEnabled true\n\n    }\n\n    // resourcePrefix \"base_\"\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    lintOptions {\n        abortOnError false\n    }\n\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    api \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n\n    implementation rootProject.ext.androisxLibs\n    // Support库\n    // api rootProject.ext.supportLibs\n    // 网络请求库\n    api rootProject.ext.networkLibs\n    // RxJava2\n    api rootProject.ext.rxJavaLibs\n    // commonLibs\n    api rootProject.ext.commonLibs\n\n    kapt rootProject.ext.annotationProcessorLibs\n\n}\n\n"
  },
  {
    "path": "baselibs/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "baselibs/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"com.cxz.kotlin.baselibs\">\n\n    <!-- 获取GSM网络信息状态，如当前的网络连接是否有效 -->\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n    <!-- 允许访问Wi-Fi网络状态信息 -->\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>\n    <!-- 允许获取Wi-Fi网络状态改变信息 -->\n    <uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>\n    <!-- 访问网络 -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <!-- 读取手机状态 -->\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>\n    <!-- SD 卡读写权限 -->\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>\n\n</manifest>\n"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseActivity.kt",
    "content": "package com.cxz.kotlin.baselibs.base\n\nimport android.content.pm.ActivityInfo\nimport android.os.Bundle\nimport android.view.MenuItem\nimport android.view.MotionEvent\nimport android.view.WindowManager\nimport androidx.annotation.ColorInt\nimport androidx.annotation.LayoutRes\nimport androidx.appcompat.app.AppCompatActivity\nimport com.cxz.kotlin.baselibs.utils.CommonUtil\nimport com.cxz.kotlin.baselibs.utils.KeyBoardUtil\nimport com.cxz.kotlin.baselibs.utils.StatusBarUtil\nimport com.tbruyelle.rxpermissions2.RxPermissions\nimport org.greenrobot.eventbus.EventBus\n\n/**\n * @author chenxz\n * @date 2018/11/19\n * @desc BaseActivity\n */\nabstract class BaseActivity : AppCompatActivity() {\n\n    /**\n     * 布局文件id\n     */\n    @LayoutRes\n    protected abstract fun attachLayoutRes(): Int\n\n    /**\n     * 初始化数据\n     */\n    open fun initData() {}\n\n    /**\n     * 初始化 View\n     */\n    abstract fun initView()\n\n    /**\n     * 开始请求\n     */\n    abstract fun start()\n\n    /**\n     * 是否使用 EventBus\n     */\n    open fun useEventBus(): Boolean = false\n\n    /**\n     * 获取权限处理类\n     */\n    protected val rxPermissions: RxPermissions by lazy {\n        RxPermissions(this)\n    }\n\n    /**\n     * 设置状态栏的背景颜色\n     */\n    fun setStatusBarColor(@ColorInt color: Int) {\n        StatusBarUtil.setColor(this, color, 0)\n    }\n\n    /**\n     * 设置状态栏图标的颜色\n     *\n     * @param dark true: 黑色  false: 白色\n     */\n    fun setStatusBarIcon(dark: Boolean) {\n        if (dark) {\n            StatusBarUtil.setLightMode(this)\n        } else {\n            StatusBarUtil.setDarkMode(this)\n        }\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)\n        super.onCreate(savedInstanceState)\n        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT // 强制竖屏\n        setContentView(attachLayoutRes())\n        if (useEventBus()) {\n            EventBus.getDefault().register(this)\n        }\n        initView()\n        initData()\n        start()\n    }\n\n    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {\n        if (ev?.action == MotionEvent.ACTION_UP) {\n            val v = currentFocus\n            // 如果不是落在EditText区域，则需要关闭输入法\n            if (KeyBoardUtil.isHideKeyboard(v, ev)) {\n                KeyBoardUtil.hideKeyBoard(this, v)\n            }\n        }\n        return super.dispatchTouchEvent(ev)\n    }\n\n    override fun onOptionsItemSelected(item: MenuItem): Boolean {\n        when (item.itemId) {\n            android.R.id.home -> {\n                onBackPressed()\n            }\n        }\n        return super.onOptionsItemSelected(item)\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        if (useEventBus()) {\n            EventBus.getDefault().unregister(this)\n        }\n        CommonUtil.fixInputMethodManagerLeak(this)\n    }\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseFragment.kt",
    "content": "package com.cxz.kotlin.baselibs.base\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.annotation.LayoutRes\nimport com.tbruyelle.rxpermissions2.RxPermissions\nimport org.greenrobot.eventbus.EventBus\n\n/**\n * @author chenxz\n * @date 2018/11/19\n * @desc BaseFragment\n */\nabstract class BaseFragment : androidx.fragment.app.Fragment() {\n\n    /**\n     * 视图是否加载完毕\n     */\n    private var isViewPrepare = false\n\n    /**\n     * 数据是否加载过了\n     */\n    private var hasLoadData = false\n\n    /**\n     * 加载布局\n     */\n    @LayoutRes\n    abstract fun attachLayoutRes(): Int\n\n    /**\n     * 初始化数据\n     */\n    open fun initData() {}\n\n    /**\n     * 初始化 View\n     */\n    abstract fun initView(view: View)\n\n    /**\n     * 懒加载\n     */\n    abstract fun lazyLoad()\n\n    /**\n     * 是否使用 EventBus\n     */\n    open fun useEventBus(): Boolean = false\n\n    /**\n     * 获取权限处理类\n     */\n    protected val rxPermissions: RxPermissions by lazy {\n        RxPermissions(this)\n    }\n\n    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {\n        return inflater.inflate(attachLayoutRes(), null)\n    }\n\n    override fun setUserVisibleHint(isVisibleToUser: Boolean) {\n        super.setUserVisibleHint(isVisibleToUser)\n        if (isVisibleToUser) {\n            lazyLoadDataIfPrepared()\n        }\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        if (useEventBus()) EventBus.getDefault().register(this)\n        isViewPrepare = true\n        initView(view)\n        initData()\n        lazyLoadDataIfPrepared()\n    }\n\n    private fun lazyLoadDataIfPrepared() {\n        if (userVisibleHint && isViewPrepare && !hasLoadData) {\n            lazyLoad()\n            hasLoadData = true\n        }\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        if (useEventBus()) EventBus.getDefault().unregister(this)\n    }\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseMvpActivity.kt",
    "content": "package com.cxz.kotlin.baselibs.base\n\nimport com.cxz.kotlin.baselibs.ext.showToast\nimport com.cxz.kotlin.baselibs.mvp.IPresenter\nimport com.cxz.kotlin.baselibs.mvp.IView\n\n/**\n * @author chenxz\n * @date 2018/11/19\n * @desc BaseMvpActivity\n */\nabstract class BaseMvpActivity<in V : IView, P : IPresenter<V>> : BaseActivity(), IView {\n\n    /**\n     * Presenter\n     */\n    protected var mPresenter: P? = null\n\n    protected abstract fun createPresenter(): P\n\n    override fun initView() {\n        mPresenter = createPresenter()\n        mPresenter?.attachView(this as V)\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        mPresenter?.detachView()\n        this.mPresenter = null\n    }\n\n    override fun showLoading() {\n    }\n\n    override fun hideLoading() {\n    }\n\n    override fun showError(errorMsg: String) {\n        showToast(errorMsg)\n    }\n\n    override fun showDefaultMsg(msg: String) {\n        showToast(msg)\n    }\n\n    override fun showMsg(msg: String) {\n        showToast(msg)\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseMvpFragment.kt",
    "content": "package com.cxz.kotlin.baselibs.base\n\nimport android.view.View\nimport com.cxz.kotlin.baselibs.ext.showToast\nimport com.cxz.kotlin.baselibs.mvp.IPresenter\nimport com.cxz.kotlin.baselibs.mvp.IView\n\n/**\n * @author chenxz\n * @date 2018/11/19\n * @desc BaseMvpFragment\n */\nabstract class BaseMvpFragment<in V : IView, P : IPresenter<V>> : BaseFragment(), IView {\n\n    /**\n     * Presenter\n     */\n    protected var mPresenter: P? = null\n\n    protected abstract fun createPresenter(): P\n\n    override fun initView(view: View) {\n        mPresenter = createPresenter()\n        mPresenter?.attachView(this as V)\n    }\n\n    override fun onDestroyView() {\n        super.onDestroyView()\n        mPresenter?.detachView()\n        this.mPresenter = null\n    }\n\n    override fun showLoading() {\n    }\n\n    override fun hideLoading() {\n    }\n\n    override fun showError(errorMsg: String) {\n        showToast(errorMsg)\n    }\n\n    override fun showDefaultMsg(msg: String) {\n        showToast(msg)\n    }\n\n    override fun showMsg(msg: String) {\n        showToast(msg)\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/base/BaseMvpTitleActivity.kt",
    "content": "package com.cxz.kotlin.baselibs.base\n\nimport androidx.annotation.ColorRes\nimport androidx.annotation.StringRes\nimport com.cxz.kotlin.baselibs.R\nimport com.cxz.kotlin.baselibs.mvp.IPresenter\nimport com.cxz.kotlin.baselibs.mvp.IView\nimport kotlinx.android.synthetic.main.activity_base_title.*\nimport kotlinx.android.synthetic.main.base_toolbar.*\n\n/**\n * @author chenxz\n * @date 2018/11/20\n * @desc BaseMvpTitleActivity\n */\nabstract class BaseMvpTitleActivity<in V : IView, P : IPresenter<V>> : BaseMvpActivity<V, P>() {\n\n    /**\n     * 子布局文件id\n     */\n    protected abstract fun attachChildLayoutRes(): Int\n\n    /**\n     * 是否启用返回键\n     */\n    open fun hasBackIcon(): Boolean = true\n\n    override fun attachLayoutRes(): Int = R.layout.activity_base_title\n\n    override fun initView() {\n        super.initView()\n        base_container.addView(layoutInflater.inflate(attachChildLayoutRes(), null))\n        base_toolbar.title = \"\"\n        setSupportActionBar(base_toolbar)\n        supportActionBar?.setDisplayHomeAsUpEnabled(hasBackIcon())\n    }\n\n    /**\n     * 设置 Title\n     */\n    fun setBaseTitle(title: String) {\n        base_title_tv.text = title\n    }\n\n    /**\n     * 设置 Title\n     */\n    fun setBaseTitleText(@StringRes resId: Int) {\n        base_title_tv.setText(resId)\n    }\n\n    /**\n     * 设置 Title 颜色\n     */\n    fun setBaseTitleColor(@ColorRes color: Int) {\n        base_title_tv.setTextColor(resources.getColor(color))\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/bean/BaseBean.kt",
    "content": "package com.cxz.kotlin.baselibs.bean\n\n/**\n * @author admin\n * @date 2018/11/21\n * @desc\n */\nopen class BaseBean {\n    var errorCode: Int = 0\n    var errorMsg: String = \"\"\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/config/AppConfig.kt",
    "content": "package com.cxz.kotlin.baselibs.config\n\nimport android.app.Application\n\n/**\n * @author chenxz\n * @date 2019/11/1\n * @desc 用来初始化项目所需要的配置\n */\nobject AppConfig {\n\n    const val TAG = \"KotlinMVP\"\n\n    var debug = false\n\n    private var application: Application? = null\n\n    /**\n     * Init, it must be call before used .\n     */\n    fun init(application: Application) {\n        this.application = application\n    }\n\n    fun getApplication(): Application {\n        if (application == null) {\n            throw RuntimeException(\"Please init in Application#onCreate first.\")\n        }\n        return application!!\n    }\n\n    fun openDebug() {\n        debug = true\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/ext/CommonExt.kt",
    "content": "package com.cxz.kotlin.baselibs.ext\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.Intent\nimport android.content.res.Resources\nimport android.net.Uri\nimport android.view.View\nimport android.widget.Checkable\nimport android.widget.TextView\nimport androidx.core.content.ContextCompat\nimport androidx.fragment.app.Fragment\nimport com.cxz.kotlin.baselibs.R\nimport com.cxz.kotlin.baselibs.config.AppConfig\nimport com.cxz.kotlin.baselibs.utils.NLog\nimport com.cxz.kotlin.baselibs.widget.CustomToast\nimport com.google.android.material.snackbar.Snackbar\n\n/**\n * @author chenxz\n * @date 2018/11/20\n * @desc\n */\n\nfun Any.loge(content: String?) {\n    val tag = this.javaClass.simpleName ?: AppConfig.TAG\n    NLog.e(tag, content ?: \"\")\n}\n\nfun dp2px(dpValue: Float): Int {\n    return (0.5f + dpValue * Resources.getSystem().displayMetrics.density).toInt()\n}\n\nfun Fragment.showToast(content: String) {\n    CustomToast(this.activity?.applicationContext, content).show()\n}\n\nfun Context.showToast(content: String) {\n    CustomToast(this, content).show()\n}\n\nfun Activity.showSnackMsg(msg: String) {\n    val snackbar = Snackbar.make(this.window.decorView, msg, Snackbar.LENGTH_SHORT)\n    val view = snackbar.view\n    view.findViewById<TextView>(R.id.snackbar_text)\n        .setTextColor(ContextCompat.getColor(this, R.color.white))\n    snackbar.show()\n}\n\nfun Fragment.showSnackMsg(msg: String) {\n    this.activity ?: return\n    val snackbar =\n        Snackbar.make(this.activity!!.window.decorView, msg, com.google.android.material.snackbar.Snackbar.LENGTH_SHORT)\n    val view = snackbar.view\n    view.findViewById<TextView>(R.id.snackbar_text)\n        .setTextColor(ContextCompat.getColor(this.activity!!, R.color.white))\n    snackbar.show()\n}\n\nfun Context.openBrowser(url: String) {\n    Intent(Intent.ACTION_VIEW, Uri.parse(url)).run { startActivity(this) }\n}\n\n// 扩展点击事件属性(重复点击时长)\nvar <T : View> T.lastClickTime: Long\n    set(value) = setTag(1766613352, value)\n    get() = getTag(1766613352) as? Long ?: 0\n\n// 重复点击事件绑定\ninline fun <T : View> T.setSingleClickListener(time: Long = 1000, crossinline block: (T) -> Unit) {\n    setOnClickListener {\n        val currentTimeMillis = System.currentTimeMillis()\n        if (currentTimeMillis - lastClickTime > time || this is Checkable) {\n            lastClickTime = currentTimeMillis\n            block(this)\n        }\n    }\n}\n"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/ext/RxExt.kt",
    "content": "package com.cxz.kotlin.baselibs.ext\n\nimport com.cxz.kotlin.baselibs.bean.BaseBean\nimport com.cxz.kotlin.baselibs.http.HttpStatus\nimport com.cxz.kotlin.baselibs.http.exception.ExceptionHandle\nimport com.cxz.kotlin.baselibs.http.function.RetryWithDelay\nimport com.cxz.kotlin.baselibs.mvp.IModel\nimport com.cxz.kotlin.baselibs.mvp.IView\nimport com.cxz.kotlin.baselibs.rx.SchedulerUtils\nimport com.cxz.kotlin.baselibs.utils.NetWorkUtil\nimport io.reactivex.Observable\nimport io.reactivex.Observer\nimport io.reactivex.disposables.Disposable\n\n/**\n * @author chenxz\n * @date 2018/11/20\n * @desc\n */\n\nfun <T : BaseBean> Observable<T>.ss(\n    model: IModel?,\n    view: IView?,\n    isShowLoading: Boolean = true,\n    onSuccess: (T) -> Unit,\n    onError: ((T) -> Unit)? = null\n) {\n    this.compose(SchedulerUtils.ioToMain())\n        .retryWhen(RetryWithDelay())\n        .subscribe(object : Observer<T> {\n            override fun onComplete() {\n                view?.hideLoading()\n            }\n\n            override fun onSubscribe(d: Disposable) {\n                if (isShowLoading) view?.showLoading()\n                model?.addDisposable(d)\n                if (!NetWorkUtil.isConnected()) {\n                    view?.showDefaultMsg(\"当前网络不可用，请检查网络设置\")\n                    d.dispose()\n                    onComplete()\n                }\n            }\n\n            override fun onNext(t: T) {\n                view?.hideLoading()\n                when {\n                    t.errorCode == HttpStatus.SUCCESS -> onSuccess.invoke(t)\n                    t.errorCode == HttpStatus.TOKEN_INVALID -> {\n                        // Token 过期，重新登录\n                    }\n                    else -> {\n                        if (onError != null) {\n                            onError.invoke(t)\n                        } else {\n                            if (t.errorMsg.isNotEmpty())\n                                view?.showDefaultMsg(t.errorMsg)\n                        }\n                    }\n                }\n            }\n\n            override fun onError(t: Throwable) {\n                view?.hideLoading()\n                view?.showError(ExceptionHandle.handleException(t))\n            }\n        })\n}\n\nfun <T : BaseBean> Observable<T>.sss(\n    view: IView?,\n    isShowLoading: Boolean = true,\n    onSuccess: (T) -> Unit,\n    onError: ((T) -> Unit)? = null\n): Disposable {\n    if (isShowLoading) view?.showLoading()\n    return this.compose(SchedulerUtils.ioToMain())\n        .retryWhen(RetryWithDelay())\n        .subscribe({\n            if (isShowLoading) view?.showLoading()\n            when {\n                it.errorCode == HttpStatus.SUCCESS -> onSuccess.invoke(it)\n                it.errorCode == HttpStatus.TOKEN_INVALID -> {\n                    // Token 过期，重新登录\n                }\n                else -> {\n                    if (onError != null) {\n                        onError.invoke(it)\n                    } else {\n                        if (it.errorMsg.isNotEmpty())\n                            view?.showDefaultMsg(it.errorMsg)\n                    }\n                }\n            }\n        }, {\n            view?.hideLoading()\n            view?.showError(ExceptionHandle.handleException(it))\n        })\n}\n"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/HttpStatus.kt",
    "content": "package com.cxz.kotlin.baselibs.http\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nobject HttpStatus {\n    /**\n     * 响应成功\n     */\n    const val SUCCESS = 0\n\n    /**\n     * Token 过期\n     */\n    const val TOKEN_INVALID = 401\n\n    /**\n     * 未知错误\n     */\n    const val UNKNOWN_ERROR = 1002\n\n    /**\n     * 服务器内部错误\n     */\n    const val SERVER_ERROR = 1003\n\n    /**\n     * 网络连接超时\n     */\n    const val NETWORK_ERROR = 1004\n\n    /**\n     * API解析异常（或者第三方数据结构更改）等其他异常\n     */\n    const val API_ERROR = 1005\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/RetrofitFactory.kt",
    "content": "package com.cxz.kotlin.baselibs.http\n\nimport com.cxz.kotlin.baselibs.config.AppConfig\nimport com.cxz.kotlin.baselibs.http.constant.HttpConstant\nimport com.cxz.kotlin.baselibs.http.interceptor.CacheInterceptor\nimport com.cxz.kotlin.baselibs.http.interceptor.CookieInterceptor\nimport com.cxz.kotlin.baselibs.http.interceptor.HeaderInterceptor\nimport okhttp3.Cache\nimport okhttp3.OkHttpClient\nimport okhttp3.logging.HttpLoggingInterceptor\nimport retrofit2.Retrofit\nimport retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory\nimport retrofit2.converter.moshi.MoshiConverterFactory\nimport java.io.File\nimport java.util.concurrent.TimeUnit\n\n/**\n * @author admin\n * @date 2018/11/21\n * @desc RetrofitFactory\n */\nabstract class RetrofitFactory<T> {\n\n    var service: T\n    private var mBaseUrl = \"\"\n    private var retrofit: Retrofit? = null\n\n    abstract fun baseUrl(): String\n\n    abstract fun getService(): Class<T>\n\n    init {\n        mBaseUrl = this.baseUrl()\n        if (mBaseUrl.isEmpty()) {\n            throw RuntimeException(\"base url can not be empty!\")\n        }\n        service = getRetrofit()!!.create(this.getService())\n    }\n\n    /**\n     * 获取 Retrofit 实例对象\n     */\n    private fun getRetrofit(): Retrofit? {\n        if (retrofit == null) {\n            synchronized(RetrofitFactory::class.java) {\n                if (retrofit == null) {\n                    retrofit = Retrofit.Builder()\n                        .baseUrl(mBaseUrl)  // baseUrl\n                        .client(attachOkHttpClient())\n                        //.addConverterFactory(GsonConverterFactory.create())\n                        .addConverterFactory(MoshiConverterFactory.create())\n                        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())\n                        .build()\n                }\n            }\n        }\n        return retrofit\n    }\n\n    /**\n     * 获取 OkHttpClient 实例对象\n     * 子类可重写，自定义 OkHttpClient\n     */\n    open fun attachOkHttpClient(): OkHttpClient {\n        val builder = OkHttpClient().newBuilder()\n        val httpLoggingInterceptor = HttpLoggingInterceptor()\n        if (AppConfig.debug) {\n            httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY\n        } else {\n            httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.NONE\n        }\n\n        //设置 请求的缓存的大小跟位置\n        val cacheFile = File(AppConfig.getApplication().cacheDir, \"cache\")\n        val cache = Cache(cacheFile, HttpConstant.MAX_CACHE_SIZE)\n\n        builder.run {\n            addInterceptor(httpLoggingInterceptor)\n            addInterceptor(HeaderInterceptor())\n            addInterceptor(CookieInterceptor())\n            addInterceptor(CacheInterceptor())\n            cache(cache)  //添加缓存\n            connectTimeout(HttpConstant.DEFAULT_TIMEOUT, TimeUnit.SECONDS)\n            readTimeout(HttpConstant.DEFAULT_TIMEOUT, TimeUnit.SECONDS)\n            writeTimeout(HttpConstant.DEFAULT_TIMEOUT, TimeUnit.SECONDS)\n            retryOnConnectionFailure(true) // 错误重连\n            // cookieJar(CookieManager())\n        }\n        return builder.build()\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/constant/HttpConstant.kt",
    "content": "package com.cxz.kotlin.baselibs.http.constant\n\n/**\n * @author chenxz\n * @date 2018/11/21\n * @desc HttpConstant\n */\nobject HttpConstant {\n\n    const val DEFAULT_TIMEOUT: Long = 15\n\n    const val MAX_CACHE_SIZE: Long = 1024 * 1024 * 50 // 50M 的缓存大小\n\n    const val TOKEN_KEY = \"token\"\n    const val SET_COOKIE_KEY = \"set-cookie\"\n    const val COOKIE_NAME = \"Cookie\"\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/cookies/CookieManager.kt",
    "content": "package com.cxz.kotlin.baselibs.http.cookies\n\nimport okhttp3.Cookie\nimport okhttp3.CookieJar\nimport okhttp3.HttpUrl\n\n/**\n * Created by chenxz on 2018/6/6.\n */\nclass CookieManager : CookieJar {\n\n    private val COOKIE_STORE = PersistentCookieStore()\n\n    override fun saveFromResponse(url: HttpUrl?, cookies: MutableList<Cookie>?) {\n        cookies ?: return\n        url ?: return\n        if (cookies.size > 0) {\n            for (cookie in cookies) {\n                COOKIE_STORE.add(url, cookie)\n            }\n        }\n    }\n\n    override fun loadForRequest(url: HttpUrl): List<Cookie> = COOKIE_STORE.get(url)\n\n    /**\n     * 清除所有cookie\n     */\n    fun clearAllCookies() {\n        COOKIE_STORE.removeAll()\n    }\n\n    /**\n     * 清除指定cookie\n     *\n     * @param url HttpUrl\n     * @param cookie Cookie\n     * @return if clear cookies\n     */\n    fun clearCookies(url: HttpUrl, cookie: Cookie): Boolean {\n        return COOKIE_STORE.remove(url, cookie)\n    }\n\n    /**\n     * 获取cookies\n     *\n     * @return List<Cookie>\n    </Cookie> */\n    fun getCookies(): List<Cookie> {\n        return COOKIE_STORE.getCookies()\n    }\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/cookies/OkHttpCookies.kt",
    "content": "package com.cxz.kotlin.baselibs.http.cookies\n\nimport okhttp3.Cookie\nimport java.io.IOException\nimport java.io.ObjectInputStream\nimport java.io.ObjectOutputStream\nimport java.io.Serializable\n\n/**\n * Created by chenxz on 2018/6/6.\n */\nclass OkHttpCookies : Serializable {\n\n    private lateinit var cookies: Cookie\n    private var clientCookies: Cookie? = null\n\n    private constructor()\n\n    constructor(cookies: Cookie?) {\n        this.cookies = cookies!!\n    }\n\n    fun getCookies(): Cookie? {\n        var bestCookies: Cookie = cookies\n        if (clientCookies != null) {\n            bestCookies = clientCookies as Cookie\n        }\n        return bestCookies\n    }\n\n    @Throws(IOException::class)\n    private fun writeObject(out: ObjectOutputStream) {\n        out.writeObject(cookies.name())\n        out.writeObject(cookies.value())\n        out.writeLong(cookies.expiresAt())\n        out.writeObject(cookies.domain())\n        out.writeObject(cookies.path())\n        out.writeBoolean(cookies.secure())\n        out.writeBoolean(cookies.httpOnly())\n        out.writeBoolean(cookies.hostOnly())\n        out.writeBoolean(cookies.persistent())\n    }\n\n    @Throws(IOException::class, ClassNotFoundException::class)\n    private fun readObject(`in`: ObjectInputStream) {\n        val name = `in`.readObject() as String\n        val value = `in`.readObject() as String\n        val expiresAt = `in`.readLong()\n        val domain = `in`.readObject() as String\n        val path = `in`.readObject() as String\n        val secure = `in`.readBoolean()\n        val httpOnly = `in`.readBoolean()\n        val hostOnly = `in`.readBoolean()\n        var builder = Cookie.Builder()\n        builder = builder.name(name)\n        builder = builder.value(value)\n        builder = builder.expiresAt(expiresAt)\n        builder = if (hostOnly) builder.hostOnlyDomain(domain) else builder.domain(domain)\n        builder = builder.path(path)\n        builder = if (secure) builder.secure() else builder\n        builder = if (httpOnly) builder.httpOnly() else builder\n        clientCookies = builder.build()\n    }\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/cookies/PersistentCookieStore.kt",
    "content": "package com.cxz.kotlin.baselibs.http.cookies\n\nimport android.content.Context\nimport android.content.SharedPreferences\nimport android.text.TextUtils\nimport com.cxz.kotlin.baselibs.config.AppConfig\nimport com.cxz.kotlin.baselibs.utils.NLog\nimport okhttp3.Cookie\nimport okhttp3.HttpUrl\nimport java.io.*\nimport java.util.*\nimport java.util.concurrent.ConcurrentHashMap\n\n/**\n * Created by chenxz on 2018/6/6.\n */\nclass PersistentCookieStore {\n\n    private val LOG_TAG: String = \"PersistentCookieStore\"\n    private val COOKIE_PREFS: String = \"Cookies_Prefs\"\n\n    private val cookies: HashMap<String, ConcurrentHashMap<String, Cookie>> = HashMap()\n\n    private val cookiePrefs: SharedPreferences\n\n    init {\n        cookiePrefs = AppConfig.getApplication().getSharedPreferences(COOKIE_PREFS, Context.MODE_PRIVATE)\n        val prefsMap = cookiePrefs.all\n\n        for (entry in prefsMap) {\n            val cookieNames = TextUtils.split(entry.value as String, \",\")\n            for (name in cookieNames) {\n                val encodedCookie = cookiePrefs.getString(name, null)\n                if (encodedCookie != null) {\n                    val decodedCookie = decodeCookie(encodedCookie)\n                    if (decodedCookie != null) {\n                        if (!cookies.containsKey(entry.key)) {\n                            cookies.put(entry.key, ConcurrentHashMap())\n                        }\n                        cookies[entry.key]?.put(name, decodedCookie)\n                    }\n                }\n            }\n        }\n\n    }\n\n    private fun getCookieToken(cookie: Cookie): String = cookie.name() + \"@\" + cookie.domain()\n\n    fun add(url: HttpUrl, cookie: Cookie) {\n        val name = getCookieToken(cookie)\n        //将cookies缓存到内存中 如果缓存过期 就重置此cookie\n        if (!cookie.persistent()) {\n            if (!cookies.containsKey(url.host())) {\n                cookies.put(url.host(), ConcurrentHashMap(10))\n            }\n            cookies[url.host()]?.put(name, cookie)\n        } else {\n            if (cookies.containsKey(url.host())) {\n                cookies[url.host()]?.remove(name)\n            }\n        }\n        // 将Cookie持久化到本地\n        val prefsWriter: SharedPreferences.Editor = cookiePrefs.edit()\n        cookies[url.host()]?.entries?.let {\n            prefsWriter.putString(url.host(), TextUtils.join(\",\", it))\n        }\n        prefsWriter.putString(name, encodeCookie(OkHttpCookies(cookie)))\n        prefsWriter.apply()\n    }\n\n    fun get(url: HttpUrl): List<Cookie> {\n        val list: ArrayList<Cookie> = ArrayList()\n        if (cookies.containsKey(url.host())) {\n            list.addAll(cookies[url.host()]?.values!!)\n        }\n        return list\n    }\n\n    fun removeAll() {\n        val prefsWriter = cookiePrefs.edit()\n        prefsWriter.clear()\n        prefsWriter.apply()\n        for (key in cookies.keys) {\n            cookies.remove(key)\n        }\n    }\n\n    fun remove(url: HttpUrl, cookie: Cookie): Boolean {\n        val name = getCookieToken(cookie)\n        return if (cookies.containsKey(url.host()) && cookies[url.host()]?.containsKey(name)!!) {\n            cookies[url.host()]?.remove(name)\n\n            val prefsWriter = cookiePrefs.edit()\n            if (cookiePrefs.contains(name)) {\n                prefsWriter.remove(name)\n            }\n            cookies[url.host()]?.keys?.let {\n                prefsWriter.putString(url.host(), TextUtils.join(\",\", it))\n            }\n            prefsWriter.apply()\n\n            true\n        } else {\n            false\n        }\n    }\n\n    fun getCookies(): List<Cookie> {\n        val ret = ArrayList<Cookie>()\n        for (key in cookies.keys) {\n            ret.addAll(cookies[key]?.values!!)\n        }\n        return ret\n    }\n\n    /**\n     * cookies 序列化成 string\n     *\n     * @param cookie 要序列化的cookie\n     * @return 序列化之后的string\n     */\n    private fun encodeCookie(cookie: OkHttpCookies?): String? {\n        if (cookie == null) {\n            return null\n        }\n        val os = ByteArrayOutputStream()\n        try {\n            val outputStream = ObjectOutputStream(os)\n            outputStream.writeObject(cookie)\n        } catch (e: IOException) {\n            NLog.d(LOG_TAG, \"IOException in encodeCookie $e\")\n            return null\n        }\n\n        return byteArrayToHexString(os.toByteArray())\n    }\n\n    /**\n     * 将字符串反序列化成cookies\n     *\n     * @param cookieString cookies string\n     * @return cookie object\n     */\n    private fun decodeCookie(cookieString: String): Cookie? {\n        val bytes = hexStringToByteArray(cookieString)\n        val byteArrayInputStream = ByteArrayInputStream(bytes)\n        var cookie: Cookie? = null\n        try {\n            val objectInputStream = ObjectInputStream(byteArrayInputStream)\n            cookie = (objectInputStream.readObject() as OkHttpCookies).getCookies()\n        } catch (e: IOException) {\n            NLog.d(LOG_TAG, \"IOException in decodeCookie $e\")\n        } catch (e: ClassNotFoundException) {\n            NLog.d(LOG_TAG, \"ClassNotFoundException in decodeCookie $e\")\n        }\n\n        return cookie\n    }\n\n    /**\n     * 二进制数组转十六进制字符串\n     *\n     * @param bytes byte array to be converted\n     * @return string containing hex values\n     */\n    private fun byteArrayToHexString(bytes: ByteArray): String {\n        val sb = StringBuilder(bytes.size * 2)\n        for (element in bytes) {\n            val v = element.toInt() and 0xff\n            if (v < 16) {\n                sb.append('0')\n            }\n            sb.append(Integer.toHexString(v))\n        }\n        return sb.toString().toUpperCase(Locale.US)\n    }\n\n    /**\n     * 十六进制字符串转二进制数组\n     *\n     * @param hexString string of hex-encoded values\n     * @return decoded byte array\n     */\n    private fun hexStringToByteArray(hexString: String): ByteArray {\n        val len = hexString.length\n        val data = ByteArray(len / 2)\n        var i = 0\n        while (i < len) {\n            data[i / 2] = ((Character.digit(hexString[i], 16) shl 4) + Character.digit(hexString[i + 1], 16)).toByte()\n            i += 2\n        }\n        return data\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/exception/ApiException.kt",
    "content": "package com.cxz.kotlin.baselibs.http.exception\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nclass ApiException : RuntimeException {\n\n    private var code: Int? = null\n\n    constructor(throwable: Throwable, code: Int) : super(throwable) {\n        this.code = code\n    }\n\n    constructor(message: String) : super(Throwable(message))\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/exception/ExceptionHandle.kt",
    "content": "package com.cxz.kotlin.baselibs.http.exception\n\nimport com.cxz.kotlin.baselibs.http.HttpStatus\nimport com.google.gson.JsonParseException\nimport com.orhanobut.logger.Logger\nimport org.json.JSONException\nimport retrofit2.HttpException\nimport java.net.ConnectException\nimport java.net.SocketTimeoutException\nimport java.net.UnknownHostException\nimport java.text.ParseException\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nclass ExceptionHandle {\n    companion object {\n        private const val TAG = \"ExceptionHandle\"\n        var errorCode = HttpStatus.UNKNOWN_ERROR\n        var errorMsg = \"请求失败，请稍后重试\"\n\n        fun handleException(e: Throwable): String {\n            e.printStackTrace()\n            if (e is SocketTimeoutException\n                || e is ConnectException\n                || e is HttpException\n            ) { //均视为网络错误\n                Logger.e(TAG, \"网络连接异常: \" + e.message)\n                errorMsg = \"网络连接异常\"\n                errorCode = HttpStatus.NETWORK_ERROR\n            } else if (e is JsonParseException\n                || e is JSONException\n                || e is ParseException\n            ) {   //均视为解析错误\n                Logger.e(TAG, \"数据解析异常: \" + e.message)\n                errorMsg = \"数据解析异常\"\n                errorCode = HttpStatus.SERVER_ERROR\n            } else if (e is ApiException) {//服务器返回的错误信息\n                errorMsg = e.message.toString()\n                errorCode = HttpStatus.SERVER_ERROR\n            } else if (e is UnknownHostException) {\n                Logger.e(TAG, \"网络连接异常: \" + e.message)\n                errorMsg = \"网络连接异常\"\n                errorCode = HttpStatus.NETWORK_ERROR\n            } else if (e is IllegalArgumentException) {\n                errorMsg = \"参数错误\"\n                errorCode = HttpStatus.SERVER_ERROR\n            } else {//未知错误\n                try {\n                    Logger.e(TAG, \"错误: \" + e.message)\n                } catch (e1: Exception) {\n                    Logger.e(TAG, \"未知错误Debug调试 \")\n                }\n                errorMsg = \"未知错误，可能抛锚了吧~\"\n                errorCode = HttpStatus.UNKNOWN_ERROR\n            }\n            return errorMsg\n        }\n\n    }\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/function/RetryWithDelay.kt",
    "content": "package com.cxz.kotlin.baselibs.http.function\n\nimport io.reactivex.Observable\nimport io.reactivex.functions.BiFunction\nimport io.reactivex.functions.Function\nimport retrofit2.HttpException\nimport java.net.ConnectException\nimport java.net.SocketTimeoutException\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.TimeoutException\n\n/**\n * @author chenxz\n * @date 2018/8/21\n * @desc 请求重连\n */\nclass RetryWithDelay : Function<Observable<out Throwable>, Observable<*>> {\n\n    private var maxRetryCount = 3 // 可重试次数\n    private var retryDelayMillis: Long = 3000 // 重试等待时间\n\n    constructor() {}\n\n    constructor(retryDelayMillis: Long) {\n        this.retryDelayMillis = retryDelayMillis\n    }\n\n    constructor(maxRetryCount: Int, retryDelayMillis: Long) {\n        this.maxRetryCount = maxRetryCount\n        this.retryDelayMillis = retryDelayMillis\n    }\n\n    @Throws(Exception::class)\n    override fun apply(observable: Observable<out Throwable>): Observable<*> {\n        return observable\n            .zipWith(Observable.range(1, maxRetryCount + 1),\n                BiFunction<Throwable, Int, Wrapper> { t1, t2 -> Wrapper(t2, t1) })\n            .flatMap { wrapper ->\n                val t = wrapper.throwable\n                if ((t is ConnectException\n                            || t is SocketTimeoutException\n                            || t is TimeoutException\n                            || t is HttpException)\n                    && wrapper.index < maxRetryCount + 1\n                ) {\n                    Observable.timer(retryDelayMillis * wrapper.index, TimeUnit.MILLISECONDS)\n                } else Observable.error<Any>(wrapper.throwable)\n            }\n    }\n\n    private inner class Wrapper(val index: Int, val throwable: Throwable)\n\n}\n"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/interceptor/CacheInterceptor.kt",
    "content": "package com.cxz.kotlin.baselibs.http.interceptor\n\nimport com.cxz.kotlin.baselibs.config.AppConfig\nimport com.cxz.kotlin.baselibs.utils.NetWorkUtil\nimport okhttp3.CacheControl\nimport okhttp3.Interceptor\nimport okhttp3.Response\n\n/**\n * @author chenxz\n * @date 2018/9/26\n * @desc CacheInterceptor: 设置缓存\n */\nclass CacheInterceptor : Interceptor {\n\n    override fun intercept(chain: Interceptor.Chain): Response {\n        var request = chain.request()\n        if (!NetWorkUtil.isConnected(AppConfig.getApplication())) {\n            request = request.newBuilder()\n                    .cacheControl(CacheControl.FORCE_CACHE)\n                    .build()\n        }\n        val response = chain.proceed(request)\n        if (NetWorkUtil.isConnected(AppConfig.getApplication())) {\n            val maxAge = 0\n            // 有网络时 设置缓存超时时间0个小时 ,意思就是不读取缓存数据,只对get有用,post没有缓冲\n            response.newBuilder()\n                    .header(\"Cache-Control\", \"public, max-age=$maxAge\")\n                    .removeHeader(\"Retrofit\")// 清除头信息，因为服务器如果不支持，会返回一些干扰信息，不清除下面无法生效\n                    .build()\n        } else {\n            // 无网络时，设置超时为4周  只对get有用,post没有缓冲\n            val maxStale = 60 * 60 * 24 * 28\n            response.newBuilder()\n                    .header(\"Cache-Control\", \"public, only-if-cached, max-stale=$maxStale\")\n                    .removeHeader(\"nyn\")\n                    .build()\n        }\n        return response\n    }\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/interceptor/CookieInterceptor.kt",
    "content": "package com.cxz.kotlin.baselibs.http.interceptor\n\nimport com.cxz.kotlin.baselibs.http.constant.HttpConstant\nimport com.cxz.kotlin.baselibs.utils.Preference\nimport okhttp3.Interceptor\nimport okhttp3.Response\n\n/**\n * @author chenxz\n * @date 2018/9/26\n * @desc CookieInterceptor: 保存 Cookie\n */\nclass CookieInterceptor : Interceptor {\n\n    override fun intercept(chain: Interceptor.Chain): Response {\n        val request = chain.request()\n        val response = chain.proceed(request)\n        val requestUrl = request.url().toString()\n        val domain = request.url().host()\n\n        // set-cookie maybe has multi, login to save cookie\n        if (\n        //(requestUrl.contains(HttpConstant.SAVE_USER_LOGIN_KEY) || requestUrl.contains(HttpConstant.SAVE_USER_REGISTER_KEY)) &&\n            response.headers(HttpConstant.SET_COOKIE_KEY).isNotEmpty()\n        ) {\n            val cookies = response.headers(HttpConstant.SET_COOKIE_KEY)\n            val cookie = encodeCookie(cookies)\n            saveCookie(requestUrl, domain, cookie)\n        }\n        // else if (requestUrl.contains(HttpConstant.REMOVE_USER_LOGOUT_KEY) && domain.isNotEmpty()) {\n        // remove cookie\n        // Preference.clearPreference(domain)\n        // }\n        return response\n    }\n\n    private fun encodeCookie(cookies: List<String>): String {\n        val sb = StringBuilder()\n        val set = HashSet<String>()\n        cookies.map { cookie ->\n            cookie.split(\";\".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()\n        }.forEach {\n            it.filterNot { set.contains(it) }.forEach { set.add(it) }\n        }\n        val ite = set.iterator()\n        while (ite.hasNext()) {\n            val cookie = ite.next()\n            sb.append(cookie).append(\";\")\n        }\n        val last = sb.lastIndexOf(\";\")\n        if (sb.length - 1 == last) {\n            sb.deleteCharAt(last)\n        }\n        return sb.toString()\n    }\n\n    private fun saveCookie(url: String?, domain: String?, cookies: String) {\n        url ?: return\n        var spUrl: String by Preference(url, cookies)\n        @Suppress(\"UNUSED_VALUE\")\n        spUrl = cookies\n        domain ?: return\n        var spDomain: String by Preference(domain, cookies)\n        @Suppress(\"UNUSED_VALUE\")\n        spDomain = cookies\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/interceptor/HeaderInterceptor.kt",
    "content": "package com.cxz.kotlin.baselibs.http.interceptor\n\nimport com.cxz.kotlin.baselibs.http.constant.HttpConstant\nimport com.cxz.kotlin.baselibs.utils.Preference\nimport okhttp3.Interceptor\nimport okhttp3.Response\n\n/**\n * @author chenxz\n * @date 2018/9/26\n * @desc HeaderInterceptor: 设置请求头\n */\nclass HeaderInterceptor : Interceptor {\n\n    /**\n     * token\n     */\n    private var token: String by Preference(HttpConstant.TOKEN_KEY, \"\")\n\n    override fun intercept(chain: Interceptor.Chain): Response {\n\n        val request = chain.request()\n        val builder = request.newBuilder()\n\n        builder.addHeader(\"Content-type\", \"application/json; charset=utf-8\")\n        // .header(\"token\", token)\n        // .method(request.method(), request.body())\n\n        val domain = request.url().host()\n        val url = request.url().toString()\n        if (domain.isNotEmpty()) {\n            val spDomain: String by Preference(domain, \"\")\n            val cookie: String = if (spDomain.isNotEmpty()) spDomain else \"\"\n            if (cookie.isNotEmpty()) {\n                // 将 Cookie 添加到请求头\n                builder.addHeader(HttpConstant.COOKIE_NAME, cookie)\n            }\n        }\n\n        return chain.proceed(builder.build())\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/http/interceptor/QueryParameterInterceptor.kt",
    "content": "package com.cxz.kotlin.baselibs.http.interceptor\n\nimport okhttp3.Interceptor\nimport okhttp3.Request\nimport okhttp3.Response\n\n/**\n * @author chenxz\n * @date 2018/9/26\n * @desc QueryParameterInterceptor 设置公共参数\n */\nclass QueryParameterInterceptor : Interceptor {\n\n    override fun intercept(chain: Interceptor.Chain): Response {\n        val originalRequest = chain.request()\n        val request: Request\n        val modifiedUrl = originalRequest.url().newBuilder()\n                // Provide your custom parameter here\n                .addQueryParameter(\"phoneSystem\", \"\")\n                .addQueryParameter(\"phoneModel\", \"\")\n                .build()\n        request = originalRequest.newBuilder().url(modifiedUrl).build()\n        return chain.proceed(request)\n    }\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/BaseModel.kt",
    "content": "package com.cxz.kotlin.baselibs.mvp\n\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.LifecycleObserver\nimport androidx.lifecycle.LifecycleOwner\nimport androidx.lifecycle.OnLifecycleEvent\nimport io.reactivex.disposables.CompositeDisposable\nimport io.reactivex.disposables.Disposable\n\n/**\n * @author chenxz\n * @date 2018/11/18\n * @desc BaseModel\n */\nabstract class BaseModel : IModel, LifecycleObserver {\n\n    private var mCompositeDisposable: CompositeDisposable? = null\n\n    override fun addDisposable(disposable: Disposable?) {\n        if (mCompositeDisposable == null) {\n            mCompositeDisposable = CompositeDisposable()\n        }\n        disposable?.let { mCompositeDisposable?.add(it) }\n    }\n\n    override fun onDetach() {\n        unDispose()\n    }\n\n    private fun unDispose() {\n        mCompositeDisposable?.clear()  // 保证Activity结束时取消\n        mCompositeDisposable = null\n    }\n\n    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)\n    fun onDestroy(owner: LifecycleOwner) {\n        owner.lifecycle.removeObserver(this)\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/BasePresenter.kt",
    "content": "package com.cxz.kotlin.baselibs.mvp\n\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.LifecycleObserver\nimport androidx.lifecycle.LifecycleOwner\nimport androidx.lifecycle.OnLifecycleEvent\nimport io.reactivex.disposables.CompositeDisposable\nimport io.reactivex.disposables.Disposable\nimport org.greenrobot.eventbus.EventBus\n\n/**\n * @author chenxz\n * @date 2018/11/18\n * @desc BasePresenter\n */\nabstract class BasePresenter<M : IModel, V : IView> : IPresenter<V>, LifecycleObserver {\n\n    protected var mModel: M? = null\n    protected var mView: V? = null\n\n    private val isViewAttached: Boolean\n        get() = mView != null\n\n    private var mCompositeDisposable: CompositeDisposable? = null\n\n    /**\n     * 创建 Model\n     */\n    open fun createModel(): M? = null\n\n    /**\n     * 是否使用 EventBus\n     */\n    open fun useEventBus(): Boolean = false\n\n    override fun attachView(mView: V) {\n        this.mView = mView\n        mModel = createModel()\n        if (mView is LifecycleOwner) {\n            (mView as LifecycleOwner).lifecycle.addObserver(this)\n            if (mModel != null && mModel is LifecycleObserver) {\n                (mView as LifecycleOwner).lifecycle.addObserver(mModel as LifecycleObserver)\n            }\n        }\n        if (useEventBus()) {\n            EventBus.getDefault().register(this)\n        }\n    }\n\n    override fun detachView() {\n        if (useEventBus()) {\n            EventBus.getDefault().unregister(this)\n        }\n        // 保证activity结束时取消所有正在执行的订阅\n        unDispose()\n        mModel?.onDetach()\n        this.mModel = null\n        this.mView = null\n        this.mCompositeDisposable = null\n    }\n\n    open fun addDisposable(disposable: Disposable?) {\n        if (mCompositeDisposable == null) {\n            mCompositeDisposable = CompositeDisposable()\n        }\n        disposable?.let { mCompositeDisposable?.add(it) }\n    }\n\n    private fun unDispose() {\n        mCompositeDisposable?.clear()  // 保证Activity结束时取消\n        mCompositeDisposable = null\n    }\n\n    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)\n    fun onDestroy(owner: LifecycleOwner) {\n        // detachView()\n        owner.lifecycle.removeObserver(this)\n    }\n\n    open fun checkViewAttached() {\n        if (!isViewAttached) throw MvpViewNotAttachedException()\n    }\n\n    private class MvpViewNotAttachedException internal constructor() :\n        RuntimeException(\"Please call IPresenter.attachView(IBaseView) before\" + \" requesting data to the IPresenter\")\n\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/IModel.kt",
    "content": "package com.cxz.kotlin.baselibs.mvp\n\nimport io.reactivex.disposables.Disposable\n\n/**\n * @author chenxz\n * @date 2018/11/18\n * @desc IModel\n */\ninterface IModel {\n\n    fun addDisposable(disposable: Disposable?)\n\n    fun onDetach()\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/IPresenter.kt",
    "content": "package com.cxz.kotlin.baselibs.mvp\n\n/**\n * @author chenxz\n * @date 2018/11/18\n * @desc IPresenter\n */\ninterface IPresenter<in V : IView> {\n\n    /**\n     * 绑定 View\n     */\n    fun attachView(mView: V)\n\n    /**\n     * 解绑 View\n     */\n    fun detachView()\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/mvp/IView.kt",
    "content": "package com.cxz.kotlin.baselibs.mvp\n\n/**\n * @author chenxz\n * @date 2018/11/18\n * @desc IView\n */\ninterface IView {\n    /**\n     * 显示加载\n     */\n    fun showLoading()\n\n    /**\n     * 隐藏加载\n     */\n    fun hideLoading()\n\n    /**\n     * 使用默认的样式显示信息: CustomToast\n     */\n    fun showDefaultMsg(msg: String)\n\n    /**\n     * 显示信息\n     */\n    fun showMsg(msg: String)\n\n    /**\n     * 显示错误信息\n     */\n    fun showError(errorMsg: String)\n\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/provider/NewsService.kt",
    "content": "package com.cxz.kotlin.baselibs.provider\n\nimport com.alibaba.android.arouter.facade.template.IProvider\n\n/**\n * @author chenxz\n * @date 2018/12/22\n * @desc\n */\ninterface NewsService : IProvider {\n\n    fun getNewsName(): String\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/BaseObserver.kt",
    "content": "package com.cxz.kotlin.baselibs.rx\n\nimport com.cxz.kotlin.baselibs.bean.BaseBean\nimport com.cxz.kotlin.baselibs.http.HttpStatus\nimport com.cxz.kotlin.baselibs.http.exception.ExceptionHandle\nimport com.cxz.kotlin.baselibs.mvp.IView\nimport com.cxz.kotlin.baselibs.utils.NetWorkUtil\nimport io.reactivex.observers.ResourceObserver\n\n/**\n * Created by chenxz on 2018/6/11.\n */\nabstract class BaseObserver<T : BaseBean> : ResourceObserver<T> {\n\n    private var mView: IView? = null\n    private var mErrorMsg = \"\"\n    private var bShowLoading = true\n\n    constructor(view: IView) {\n        this.mView = view\n    }\n\n    constructor(view: IView, bShowLoading: Boolean) {\n        this.mView = view\n        this.bShowLoading = bShowLoading\n    }\n\n    /**\n     * 成功的回调\n     */\n    protected abstract fun onSuccess(t: T)\n\n    /**\n     * 错误的回调\n     */\n    protected fun onError(t: T) {}\n\n    override fun onStart() {\n        super.onStart()\n        if (bShowLoading) mView?.showLoading()\n        if (!NetWorkUtil.isConnected()) {\n            mView?.showDefaultMsg(\"当前网络不可用，请检查网络设置\")\n            onComplete()\n        }\n    }\n\n    override fun onNext(t: T) {\n        mView?.hideLoading()\n        when {\n            t.errorCode == HttpStatus.SUCCESS -> onSuccess(t)\n            t.errorCode == HttpStatus.TOKEN_INVALID -> {\n                // TODO Token 过期，重新登录\n            }\n            else -> {\n                onError(t)\n                if (t.errorMsg.isNotEmpty())\n                    mView?.showDefaultMsg(t.errorMsg)\n            }\n        }\n    }\n\n    override fun onError(e: Throwable) {\n        mView?.hideLoading()\n        if (mView == null) {\n            throw RuntimeException(\"mView can not be null\")\n        }\n        if (mErrorMsg.isEmpty()) {\n            mErrorMsg = ExceptionHandle.handleException(e)\n        }\n        mView?.showDefaultMsg(mErrorMsg)\n    }\n\n    override fun onComplete() {\n        mView?.hideLoading()\n    }\n}\n\n"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/BaseSubscriber.kt",
    "content": "package com.cxz.kotlin.baselibs.rx\n\nimport com.cxz.kotlin.baselibs.bean.BaseBean\nimport com.cxz.kotlin.baselibs.http.HttpStatus\nimport com.cxz.kotlin.baselibs.http.exception.ExceptionHandle\nimport com.cxz.kotlin.baselibs.mvp.IView\nimport com.cxz.kotlin.baselibs.utils.NetWorkUtil\nimport io.reactivex.subscribers.ResourceSubscriber\n\n/**\n * Created by chenxz on 2018/6/11.\n */\nabstract class BaseSubscriber<T : BaseBean> : ResourceSubscriber<T> {\n\n    private var mView: IView? = null\n    private var mErrorMsg = \"\"\n    private var bShowLoading = true\n\n    constructor(view: IView) {\n        this.mView = view\n    }\n\n    constructor(view: IView, bShowLoading: Boolean) {\n        this.mView = view\n        this.bShowLoading = bShowLoading\n    }\n\n    /**\n     * 成功的回调\n     */\n    protected abstract fun onSuccess(t: T)\n\n    /**\n     * 错误的回调\n     */\n    protected fun onError(t: T) {}\n\n    override fun onStart() {\n        super.onStart()\n        if (bShowLoading) mView?.showLoading()\n        if (!NetWorkUtil.isConnected()) {\n            mView?.showDefaultMsg(\"当前网络不可用，请检查网络设置\")\n            onComplete()\n        }\n    }\n\n    override fun onNext(t: T) {\n        mView?.hideLoading()\n        when {\n            t.errorCode == HttpStatus.SUCCESS -> onSuccess(t)\n            t.errorCode == HttpStatus.TOKEN_INVALID -> {\n                // TODO Token 过期，重新登录\n            }\n            else -> {\n                onError(t)\n                if (t.errorMsg.isNotEmpty())\n                    mView?.showDefaultMsg(t.errorMsg)\n            }\n        }\n    }\n\n    override fun onError(e: Throwable) {\n        mView?.hideLoading()\n        if (mView == null) {\n            throw RuntimeException(\"mView can not be null\")\n        }\n        if (mErrorMsg.isEmpty()) {\n            mErrorMsg = ExceptionHandle.handleException(e)\n        }\n        mView?.showDefaultMsg(mErrorMsg)\n    }\n\n    override fun onComplete() {\n        mView?.hideLoading()\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/SchedulerUtils.kt",
    "content": "package com.cxz.kotlin.baselibs.rx\n\nimport com.cxz.kotlin.baselibs.rx.scheduler.IoMainScheduler\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nobject SchedulerUtils {\n\n    fun <T> ioToMain(): IoMainScheduler<T> = IoMainScheduler()\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/BaseScheduler.kt",
    "content": "package com.cxz.kotlin.baselibs.rx.scheduler\n\nimport io.reactivex.*\nimport org.reactivestreams.Publisher\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nabstract class BaseScheduler<T> protected constructor(\n    private val subscribeOnScheduler: Scheduler,\n    private val observeOnScheduler: Scheduler\n) : ObservableTransformer<T, T>,\n    SingleTransformer<T, T>,\n    MaybeTransformer<T, T>,\n    CompletableTransformer,\n    FlowableTransformer<T, T> {\n\n    override fun apply(upstream: Completable): CompletableSource {\n        return upstream.subscribeOn(subscribeOnScheduler)\n            .observeOn(observeOnScheduler)\n    }\n\n    override fun apply(upstream: Flowable<T>): Publisher<T> {\n        return upstream.subscribeOn(subscribeOnScheduler)\n            .observeOn(observeOnScheduler)\n    }\n\n    override fun apply(upstream: Maybe<T>): MaybeSource<T> {\n        return upstream.subscribeOn(subscribeOnScheduler)\n            .observeOn(observeOnScheduler)\n    }\n\n    override fun apply(upstream: Observable<T>): ObservableSource<T> {\n        return upstream.subscribeOn(subscribeOnScheduler)\n            .observeOn(observeOnScheduler)\n    }\n\n    override fun apply(upstream: Single<T>): SingleSource<T> {\n        return upstream.subscribeOn(subscribeOnScheduler)\n            .observeOn(observeOnScheduler)\n    }\n}\n"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/ComputationMainScheduler.kt",
    "content": "package com.cxz.kotlin.baselibs.rx.scheduler\n\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactivex.schedulers.Schedulers\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nclass ComputationMainScheduler<T> private constructor() :\n    BaseScheduler<T>(Schedulers.computation(), AndroidSchedulers.mainThread())\n"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/IoMainScheduler.kt",
    "content": "package com.cxz.kotlin.baselibs.rx.scheduler\n\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactivex.schedulers.Schedulers\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nclass IoMainScheduler<T> : BaseScheduler<T>(Schedulers.io(), AndroidSchedulers.mainThread())\n"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/NewThreadMainScheduler.kt",
    "content": "package com.cxz.kotlin.baselibs.rx.scheduler\n\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactivex.schedulers.Schedulers\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nclass NewThreadMainScheduler<T> private constructor() :\n    BaseScheduler<T>(Schedulers.newThread(), AndroidSchedulers.mainThread())\n"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/SingleMainScheduler.kt",
    "content": "package com.cxz.kotlin.baselibs.rx.scheduler\n\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactivex.schedulers.Schedulers\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nclass SingleMainScheduler<T> private constructor() :\n    BaseScheduler<T>(Schedulers.single(), AndroidSchedulers.mainThread())\n"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/rx/scheduler/TrampolineMainScheduler.kt",
    "content": "package com.cxz.kotlin.baselibs.rx.scheduler\n\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactivex.schedulers.Schedulers\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nclass TrampolineMainScheduler<T> private constructor() :\n    BaseScheduler<T>(Schedulers.trampoline(), AndroidSchedulers.mainThread())\n"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/AnimatorUtil.kt",
    "content": "package com.cxz.kotlin.baselibs.utils\n\nimport android.view.View\nimport android.view.animation.AccelerateInterpolator\nimport androidx.core.view.ViewCompat\nimport androidx.core.view.ViewPropertyAnimatorListener\nimport androidx.interpolator.view.animation.LinearOutSlowInInterpolator\n\n/**\n * Created by chenxz on 2018/5/13.\n */\nobject AnimatorUtil {\n\n    private val FAST_OUT_SLOW_IN_INTERPOLATOR: LinearOutSlowInInterpolator by lazy {\n        LinearOutSlowInInterpolator()\n    }\n\n    private val LINER_INTERPOLATOR: AccelerateInterpolator by lazy {\n        AccelerateInterpolator()\n    }\n\n    /**\n     * 显示View\n     * @param view View\n     * @param listener ViewPropertyAnimatorListener\n     */\n    fun scaleShow(view: View, listener: ViewPropertyAnimatorListener) {\n        view.visibility = View.VISIBLE\n        ViewCompat.animate(view)\n            .scaleX(1.0f)\n            .scaleY(1.0f)\n            .alpha(1.0f)\n            .setDuration(800)\n            .setListener(listener)\n            .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)\n            .start()\n    }\n\n    /**\n     * 隐藏View\n     * @param view View\n     * @param listener ViewPropertyAnimatorListener\n     */\n    fun scaleHide(view: View, listener: ViewPropertyAnimatorListener) {\n        ViewCompat.animate(view)\n            .scaleX(0.0f)\n            .scaleY(0.0f)\n            .alpha(0.0f)\n            .setDuration(800)\n            .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)\n            .setListener(listener)\n            .start()\n    }\n\n    /**\n     * 显示view\n     *\n     * @param view View\n     * @param listener ViewPropertyAnimatorListener\n     */\n    fun translateShow(view: View, listener: ViewPropertyAnimatorListener) {\n        view.visibility = View.VISIBLE\n        ViewCompat.animate(view)\n            .translationY(0f)\n            .setDuration(400)\n            .setListener(listener)\n            .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)\n            .start()\n    }\n\n    /**\n     * 隐藏view\n     *\n     * @param view View\n     * @param listener ViewPropertyAnimatorListener\n     */\n    fun translateHide(view: View, listener: ViewPropertyAnimatorListener) {\n        view.visibility = View.VISIBLE\n        ViewCompat.animate(view)\n            .translationY(350f)\n            .setDuration(400)\n            .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)\n            .setListener(listener)\n            .start()\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/AppUtils.kt",
    "content": "package com.cxz.kotlin.baselibs.utils\n\nimport android.annotation.SuppressLint\nimport android.app.ActivityManager\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport java.security.MessageDigest\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nclass AppUtils private constructor() {\n    init {\n        throw Error(\"Do not need instantiate!\")\n    }\n\n    companion object {\n\n        private val DEBUG = true\n        private val TAG = \"AppUtils\"\n\n\n        /**\n         * 得到软件版本号\n         *\n         * @param context 上下文\n         * @return 当前版本Code\n         */\n        fun getVerCode(context: Context): Int {\n            var verCode = -1\n            try {\n                val packageName = context.packageName\n                verCode = context.packageManager\n                    .getPackageInfo(packageName, 0).versionCode\n            } catch (e: PackageManager.NameNotFoundException) {\n                e.printStackTrace()\n            }\n\n            return verCode\n        }\n\n\n        /**\n         * 获取应用运行的最大内存\n         *\n         * @return 最大内存\n         */\n        val maxMemory: Long\n            get() = Runtime.getRuntime().maxMemory() / 1024\n\n        /**\n         * 得到软件显示版本信息\n         *\n         * @param context 上下文\n         * @return 当前版本信息\n         */\n        fun getVerName(context: Context): String {\n            var verName = \"\"\n            try {\n                val packageName = context.packageName\n                verName = context.packageManager\n                    .getPackageInfo(packageName, 0).versionName\n            } catch (e: PackageManager.NameNotFoundException) {\n                e.printStackTrace()\n            }\n\n            return verName\n        }\n\n\n        /**\n         * 获取应用签名\n         *\n         * @param context 上下文\n         * @param pkgName 包名\n         * @return 返回应用的签名\n         */\n        fun getSign(context: Context, pkgName: String): String? {\n            return try {\n                @SuppressLint(\"PackageManagerGetSignatures\") val pis = context.packageManager\n                    .getPackageInfo(\n                        pkgName,\n                        PackageManager.GET_SIGNATURES\n                    )\n                hexDigest(pis.signatures[0].toByteArray())\n            } catch (e: PackageManager.NameNotFoundException) {\n                e.printStackTrace()\n                null\n            }\n\n        }\n\n        /**\n         * 将签名字符串转换成需要的32位签名\n         *\n         * @param paramArrayOfByte 签名byte数组\n         * @return 32位签名字符串\n         */\n        private fun hexDigest(paramArrayOfByte: ByteArray): String {\n            val hexDigits = charArrayOf(\n                48.toChar(),\n                49.toChar(),\n                50.toChar(),\n                51.toChar(),\n                52.toChar(),\n                53.toChar(),\n                54.toChar(),\n                55.toChar(),\n                56.toChar(),\n                57.toChar(),\n                97.toChar(),\n                98.toChar(),\n                99.toChar(),\n                100.toChar(),\n                101.toChar(),\n                102.toChar()\n            )\n            try {\n                val localMessageDigest = MessageDigest.getInstance(\"MD5\")\n                localMessageDigest.update(paramArrayOfByte)\n                val arrayOfByte = localMessageDigest.digest()\n                val arrayOfChar = CharArray(32)\n                var i = 0\n                var j = 0\n                while (true) {\n                    if (i >= 16) {\n                        return String(arrayOfChar)\n                    }\n                    val k = arrayOfByte[i].toInt()\n                    arrayOfChar[j] = hexDigits[0xF and k.ushr(4)]\n                    arrayOfChar[++j] = hexDigits[k and 0xF]\n                    i++\n                    j++\n                }\n            } catch (e: Exception) {\n                e.printStackTrace()\n            }\n\n            return \"\"\n        }\n\n\n        /**\n         * 获取设备的可用内存大小\n         *\n         * @param context 应用上下文对象context\n         * @return 当前内存大小\n         */\n        fun getDeviceUsableMemory(context: Context): Int {\n            val am = context.getSystemService(\n                Context.ACTIVITY_SERVICE\n            ) as ActivityManager\n            val mi = ActivityManager.MemoryInfo()\n            am.getMemoryInfo(mi)\n            // 返回当前系统的可用内存\n            return (mi.availMem / (1024 * 1024)).toInt()\n        }\n\n        /**\n         * 获取手机系统SDK版本\n         *\n         * @return 如API 17 则返回 17\n         */\n        val sdkVersion: Int\n            get() = android.os.Build.VERSION.SDK_INT\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/CommonUtil.kt",
    "content": "package com.cxz.kotlin.baselibs.utils\n\nimport android.content.Context\nimport android.graphics.Color\nimport android.text.TextUtils\nimport android.util.TypedValue\nimport android.view.View\nimport android.view.inputmethod.InputMethodManager\nimport java.io.BufferedReader\nimport java.io.FileReader\nimport java.io.IOException\nimport java.lang.reflect.Field\nimport java.util.*\n\n\n/**\n * Created by chenxz on 2018/5/14.\n */\nobject CommonUtil {\n\n    init {\n    }\n\n    fun dp2px(context: Context, dpValue: Float): Int {\n        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.resources.displayMetrics).toInt()\n    }\n\n    fun sp2px(context: Context, spValue: Float): Int {\n        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, context.resources.displayMetrics).toInt()\n    }\n\n    /**\n     * 获取状态栏高度\n     *\n     * @param context context\n     * @return 状态栏高度\n     */\n    fun getStatusBarHeight(context: Context): Int {\n        // 获得状态栏高度\n        val resourceId = context.resources.getIdentifier(\"status_bar_height\", \"dimen\", \"android\")\n        return context.resources.getDimensionPixelSize(resourceId)\n    }\n\n    /**\n     * 获取随机rgb颜色值\n     */\n    fun randomColor(): Int {\n        val random = Random()\n        //0-190, 如果颜色值过大,就越接近白色,就看不清了,所以需要限定范围\n        var red = random.nextInt(190)\n        var green = random.nextInt(190)\n        var blue = random.nextInt(190)\n//        if (SettingUtil.getIsNightMode()) {\n//            //150-255\n//            red = random.nextInt(105) + 150\n//            green = random.nextInt(105) + 150\n//            blue = random.nextInt(105) + 150\n//        }\n        //使用rgb混合生成一种新的颜色,Color.rgb生成的是一个int数\n        return Color.rgb(red, green, blue)\n    }\n\n    /**\n     * 解决InputMethodManager引起的内存泄漏\n     * 在Activity的onDestroy方法里调用\n     */\n    fun fixInputMethodManagerLeak(context: Context) {\n\n        val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager\n        val arr = arrayOf(\"mCurRootView\", \"mServedView\", \"mNextServedView\")\n        var field: Field? = null\n        var objGet: Any? = null\n        for (i in arr.indices) {\n            val param = arr[i]\n            try {\n                field = imm.javaClass.getDeclaredField(param)\n                if (field.isAccessible === false) {\n                    field.isAccessible = true\n                }\n                objGet = field.get(imm)\n                if (objGet != null && objGet is View) {\n                    val view = objGet\n                    if (view.context === context) {\n                        // 被InputMethodManager持有引用的context是想要目标销毁的\n                        field.set(imm, null) // 置空，破坏掉path to gc节点\n                    } else {\n                        // 不是想要目标销毁的，即为又进了另一层界面了，不要处理，避免影响原逻辑,也就不用继续for循环了\n                        break\n                    }\n                }\n            } catch (t: Throwable) {\n                t.printStackTrace()\n            }\n\n        }\n\n    }\n\n    /**\n     * 获取当前进程名\n     */\n    fun getProcessName(pid: Int): String {\n        var reader: BufferedReader? = null\n        try {\n            reader = BufferedReader(FileReader(\"/proc/$pid/cmdline\"))\n            var processName = reader!!.readLine()\n            if (!TextUtils.isEmpty(processName)) {\n                processName = processName.trim({ it <= ' ' })\n            }\n            return processName\n        } catch (throwable: Throwable) {\n            throwable.printStackTrace()\n        } finally {\n            try {\n                if (reader != null) {\n                    reader!!.close()\n                }\n            } catch (exception: IOException) {\n                exception.printStackTrace()\n            }\n        }\n        return \"\"\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/FileProvider7.kt",
    "content": "package com.cxz.kotlin.baselibs.utils\n\nimport android.content.Context\nimport android.content.Intent\nimport android.content.pm.PackageManager\nimport android.net.Uri\nimport android.os.Build\nimport androidx.core.content.FileProvider\nimport java.io.File\n\n/**\n * Created by chenxz on 2018/7/16.\n */\nobject FileProvider7 {\n\n    fun getUriForFile(context: Context, file: File): Uri? {\n        var fileUri: Uri? = null\n        if (Build.VERSION.SDK_INT >= 24) {\n            fileUri = getUriForFile24(context, file)\n        } else {\n            fileUri = Uri.fromFile(file)\n        }\n        return fileUri\n    }\n\n\n    fun getUriForFile24(context: Context, file: File): Uri {\n        val fileUri = FileProvider.getUriForFile(context, context.packageName + \".fileprovider\", file)\n        return fileUri\n    }\n\n\n    fun setIntentDataAndType(\n        context: Context,\n        intent: Intent,\n        type: String,\n        file: File,\n        writeAble: Boolean\n    ) {\n        if (Build.VERSION.SDK_INT >= 24) {\n            intent.setDataAndType(getUriForFile(context, file), type)\n            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)\n            if (writeAble) {\n                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)\n            }\n        } else {\n            intent.setDataAndType(Uri.fromFile(file), type)\n        }\n    }\n\n    fun setIntentData(\n        context: Context,\n        intent: Intent,\n        file: File,\n        writeAble: Boolean\n    ) {\n        if (Build.VERSION.SDK_INT >= 24) {\n            intent.data = getUriForFile(context, file)\n            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)\n            if (writeAble) {\n                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)\n            }\n        } else {\n            intent.data = Uri.fromFile(file)\n        }\n    }\n\n    fun grantPermissions(context: Context, intent: Intent, uri: Uri, writeAble: Boolean) {\n        var flag = Intent.FLAG_GRANT_READ_URI_PERMISSION\n        if (writeAble) {\n            flag = flag or Intent.FLAG_GRANT_WRITE_URI_PERMISSION\n        }\n        intent.addFlags(flag)\n        val resInfoList = context.packageManager\n            .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)\n        for (resolveInfo in resInfoList) {\n            val packageName = resolveInfo.activityInfo.packageName\n            context.grantUriPermission(packageName, uri, flag)\n        }\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/KeyBoardUtil.kt",
    "content": "package com.cxz.kotlin.baselibs.utils\n\nimport android.content.Context\nimport android.view.MotionEvent\nimport android.view.View\nimport android.view.inputmethod.InputMethodManager\nimport android.widget.EditText\n\n/**\n * @author chenxz\n * @date 2018/11/19\n * @desc KeyBoardUtil\n */\nobject KeyBoardUtil {\n\n    /**\n     * 是否落在 EditText 区域\n     */\n    fun isHideKeyboard(view: View?, event: MotionEvent): Boolean {\n        if (view != null && view is EditText) {\n            val location = intArrayOf(0, 0)\n            view.getLocationInWindow(location)\n            //获取现在拥有焦点的控件view的位置，即EditText\n            val left = location[0]\n            val top = location[1]\n            val bottom = top + view.height\n            val right = left + view.width\n            //判断我们手指点击的区域是否落在EditText上面，如果不是，则返回true，否则返回false\n            val isInEt = (event.x > left && event.x < right && event.y > top && event.y < bottom)\n            return !isInEt\n        }\n        return false\n    }\n\n    /**\n     * 关闭软键盘\n     */\n    fun hideKeyBoard(context: Context, view: View?) {\n        val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager\n        imm.hideSoftInputFromWindow(view!!.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)\n    }\n\n    /**\n     * 打卡软键盘\n     */\n    fun openKeyBord(mEditText: EditText, mContext: Context) {\n        val imm = mContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager\n        imm.showSoftInput(mEditText, InputMethodManager.RESULT_SHOWN)\n        imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY)\n    }\n\n    /**\n     * 关闭软键盘\n     */\n    fun closeKeyBord(mEditText: EditText, mContext: Context) {\n        val imm = mContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager\n        imm.hideSoftInputFromWindow(mEditText.windowToken, 0)\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/NLog.kt",
    "content": "package com.cxz.kotlin.baselibs.utils\n\nimport android.util.Log\n\n/**\n * @author chenxz\n * @date 2019/11/9\n * @desc 日志打印类\n */\nobject NLog {\n\n    private const val TAG = \"KotlinMVP\"\n\n    private var debug: Boolean = false\n\n    fun openLog() {\n        debug = true\n    }\n\n    fun i(tag: String, content: String) {\n        if (debug) {\n            Log.i(tag, content)\n        }\n    }\n\n    fun i(content: String) {\n        i(TAG, content)\n    }\n\n    fun v(tag: String, content: String) {\n        if (debug) {\n            Log.v(tag, content)\n        }\n    }\n\n    fun v(content: String) {\n        v(TAG, content)\n    }\n\n    fun d(tag: String, content: String) {\n        if (debug) {\n            Log.d(tag, content)\n        }\n    }\n\n    fun d(content: String) {\n        d(TAG, content)\n    }\n\n    fun w(tag: String, content: String) {\n        if (debug) {\n            Log.w(tag, content)\n        }\n    }\n\n    fun w(content: String) {\n        w(TAG, content)\n    }\n\n    fun e(tag: String, content: String) {\n        if (debug) {\n            Log.e(tag, content)\n        }\n    }\n\n    fun e(content: String) {\n        e(TAG, content)\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/NetWorkUtil.kt",
    "content": "package com.cxz.kotlin.baselibs.utils\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.content.Intent\nimport android.net.ConnectivityManager\nimport android.net.NetworkInfo\nimport android.net.wifi.WifiManager\nimport android.provider.Settings\nimport android.telephony.TelephonyManager\nimport com.cxz.kotlin.baselibs.config.AppConfig\nimport java.net.NetworkInterface\nimport java.net.SocketException\n\n/**\n * @author admin\n * @date 2018/11/21\n * @desc\n */\nobject NetWorkUtil {\n\n    val NETWORK_WIFI = 1 // wifi network\n    val NETWORK_4G = 4 // \"4G\" networks\n    val NETWORK_3G = 3 // \"3G\" networks\n    val NETWORK_2G = 2 // \"2G\" networks\n    val NETWORK_UNKNOWN = 5 // unknown network\n    val NETWORK_NO = -1 // no network\n\n    private val NETWORK_TYPE_GSM = 16\n    private val NETWORK_TYPE_TD_SCDMA = 17\n    private val NETWORK_TYPE_IWLAN = 18\n\n    /**\n     * 打开网络设置界面\n     *\n     * 3.0以下打开设置界面\n     *\n     * @param context 上下文\n     */\n    fun openWirelessSettings(context: Context) {\n        if (android.os.Build.VERSION.SDK_INT > 10) {\n            context.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))\n        } else {\n            context.startActivity(Intent(Settings.ACTION_SETTINGS))\n        }\n    }\n\n    /**\n     * 获取活动网络信息\n     *\n     * 需添加权限\n     * `<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>`\n     *\n     * @param context 上下文\n     *\n     * @return NetworkInfo\n     */\n    @SuppressLint(\"MissingPermission\")\n    private fun getActiveNetworkInfo(context: Context): NetworkInfo? {\n        val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager\n        return cm.activeNetworkInfo\n    }\n\n    /**\n     * 判断网络是否连接\n     *\n     * 需添加权限\n     * `<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>`\n     *\n     * @return `true`: 是 `false`: 否\n     */\n    fun isConnected(): Boolean {\n        return isConnected(AppConfig.getApplication())\n    }\n\n    /**\n     * 判断网络是否连接\n     *\n     * 需添加权限\n     * `<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>`\n     *\n     * @return `true`: 是 `false`: 否\n     */\n    fun isConnected(context: Context): Boolean {\n        val info = getActiveNetworkInfo(context)\n        return info != null && info.isConnected\n    }\n\n    /**\n     * 打开或关闭移动数据\n     *\n     * 需系统应用 需添加权限\n     * `<uses-permission android:name=\"android.permission.MODIFY_PHONE_STATE\"/>`\n     *\n     * @param context 上下文\n     * @param enabled `true`: 打开 `false`: 关闭\n     */\n    fun setDataEnabled(context: Context, enabled: Boolean) {\n        try {\n            val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager\n            val setMobileDataEnabledMethod =\n                tm.javaClass.getDeclaredMethod(\"setDataEnabled\", Boolean::class.javaPrimitiveType)\n            setMobileDataEnabledMethod?.invoke(tm, enabled)\n        } catch (e: Exception) {\n            e.printStackTrace()\n        }\n\n    }\n\n    /**\n     * 判断网络是否是4G\n     *\n     * 需添加权限\n     * `<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>`\n     *\n     * @param context 上下文\n     *\n     * @return `true`: 是 `false`: 否\n     */\n    fun is4G(context: Context): Boolean {\n        val info = getActiveNetworkInfo(context)\n        return info != null && info.isAvailable && info.subtype == TelephonyManager.NETWORK_TYPE_LTE\n    }\n\n    /**\n     * 判断wifi是否打开\n     *\n     * 需添加权限\n     * `<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>`\n     *\n     * @param context 上下文\n     *\n     * @return `true`: 是 `false`: 否\n     */\n    fun getWifiEnabled(context: Context): Boolean {\n        val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager\n        return wifiManager.isWifiEnabled\n    }\n\n    /**\n     * 打开或关闭wifi\n     *\n     * 需添加权限\n     * `<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>`\n     *\n     * @param context 上下文\n     *\n     * @param enabled true`: 打开 `false`: 关闭\n     */\n    fun setWifiEnabled(context: Context, enabled: Boolean) {\n        val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager\n        if (enabled) {\n            if (!wifiManager.isWifiEnabled) {\n                //\t\t\t\twifiManager.setWifiEnabled(true);\n            }\n        } else {\n            if (wifiManager.isWifiEnabled) {\n                //\t\t\t\twifiManager.setWifiEnabled(false);\n            }\n        }\n    }\n\n    /**\n     * 判断wifi是否连接状态\n     *\n     * 需添加权限\n     * `<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>`\n     *\n     * @param context 上下文\n     *\n     * @return `true`: 连接 `false`: 未连接\n     */\n    fun isWifiConnected(context: Context): Boolean {\n        val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager\n        return cm.activeNetworkInfo?.type == ConnectivityManager.TYPE_WIFI\n    }\n\n    /**\n     * 获取网络运营商名称\n     *\n     * 中国移动、如中国联通、中国电信\n     *\n     * @param context 上下文\n     *\n     * @return 运营商名称\n     */\n    fun getNetworkOperatorName(context: Context): String? {\n        val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager\n        return tm?.networkOperatorName\n    }\n\n    /**\n     * 获取当前的网络类型(WIFI,2G,3G,4G)\n     *\n     * 需添加权限\n     * `<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>`\n     *\n     * @param context 上下文\n     *\n     * @return 网络类型\n     *\n     *  * [.NETWORK_WIFI] = 1;\n     *  * [.NETWORK_4G] = 4;\n     *  * [.NETWORK_3G] = 3;\n     *  * [.NETWORK_2G] = 2;\n     *  * [.NETWORK_UNKNOWN] = 5;\n     *  * [.NETWORK_NO] = -1;\n     *\n     */\n    fun getNetworkType(context: Context): Int {\n        var netType = NETWORK_NO\n        val info = getActiveNetworkInfo(context)\n        if (info != null && info.isAvailable) {\n\n            if (info.type == ConnectivityManager.TYPE_WIFI) {\n                netType = NETWORK_WIFI\n            } else if (info.type == ConnectivityManager.TYPE_MOBILE) {\n                when (info.subtype) {\n\n                    NETWORK_TYPE_GSM, TelephonyManager.NETWORK_TYPE_GPRS, TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_EDGE, TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyManager.NETWORK_TYPE_IDEN -> netType =\n                        NETWORK_2G\n\n                    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 =\n                        NETWORK_3G\n\n                    NETWORK_TYPE_IWLAN, TelephonyManager.NETWORK_TYPE_LTE -> netType = NETWORK_4G\n                    else -> {\n\n                        val subtypeName = info.subtypeName\n                        if (\"TD-SCDMA\".equals(subtypeName, ignoreCase = true) || \"WCDMA\".equals(\n                                subtypeName,\n                                ignoreCase = true\n                            )\n                            || \"CDMA2000\".equals(subtypeName, ignoreCase = true)\n                        ) {\n                            netType = NETWORK_3G\n                        } else {\n                            netType = NETWORK_UNKNOWN\n                        }\n                    }\n                }\n            } else {\n                netType = NETWORK_UNKNOWN\n            }\n        }\n        return netType\n    }\n\n    /**\n     * 获取当前的网络类型(WIFI,2G,3G,4G)\n     *\n     * 依赖上面的方法\n     *\n     * @param context 上下文\n     *\n     * @return 网络类型名称\n     *\n     *  * NETWORK_WIFI\n     *  * NETWORK_4G\n     *  * NETWORK_3G\n     *  * NETWORK_2G\n     *  * NETWORK_UNKNOWN\n     *  * NETWORK_NO\n     *\n     */\n    fun getNetworkTypeName(context: Context): String {\n        return when (getNetworkType(context)) {\n            NETWORK_WIFI -> \"NETWORK_WIFI\"\n            NETWORK_4G -> \"NETWORK_4G\"\n            NETWORK_3G -> \"NETWORK_3G\"\n            NETWORK_2G -> \"NETWORK_2G\"\n            NETWORK_NO -> \"NETWORK_NO\"\n            else -> \"NETWORK_UNKNOWN\"\n        }\n    }\n\n    /**\n     * 获取IP地址\n     *\n     * 需添加权限\n     * `<uses-permission android:name=\"android.permission.INTERNET\"/>`\n     *\n     * @param useIPv4 是否用IPv4\n     *\n     * @return IP地址\n     */\n    fun getIPAddress(useIPv4: Boolean): String? {\n        try {\n            val nis = NetworkInterface.getNetworkInterfaces()\n            while (nis.hasMoreElements()) {\n                val ni = nis.nextElement()\n                // 防止小米手机返回10.0.2.15\n                if (!ni.isUp) {\n                    continue\n                }\n\n                val addresses = ni.inetAddresses\n                while (addresses.hasMoreElements()) {\n                    val inetAddress = addresses.nextElement()\n                    if (!inetAddress.isLoopbackAddress) {\n                        val hostAddress = inetAddress.hostAddress\n                        val isIPv4 = hostAddress.indexOf(':') < 0\n                        if (useIPv4) {\n                            if (isIPv4) {\n                                return hostAddress\n                            }\n\n                        } else {\n                            if (!isIPv4) {\n                                val index = hostAddress.indexOf('%')\n                                return if (index < 0)\n                                    hostAddress.toUpperCase()\n                                else\n                                    hostAddress.substring(0, index).toUpperCase()\n                            }\n                        }\n                    }\n                }\n            }\n        } catch (e: SocketException) {\n            e.printStackTrace()\n        }\n\n        return null\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/Preference.kt",
    "content": "package com.cxz.kotlin.baselibs.utils\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.content.SharedPreferences\nimport com.cxz.kotlin.baselibs.config.AppConfig\nimport java.io.*\nimport kotlin.reflect.KProperty\n\n/**\n * Created by chenxz on 2018/4/21.\n * kotlin委托属性+SharedPreference实例\n */\nclass Preference<T>(val name: String, private val default: T) {\n\n    companion object {\n        private val file_name = \"wan_android_file\"\n\n        private val prefs: SharedPreferences by lazy {\n            AppConfig.getApplication().getSharedPreferences(file_name, Context.MODE_PRIVATE)\n        }\n\n        /**\n         * 删除全部数据\n         */\n        fun clearPreference() {\n            prefs.edit().clear().apply()\n        }\n\n        /**\n         * 根据key删除存储数据\n         */\n        fun clearPreference(key: String) {\n            prefs.edit().remove(key).apply()\n        }\n\n        /**\n         * 查询某个key是否已经存在\n         *\n         * @param key\n         * @return\n         */\n        fun contains(key: String): Boolean {\n            return prefs.contains(key)\n        }\n\n        /**\n         * 返回所有的键值对\n         *\n         * @param context\n         * @return\n         */\n        fun getAll(): Map<String, *> {\n            return prefs.all\n        }\n    }\n\n    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {\n        return getSharedPreferences(name, default)\n    }\n\n    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {\n        putSharedPreferences(name, value)\n    }\n\n    @SuppressLint(\"CommitPrefEdits\")\n    private fun putSharedPreferences(name: String, value: T) = with(prefs.edit()) {\n        when (value) {\n            is Long -> putLong(name, value)\n            is String -> putString(name, value)\n            is Int -> putInt(name, value)\n            is Boolean -> putBoolean(name, value)\n            is Float -> putFloat(name, value)\n            else -> putString(name, serialize(value))\n        }.apply()\n    }\n\n    @Suppress(\"UNCHECKED_CAST\")\n    private fun getSharedPreferences(name: String, default: T): T = with(prefs) {\n        val res: Any = when (default) {\n            is Long -> getLong(name, default)\n            is String -> getString(name, default) ?: \"\"\n            is Int -> getInt(name, default)\n            is Boolean -> getBoolean(name, default)\n            is Float -> getFloat(name, default)\n            else -> deSerialization(getString(name, serialize(default)) ?: \"\")\n        }\n        return res as T\n    }\n\n    /**\n     * 序列化对象\n\n     * @param person\n     * *\n     * @return\n     * *\n     * @throws IOException\n     */\n    @Throws(IOException::class)\n    private fun <A> serialize(obj: A): String {\n        val byteArrayOutputStream = ByteArrayOutputStream()\n        val objectOutputStream = ObjectOutputStream(\n            byteArrayOutputStream\n        )\n        objectOutputStream.writeObject(obj)\n        var serStr = byteArrayOutputStream.toString(\"ISO-8859-1\")\n        serStr = java.net.URLEncoder.encode(serStr, \"UTF-8\")\n        objectOutputStream.close()\n        byteArrayOutputStream.close()\n        return serStr\n    }\n\n    /**\n     * 反序列化对象\n\n     * @param str\n     * *\n     * @return\n     * *\n     * @throws IOException\n     * *\n     * @throws ClassNotFoundException\n     */\n    @Suppress(\"UNCHECKED_CAST\")\n    @Throws(IOException::class, ClassNotFoundException::class)\n    private fun <A> deSerialization(str: String): A {\n        val redStr = java.net.URLDecoder.decode(str, \"UTF-8\")\n        val byteArrayInputStream = ByteArrayInputStream(\n            redStr.toByteArray(charset(\"ISO-8859-1\"))\n        )\n        val objectInputStream = ObjectInputStream(\n            byteArrayInputStream\n        )\n        val obj = objectInputStream.readObject() as A\n        objectInputStream.close()\n        byteArrayInputStream.close()\n        return obj\n    }\n\n}\n"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/RomUtil.kt",
    "content": "package com.cxz.kotlin.baselibs.utils\n\nimport android.os.Build\nimport android.text.TextUtils\nimport java.io.BufferedReader\nimport java.io.IOException\nimport java.io.InputStreamReader\n\n/**\n * Created by chenxz on 2018/4/21.\n */\nobject RomUtil {\n\n    internal object AvailableRomType {\n        val MIUI = 1\n        val FLYME = 2\n        val ANDROID_NATIVE = 3\n        val NA = 4\n    }\n\n    fun isLightStatusBarAvailable(): Boolean {\n        return isMIUIV6OrAbove() || isFlymeV4OrAbove() || isAndroidMOrAbove()\n    }\n\n    fun getLightStatausBarAvailableRomType(): Int {\n        if (isMIUIV6OrAbove()) {\n            return AvailableRomType.MIUI\n        }\n\n        if (isFlymeV4OrAbove()) {\n            return AvailableRomType.FLYME\n        }\n\n        return if (isAndroidMOrAbove()) {\n            AvailableRomType.ANDROID_NATIVE\n        } else AvailableRomType.NA\n\n    }\n\n    //Flyme V4的displayId格式为 [Flyme OS 4.x.x.xA]\n    //Flyme V5的displayId格式为 [Flyme 5.x.x.x beta]\n    private fun isFlymeV4OrAbove(): Boolean {\n        val displayId = Build.DISPLAY\n        if (!TextUtils.isEmpty(displayId) && displayId.contains(\"Flyme\")) {\n            val displayIdArray = displayId.split(\" \".toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()\n            for (temp in displayIdArray) {\n                //版本号4以上，形如4.x.\n                if (temp.matches(\"^[4-9]\\\\.(\\\\d+\\\\.)+\\\\S*\".toRegex())) {\n                    return true\n                }\n            }\n        }\n        return false\n    }\n\n    //MIUI V6对应的versionCode是4\n    //MIUI V7对应的versionCode是5\n    private fun isMIUIV6OrAbove(): Boolean {\n        val miuiVersionCodeStr = getSystemProperty(\"ro.miui.ui.version.code\")\n        if (!TextUtils.isEmpty(miuiVersionCodeStr)) {\n            try {\n                val miuiVersionCode = Integer.parseInt(miuiVersionCodeStr)\n                if (miuiVersionCode >= 4) {\n                    return true\n                }\n            } catch (e: Exception) {\n            }\n\n        }\n        return false\n    }\n\n    //Android Api 23以上\n    private fun isAndroidMOrAbove(): Boolean {\n        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M\n    }\n\n    private fun getSystemProperty(propName: String): String? {\n        val line: String\n        var input: BufferedReader? = null\n        try {\n            val p = Runtime.getRuntime().exec(\"getprop \" + propName)\n            input = BufferedReader(InputStreamReader(p.inputStream), 1024)\n            line = input!!.readLine()\n            input!!.close()\n        } catch (ex: IOException) {\n            return null\n        } finally {\n            if (input != null) {\n                try {\n                    input!!.close()\n                } catch (e: IOException) {\n                }\n\n            }\n        }\n        return line\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/RxTimerUtil.kt",
    "content": "package com.cxz.kotlin.baselibs.utils\n\nimport io.reactivex.Observable\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactivex.disposables.Disposable\nimport java.util.concurrent.TimeUnit\n\n/**\n * @author chenxz\n * @date 2019/1/10\n * @desc\n */\nobject RxTimerUtil {\n\n    private var mDisposable: Disposable? = null\n\n    /**\n     * 延迟 milliseconds 毫秒后执行 task\n     */\n    fun postDelay(milliseconds: Long, task: ((Long) -> Unit)? = null) {\n        mDisposable = Observable.timer(milliseconds, TimeUnit.MILLISECONDS)\n            .observeOn(AndroidSchedulers.mainThread())\n            .subscribe { number ->\n                task?.invoke(number)\n                cancel()\n            }\n    }\n\n    /**\n     * 每隔 milliseconds 毫秒循环执行 task\n     */\n    fun interval(milliseconds: Long, task: ((Long) -> Unit)? = null) {\n        mDisposable = Observable.interval(milliseconds, TimeUnit.MILLISECONDS)\n            .observeOn(AndroidSchedulers.mainThread())\n            .subscribe { number ->\n                task?.invoke(number)\n            }\n    }\n\n    /**\n     * 取消任务\n     */\n    fun cancel() {\n        mDisposable?.let {\n            if (!it.isDisposed) {\n                it.dispose()\n            }\n        }\n    }\n\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/utils/StatusBarUtil.kt",
    "content": "package com.cxz.kotlin.baselibs.utils\n\nimport android.annotation.TargetApi\nimport android.app.Activity\nimport android.content.Context\nimport android.graphics.Color\nimport android.os.Build\nimport androidx.annotation.ColorInt\nimport androidx.annotation.IntRange\nimport androidx.coordinatorlayout.widget.CoordinatorLayout\nimport androidx.drawerlayout.widget.DrawerLayout\nimport android.view.View\nimport android.view.ViewGroup\nimport android.view.WindowManager\nimport android.widget.LinearLayout\nimport com.cxz.kotlin.baselibs.R\n\n/**\n * Created by chenxz on 2018/4/21.\n * 沉浸式状态栏工具类\n */\nobject StatusBarUtil {\n\n    val DEFAULT_STATUS_BAR_ALPHA = 112\n    private val FAKE_STATUS_BAR_VIEW_ID = R.id.statusbarutil_fake_status_bar_view\n    private val FAKE_TRANSLUCENT_VIEW_ID = R.id.statusbarutil_translucent_view\n    private val TAG_KEY_HAVE_SET_OFFSET = -123\n\n    /**\n     * 设置状态栏颜色\n     *\n     * @param activity 需要设置的 activity\n     * @param color    状态栏颜色值\n     */\n    fun setColor(activity: Activity, @ColorInt color: Int) {\n        setColor(activity, color, DEFAULT_STATUS_BAR_ALPHA)\n    }\n\n    /**\n     * 设置状态栏颜色\n     *\n     * @param activity       需要设置的activity\n     * @param color          状态栏颜色值\n     * @param statusBarAlpha 状态栏透明度\n     */\n\n    fun setColor(activity: Activity, @ColorInt color: Int, @IntRange(from = 0, to = 255) statusBarAlpha: Int) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)\n            activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)\n            activity.window.statusBarColor = calculateStatusColor(color, statusBarAlpha)\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)\n            val decorView = activity.window.decorView as ViewGroup\n            val fakeStatusBarView = decorView.findViewById<View>(FAKE_STATUS_BAR_VIEW_ID)\n            if (fakeStatusBarView != null) {\n                if (fakeStatusBarView!!.visibility === View.GONE) {\n                    fakeStatusBarView!!.visibility = View.VISIBLE\n                }\n                fakeStatusBarView!!.setBackgroundColor(calculateStatusColor(color, statusBarAlpha))\n            } else {\n                decorView.addView(createStatusBarView(activity, color, statusBarAlpha))\n            }\n            setRootView(activity)\n        }\n    }\n\n    /**\n     * 为滑动返回界面设置状态栏颜色\n     *\n     * @param activity 需要设置的activity\n     * @param color    状态栏颜色值\n     */\n    fun setColorForSwipeBack(activity: Activity, color: Int) {\n        setColorForSwipeBack(activity, color, DEFAULT_STATUS_BAR_ALPHA)\n    }\n\n    /**\n     * 为滑动返回界面设置状态栏颜色\n     *\n     * @param activity       需要设置的activity\n     * @param color          状态栏颜色值\n     * @param statusBarAlpha 状态栏透明度 @IntRange(from = 0, to = 255)\n     */\n    fun setColorForSwipeBack(activity: Activity, @ColorInt color: Int, statusBarAlpha: Int) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n\n            val contentView = activity.findViewById<View>(android.R.id.content) as ViewGroup\n            val rootView = contentView.getChildAt(0)\n            val statusBarHeight = getStatusBarHeight(activity)\n            if (rootView != null && rootView is androidx.coordinatorlayout.widget.CoordinatorLayout) {\n                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n                    rootView.fitsSystemWindows = false\n                    contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha))\n                    val isNeedRequestLayout = contentView.paddingTop < statusBarHeight\n                    if (isNeedRequestLayout) {\n                        contentView.setPadding(0, statusBarHeight, 0, 0)\n                        rootView.post { rootView.requestLayout() }\n                    }\n                } else {\n                    rootView.setStatusBarBackgroundColor(calculateStatusColor(color, statusBarAlpha))\n                }\n            } else {\n                contentView.setPadding(0, statusBarHeight, 0, 0)\n                contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha))\n            }\n            setTransparentForWindow(activity)\n        }\n    }\n\n    /**\n     * 设置状态栏纯色 不加半透明效果\n     *\n     * @param activity 需要设置的 activity\n     * @param color    状态栏颜色值\n     */\n    fun setColorNoTranslucent(activity: Activity, @ColorInt color: Int) {\n        setColor(activity, color, 0)\n    }\n\n    /**\n     * 设置状态栏颜色(5.0以下无半透明效果,不建议使用)\n     *\n     * @param activity 需要设置的 activity\n     * @param color    状态栏颜色值\n     */\n    @Deprecated(\"\")\n    fun setColorDiff(activity: Activity, @ColorInt color: Int) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {\n            return\n        }\n        transparentStatusBar(activity)\n        val contentView = activity.findViewById<View>(android.R.id.content) as ViewGroup\n        // 移除半透明矩形,以免叠加\n        val fakeStatusBarView = contentView.findViewById<View>(FAKE_STATUS_BAR_VIEW_ID)\n        if (fakeStatusBarView != null) {\n            if (fakeStatusBarView!!.visibility === View.GONE) {\n                fakeStatusBarView!!.visibility = View.VISIBLE\n            }\n            fakeStatusBarView!!.setBackgroundColor(color)\n        } else {\n            contentView.addView(createStatusBarView(activity, color))\n        }\n        setRootView(activity)\n    }\n\n    /**\n     * 使状态栏半透明\n     *\n     *\n     * 适用于图片作为背景的界面,此时需要图片填充到状态栏\n     *\n     * @param activity 需要设置的activity\n     */\n    fun setTranslucent(activity: Activity) {\n        setTranslucent(activity, DEFAULT_STATUS_BAR_ALPHA)\n    }\n\n    /**\n     * 使状态栏半透明\n     *\n     *\n     * 适用于图片作为背景的界面,此时需要图片填充到状态栏\n     *\n     * @param activity       需要设置的activity\n     * @param statusBarAlpha 状态栏透明度 @IntRange(from = 0, to = 255)\n     */\n    fun setTranslucent(activity: Activity, statusBarAlpha: Int) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {\n            return\n        }\n        setTransparent(activity)\n        addTranslucentView(activity, statusBarAlpha)\n    }\n\n    /**\n     * 针对根布局是 CoordinatorLayout, 使状态栏半透明\n     *\n     *\n     * 适用于图片作为背景的界面,此时需要图片填充到状态栏\n     *\n     * @param activity       需要设置的activity\n     * @param statusBarAlpha 状态栏透明度 @IntRange(from = 0, to = 255)\n     */\n    fun setTranslucentForCoordinatorLayout(activity: Activity, statusBarAlpha: Int) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {\n            return\n        }\n        transparentStatusBar(activity)\n        addTranslucentView(activity, statusBarAlpha)\n    }\n\n    /**\n     * 设置状态栏全透明\n     *\n     * @param activity 需要设置的activity\n     */\n    fun setTransparent(activity: Activity) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {\n            return\n        }\n        transparentStatusBar(activity)\n        setRootView(activity)\n    }\n\n    /**\n     * 使状态栏透明(5.0以上半透明效果,不建议使用)\n     *\n     *\n     * 适用于图片作为背景的界面,此时需要图片填充到状态栏\n     *\n     * @param activity 需要设置的activity\n     */\n    @Deprecated(\"\")\n    fun setTranslucentDiff(activity: Activity) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            // 设置状态栏透明\n            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)\n            setRootView(activity)\n        }\n    }\n\n    /**\n     * 为DrawerLayout 布局设置状态栏变色\n     *\n     * @param activity     需要设置的activity\n     * @param drawerLayout DrawerLayout\n     * @param color        状态栏颜色值\n     */\n    fun setColorForDrawerLayout(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout, @ColorInt color: Int) {\n        setColorForDrawerLayout(activity, drawerLayout, color, DEFAULT_STATUS_BAR_ALPHA)\n    }\n\n    /**\n     * 为DrawerLayout 布局设置状态栏颜色,纯色\n     *\n     * @param activity     需要设置的activity\n     * @param drawerLayout DrawerLayout\n     * @param color        状态栏颜色值\n     */\n    fun setColorNoTranslucentForDrawerLayout(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout, @ColorInt color: Int) {\n        setColorForDrawerLayout(activity, drawerLayout, color, 0)\n    }\n\n    /**\n     * 为DrawerLayout 布局设置状态栏变色\n     *\n     * @param activity       需要设置的activity\n     * @param drawerLayout   DrawerLayout\n     * @param color          状态栏颜色值\n     * @param statusBarAlpha 状态栏透明度 @IntRange(from = 0, to = 255)\n     */\n    fun setColorForDrawerLayout(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout, @ColorInt color: Int, statusBarAlpha: Int) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {\n            return\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)\n            activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)\n            activity.window.statusBarColor = Color.TRANSPARENT\n        } else {\n            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)\n        }\n        // 生成一个状态栏大小的矩形\n        // 添加 statusBarView 到布局中\n        val contentLayout = drawerLayout.getChildAt(0) as ViewGroup\n        val fakeStatusBarView = contentLayout.findViewById<View>(FAKE_STATUS_BAR_VIEW_ID)\n        if (fakeStatusBarView != null) {\n            if (fakeStatusBarView!!.visibility === View.GONE) {\n                fakeStatusBarView!!.visibility = View.VISIBLE\n            }\n            fakeStatusBarView!!.setBackgroundColor(color)\n        } else {\n            contentLayout.addView(createStatusBarView(activity, color), 0)\n        }\n        // 内容布局不是 LinearLayout 时,设置padding top\n        if (contentLayout !is LinearLayout && contentLayout.getChildAt(1) != null) {\n            contentLayout.getChildAt(1)\n                .setPadding(contentLayout.paddingLeft, getStatusBarHeight(activity) + contentLayout.paddingTop,\n                    contentLayout.paddingRight, contentLayout.paddingBottom)\n        }\n        // 设置属性\n        setDrawerLayoutProperty(drawerLayout, contentLayout)\n        addTranslucentView(activity, statusBarAlpha)\n    }\n\n    /**\n     * 设置 DrawerLayout 属性\n     *\n     * @param drawerLayout              DrawerLayout\n     * @param drawerLayoutContentLayout DrawerLayout 的内容布局\n     */\n    private fun setDrawerLayoutProperty(drawerLayout: androidx.drawerlayout.widget.DrawerLayout, drawerLayoutContentLayout: ViewGroup) {\n        val drawer = drawerLayout.getChildAt(1) as ViewGroup\n        drawerLayout.fitsSystemWindows = false\n        drawerLayoutContentLayout.fitsSystemWindows = false\n        drawerLayoutContentLayout.clipToPadding = true\n        drawer.fitsSystemWindows = false\n    }\n\n    /**\n     * 为DrawerLayout 布局设置状态栏变色(5.0以下无半透明效果,不建议使用)\n     *\n     * @param activity     需要设置的activity\n     * @param drawerLayout DrawerLayout\n     * @param color        状态栏颜色值\n     */\n    @Deprecated(\"\")\n    fun setColorForDrawerLayoutDiff(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout, @ColorInt color: Int) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)\n            // 生成一个状态栏大小的矩形\n            val contentLayout = drawerLayout.getChildAt(0) as ViewGroup\n            val fakeStatusBarView = contentLayout.findViewById<View>(FAKE_STATUS_BAR_VIEW_ID)\n            if (fakeStatusBarView != null) {\n                if (fakeStatusBarView.visibility === View.GONE) {\n                    fakeStatusBarView.visibility = View.VISIBLE\n                }\n                fakeStatusBarView!!.setBackgroundColor(calculateStatusColor(color, DEFAULT_STATUS_BAR_ALPHA))\n            } else {\n                // 添加 statusBarView 到布局中\n                contentLayout.addView(createStatusBarView(activity, color), 0)\n            }\n            // 内容布局不是 LinearLayout 时,设置padding top\n            if (contentLayout !is LinearLayout && contentLayout.getChildAt(1) != null) {\n                contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0)\n            }\n            // 设置属性\n            setDrawerLayoutProperty(drawerLayout, contentLayout)\n        }\n    }\n\n    /**\n     * 为 DrawerLayout 布局设置状态栏透明\n     *\n     * @param activity     需要设置的activity\n     * @param drawerLayout DrawerLayout\n     */\n    fun setTranslucentForDrawerLayout(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout) {\n        setTranslucentForDrawerLayout(activity, drawerLayout, DEFAULT_STATUS_BAR_ALPHA)\n    }\n\n    /**\n     * 为 DrawerLayout 布局设置状态栏透明\n     *\n     * @param activity     需要设置的activity\n     * @param drawerLayout DrawerLayout\n     */\n    fun setTranslucentForDrawerLayout(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout, statusBarAlpha: Int) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {\n            return\n        }\n        setTransparentForDrawerLayout(activity, drawerLayout)\n        addTranslucentView(activity, statusBarAlpha)\n    }\n\n    /**\n     * 为 DrawerLayout 布局设置状态栏透明\n     *\n     * @param activity     需要设置的activity\n     * @param drawerLayout DrawerLayout\n     */\n    fun setTransparentForDrawerLayout(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {\n            return\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)\n            activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)\n            activity.window.statusBarColor = Color.TRANSPARENT\n        } else {\n            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)\n        }\n\n        val contentLayout = drawerLayout.getChildAt(0) as ViewGroup\n        // 内容布局不是 LinearLayout 时,设置padding top\n        if (contentLayout !is LinearLayout && contentLayout.getChildAt(1) != null) {\n            contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0)\n        }\n\n        // 设置属性\n        setDrawerLayoutProperty(drawerLayout, contentLayout)\n    }\n\n    /**\n     * 为 DrawerLayout 布局设置状态栏透明(5.0以上半透明效果,不建议使用)\n     *\n     * @param activity     需要设置的activity\n     * @param drawerLayout DrawerLayout\n     */\n    @Deprecated(\"\")\n    fun setTranslucentForDrawerLayoutDiff(activity: Activity, drawerLayout: androidx.drawerlayout.widget.DrawerLayout) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            // 设置状态栏透明\n            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)\n            // 设置内容布局属性\n            val contentLayout = drawerLayout.getChildAt(0) as ViewGroup\n            contentLayout.fitsSystemWindows = true\n            contentLayout.clipToPadding = true\n            // 设置抽屉布局属性\n            val vg = drawerLayout.getChildAt(1) as ViewGroup\n            vg.fitsSystemWindows = false\n            // 设置 DrawerLayout 属性\n            drawerLayout.fitsSystemWindows = false\n        }\n    }\n\n    /**\n     * 为头部是 ImageView 的界面设置状态栏全透明\n     *\n     * @param activity       需要设置的activity\n     * @param needOffsetView 需要向下偏移的 View\n     */\n    fun setTransparentForImageView(activity: Activity, needOffsetView: View) {\n        setTranslucentForImageView(activity, 0, needOffsetView)\n    }\n\n    /**\n     * 为头部是 ImageView 的界面设置状态栏透明(使用默认透明度)\n     *\n     * @param activity       需要设置的activity\n     * @param needOffsetView 需要向下偏移的 View\n     */\n    fun setTranslucentForImageView(activity: Activity, needOffsetView: View) {\n        setTranslucentForImageView(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView)\n    }\n\n    /**\n     * 为头部是 ImageView 的界面设置状态栏透明\n     *\n     * @param activity       需要设置的activity\n     * @param statusBarAlpha 状态栏透明度\n     * @param needOffsetView 需要向下偏移的 View\n     */\n    fun setTranslucentForImageView(\n        activity: Activity, statusBarAlpha: Int,\n        needOffsetView: View?\n    ) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {\n            return\n        }\n        setTransparentForWindow(activity)\n        addTranslucentView(activity, statusBarAlpha)\n        if (needOffsetView != null) {\n            val haveSetOffset = needOffsetView!!.getTag(TAG_KEY_HAVE_SET_OFFSET)\n            if (haveSetOffset != null && haveSetOffset as Boolean) {\n                return\n            }\n            val layoutParams = needOffsetView!!.layoutParams as ViewGroup.MarginLayoutParams\n            layoutParams.setMargins(\n                layoutParams.leftMargin, layoutParams.topMargin + getStatusBarHeight(activity),\n                layoutParams.rightMargin, layoutParams.bottomMargin\n            )\n            needOffsetView!!.setTag(TAG_KEY_HAVE_SET_OFFSET, true)\n        }\n    }\n\n    /**\n     * 为 fragment 头部是 ImageView 的设置状态栏透明\n     *\n     * @param activity       fragment 对应的 activity\n     * @param needOffsetView 需要向下偏移的 View\n     */\n    fun setTranslucentForImageViewInFragment(activity: Activity, needOffsetView: View) {\n        setTranslucentForImageViewInFragment(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView)\n    }\n\n    /**\n     * 为 fragment 头部是 ImageView 的设置状态栏透明\n     *\n     * @param activity       fragment 对应的 activity\n     * @param needOffsetView 需要向下偏移的 View\n     */\n    fun setTransparentForImageViewInFragment(activity: Activity, needOffsetView: View) {\n        setTranslucentForImageViewInFragment(activity, 0, needOffsetView)\n    }\n\n    /**\n     * 为 fragment 头部是 ImageView 的设置状态栏透明\n     *\n     * @param activity       fragment 对应的 activity\n     * @param statusBarAlpha 状态栏透明度\n     * @param needOffsetView 需要向下偏移的 View\n     */\n    fun setTranslucentForImageViewInFragment(\n        activity: Activity, statusBarAlpha: Int,\n        needOffsetView: View\n    ) {\n        setTranslucentForImageView(activity, statusBarAlpha, needOffsetView)\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n            clearPreviousSetting(activity)\n        }\n    }\n\n    /**\n     * 隐藏伪状态栏 View\n     *\n     * @param activity 调用的 Activity\n     */\n    fun hideFakeStatusBarView(activity: Activity) {\n        val decorView = activity.window.decorView as ViewGroup\n        val fakeStatusBarView = decorView.findViewById<View>(FAKE_STATUS_BAR_VIEW_ID)\n        if (fakeStatusBarView != null) {\n            fakeStatusBarView!!.visibility = View.GONE\n        }\n        val fakeTranslucentView = decorView.findViewById<View>(FAKE_TRANSLUCENT_VIEW_ID)\n        if (fakeTranslucentView != null) {\n            fakeTranslucentView!!.visibility = View.GONE\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.M)\n    fun setLightMode(activity: Activity) {\n        setMIUIStatusBarDarkIcon(activity, true)\n        setMeizuStatusBarDarkIcon(activity, true)\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            activity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR //or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.M)\n    fun setDarkMode(activity: Activity) {\n        setMIUIStatusBarDarkIcon(activity, false)\n        setMeizuStatusBarDarkIcon(activity, false)\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            //activity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN\n        }\n    }\n\n    /**\n     * 修改 MIUI V6  以上状态栏颜色\n     */\n    private fun setMIUIStatusBarDarkIcon(activity: Activity, darkIcon: Boolean) {\n        val clazz = activity.window.javaClass\n        try {\n            val layoutParams = Class.forName(\"android.view.MiuiWindowManager\\$LayoutParams\")\n            val field = layoutParams.getField(\"EXTRA_FLAG_STATUS_BAR_DARK_MODE\")\n            val darkModeFlag = field.getInt(layoutParams)\n            val extraFlagField =\n                clazz.getMethod(\"setExtraFlags\", Int::class.javaPrimitiveType, Int::class.javaPrimitiveType)\n            extraFlagField.invoke(activity.window, if (darkIcon) darkModeFlag else 0, darkModeFlag)\n        } catch (e: Exception) {\n            e.printStackTrace()\n        }\n\n    }\n\n    /**\n     * 修改魅族状态栏字体颜色 Flyme 4.0\n     */\n    private fun setMeizuStatusBarDarkIcon(activity: Activity, darkIcon: Boolean) {\n        try {\n            val lp = activity.window.attributes\n            val darkFlag = WindowManager.LayoutParams::class.java.getDeclaredField(\"MEIZU_FLAG_DARK_STATUS_BAR_ICON\")\n            val meizuFlags = WindowManager.LayoutParams::class.java.getDeclaredField(\"meizuFlags\")\n            darkFlag.isAccessible = true\n            meizuFlags.isAccessible = true\n            val bit = darkFlag.getInt(null)\n            var value = meizuFlags.getInt(lp)\n            if (darkIcon) {\n                value = value or bit\n            } else {\n                value = value and bit.inv()\n            }\n            meizuFlags.setInt(lp, value)\n            activity.window.attributes = lp\n        } catch (e: Exception) {\n            //e.printStackTrace();\n        }\n\n    }\n\n    ///////////////////////////////////////////////////////////////////////////////////\n\n    @TargetApi(Build.VERSION_CODES.KITKAT)\n    private fun clearPreviousSetting(activity: Activity) {\n        val decorView = activity.window.decorView as ViewGroup\n        val fakeStatusBarView = decorView.findViewById<View>(FAKE_STATUS_BAR_VIEW_ID)\n        if (fakeStatusBarView != null) {\n            decorView.removeView(fakeStatusBarView)\n            val rootView = (activity.findViewById<View>(android.R.id.content) as ViewGroup).getChildAt(0) as ViewGroup\n            rootView.setPadding(0, 0, 0, 0)\n        }\n    }\n\n    /**\n     * 添加半透明矩形条\n     *\n     * @param activity       需要设置的 activity\n     * @param statusBarAlpha 透明值\n     */\n    private fun addTranslucentView(activity: Activity, statusBarAlpha: Int) {\n        val contentView = activity.findViewById<View>(android.R.id.content) as ViewGroup\n        val fakeTranslucentView = contentView.findViewById<View>(FAKE_TRANSLUCENT_VIEW_ID)\n        if (fakeTranslucentView != null) {\n            if (fakeTranslucentView!!.visibility === View.GONE) {\n                fakeTranslucentView!!.visibility = View.VISIBLE\n            }\n            fakeTranslucentView!!.setBackgroundColor(Color.argb(statusBarAlpha, 0, 0, 0))\n        } else {\n            contentView.addView(createTranslucentStatusBarView(activity, statusBarAlpha))\n        }\n    }\n\n    /**\n     * 生成一个和状态栏大小相同的彩色矩形条\n     *\n     * @param activity 需要设置的 activity\n     * @param color    状态栏颜色值\n     * @return 状态栏矩形条\n     */\n    private fun createStatusBarView(activity: Activity, @ColorInt color: Int): View {\n        return createStatusBarView(activity, color, 0)\n    }\n\n    /**\n     * 生成一个和状态栏大小相同的半透明矩形条\n     *\n     * @param activity 需要设置的activity\n     * @param color    状态栏颜色值\n     * @param alpha    透明值\n     * @return 状态栏矩形条\n     */\n    private fun createStatusBarView(activity: Activity, @ColorInt color: Int, @IntRange(from = 0, to = 255) alpha: Int): View {\n        // 绘制一个和状态栏一样高的矩形\n        val statusBarView = View(activity)\n        val params = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity))\n        statusBarView.layoutParams = params\n        statusBarView.setBackgroundColor(calculateStatusColor(color, alpha))\n        statusBarView.id = FAKE_STATUS_BAR_VIEW_ID\n        return statusBarView\n    }\n\n    /**\n     * 设置根布局参数\n     */\n    private fun setRootView(activity: Activity) {\n        val parent = activity.findViewById<View>(android.R.id.content) as ViewGroup\n        var i = 0\n        val count = parent.childCount\n        while (i < count) {\n            val childView = parent.getChildAt(i)\n            if (childView is ViewGroup) {\n                childView.setFitsSystemWindows(true)\n                childView.clipToPadding = true\n            }\n            i++\n        }\n    }\n\n    /**\n     * 设置透明\n     */\n    private fun setTransparentForWindow(activity: Activity) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            activity.window.statusBarColor = Color.TRANSPARENT\n            activity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or\n                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            activity.window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,\n                WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)\n        }\n    }\n\n    /**\n     * 使状态栏透明\n     */\n    @TargetApi(Build.VERSION_CODES.KITKAT)\n    private fun transparentStatusBar(activity: Activity) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)\n            activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)\n            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)\n            activity.window.statusBarColor = Color.TRANSPARENT\n        } else {\n            activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)\n        }\n    }\n\n    /**\n     * 创建半透明矩形 View\n     *\n     * @param alpha 透明值\n     * @return 半透明 View\n     */\n    private fun createTranslucentStatusBarView(activity: Activity, @IntRange(from = 0, to = 255) alpha: Int): View {\n        // 绘制一个和状态栏一样高的矩形\n        val statusBarView = View(activity)\n        val params = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity))\n        statusBarView.layoutParams = params\n        statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0))\n        statusBarView.id = FAKE_TRANSLUCENT_VIEW_ID\n        return statusBarView\n    }\n\n    /**\n     * 获取状态栏高度\n     *\n     * @param context context\n     * @return 状态栏高度\n     */\n    private fun getStatusBarHeight(context: Context): Int {\n        // 获得状态栏高度\n        val resourceId = context.resources.getIdentifier(\"status_bar_height\", \"dimen\", \"android\")\n        return context.resources.getDimensionPixelSize(resourceId)\n    }\n\n    /**\n     * 计算状态栏颜色\n     *\n     * @param color color值\n     * @param alpha alpha值\n     * @return 最终的状态栏颜色\n     */\n    private fun calculateStatusColor(@ColorInt color: Int, @IntRange(from = 0, to = 255) alpha: Int): Int {\n        if (alpha == 0) {\n            return color\n        }\n        val a = 1 - alpha / 255f\n        var red = color shr 16 and 0xff\n        var green = color shr 8 and 0xff\n        var blue = color and 0xff\n        red = (red * a + 0.5).toInt()\n        green = (green * a + 0.5).toInt()\n        blue = (blue * a + 0.5).toInt()\n        return 0xff shl 24 or (red shl 16) or (green shl 8) or blue\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/widget/CustomToast.kt",
    "content": "package com.cxz.kotlin.baselibs.widget\n\nimport android.content.Context\nimport android.view.Gravity\nimport android.view.View\nimport android.widget.TextView\nimport android.widget.Toast\nimport com.cxz.kotlin.baselibs.R\n\n/**\n * Created by chenxz on 2018/6/9.\n * 自定义Toast\n */\nclass CustomToast {\n\n    private var toast: Toast\n    private var textView: TextView\n\n    constructor(context: Context?, message: String) : this(context, message, Toast.LENGTH_SHORT)\n\n    constructor(context: Context?, message: String, duration: Int) {\n        toast = Toast(context)\n        toast.duration = duration\n        val view = View.inflate(context, R.layout.toast_custom, null)\n        textView = view.findViewById(R.id.tv_prompt)\n        textView.text = message\n        toast.view = view\n        toast.setGravity(Gravity.CENTER, 0, 0)\n    }\n\n    fun show() {\n        toast.show()\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/widget/LoadingDialog.kt",
    "content": "package com.cxz.kotlin.baselibs.widget\n\nimport android.app.Dialog\nimport android.content.Context\nimport android.view.Gravity\nimport android.view.View\nimport com.cxz.kotlin.baselibs.R\nimport com.github.ybq.android.spinkit.style.Wave\nimport kotlinx.android.synthetic.main.layout_loading_dialog.*\n\n/**\n * @author chenxz\n * @date 2018/11/27\n * @desc\n */\n\nclass LoadingDialog : Dialog {\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, theme: Int) : super(context, theme)\n\n    override fun onWindowFocusChanged(hasFocus: Boolean) {\n        loadingBar.setIndeterminateDrawable(Wave())\n        super.onWindowFocusChanged(hasFocus)\n    }\n\n    companion object {\n\n        fun showDialog(context: Context, cancelable: Boolean): LoadingDialog? {\n            return showDialog(context, null, cancelable)\n        }\n\n        fun showDialog(context: Context, message: CharSequence?, cancelable: Boolean): LoadingDialog? {\n            val dialog = LoadingDialog(context, R.style.LoadingDialog)\n            dialog.setContentView(R.layout.layout_loading_dialog)\n            if (message.isNullOrBlank()) {\n                dialog.tv_load_msg?.visibility = View.GONE\n            } else {\n                dialog.tv_load_msg?.visibility = View.VISIBLE\n                dialog.tv_load_msg?.text = message\n            }\n            dialog.setCanceledOnTouchOutside(false)\n            dialog.setCancelable(cancelable)\n            dialog.window?.attributes?.gravity = Gravity.CENTER\n            val lp = dialog.window?.attributes\n            lp?.dimAmount = 0.2f\n            dialog.window?.attributes = lp\n            dialog.show()\n            return dialog\n        }\n    }\n\n}"
  },
  {
    "path": "baselibs/src/main/java/com/cxz/kotlin/baselibs/widget/OnNoDoubleClickListener.kt",
    "content": "package com.cxz.kotlin.baselibs.widget\n\nimport android.view.View\n\n/**\n * @author admin\n * @date 2018/11/23\n * @desc 防止连续点击\n */\nabstract class OnNoDoubleClickListener : View.OnClickListener {\n\n    private var mThrottleFirstTime: Long = 1000\n    private var mLastClickTime: Long = 0\n\n    constructor()\n    constructor(throttleFirstTime: Long) {\n        mThrottleFirstTime = throttleFirstTime\n    }\n\n    override fun onClick(v: View?) {\n        val currentTime = System.currentTimeMillis()\n        if (currentTime - mLastClickTime > mThrottleFirstTime) {\n            mLastClickTime = currentTime\n            onNoDoubleClick(v)\n        }\n    }\n\n    abstract fun onNoDoubleClick(v: View?)\n\n}"
  },
  {
    "path": "baselibs/src/main/res/drawable/bg_loading_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n    <solid android:color=\"@color/transparent_75\" />\n    <corners android:radius=\"6dp\" />\n\n</shape>"
  },
  {
    "path": "baselibs/src/main/res/drawable/bg_toast_custom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n    <solid android:color=\"@color/transparent_75\" />\n    <corners android:radius=\"6dp\" />\n\n</shape>"
  },
  {
    "path": "baselibs/src/main/res/layout/activity_base_title.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <include layout=\"@layout/base_toolbar\" />\n\n    <FrameLayout\n        android:id=\"@+id/base_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n</LinearLayout>"
  },
  {
    "path": "baselibs/src/main/res/layout/base_toolbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.google.android.material.appbar.AppBarLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/app_bar_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:fitsSystemWindows=\"true\"\n    android:theme=\"@style/AppTheme.AppBarOverlay\"\n    app:elevation=\"0dp\">\n\n    <androidx.appcompat.widget.Toolbar\n        android:id=\"@+id/base_toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        app:layout_scrollFlags=\"scroll|enterAlways\"\n        app:popupTheme=\"@style/AppTheme.PopupOverlay\">\n\n        <!--中间部分，除标题外可能会含有其他view-->\n        <FrameLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:layout_gravity=\"center\">\n\n            <TextView\n                android:id=\"@+id/base_title_tv\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"match_parent\"\n                android:layout_gravity=\"center\"\n                android:gravity=\"center\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"18sp\" />\n\n        </FrameLayout>\n\n    </androidx.appcompat.widget.Toolbar>\n\n</com.google.android.material.appbar.AppBarLayout>"
  },
  {
    "path": "baselibs/src/main/res/layout/layout_loading_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center\"\n    android:background=\"@drawable/bg_loading_dialog\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:paddingLeft=\"30dp\"\n    android:paddingTop=\"20dp\"\n    android:paddingRight=\"30dp\"\n    android:paddingBottom=\"20dp\">\n\n    <com.github.ybq.android.spinkit.SpinKitView\n        android:id=\"@+id/loadingBar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        app:SpinKit_Color=\"@color/white\" />\n\n    <TextView\n        android:id=\"@+id/tv_load_msg\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"10dp\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"@dimen/sp_14\" />\n\n</LinearLayout>"
  },
  {
    "path": "baselibs/src/main/res/layout/toast_custom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"center\"\n    android:orientation=\"vertical\">\n\n    <TextView\n        android:id=\"@+id/tv_prompt\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/bg_toast_custom\"\n        android:paddingBottom=\"@dimen/dp_24\"\n        android:paddingLeft=\"@dimen/dp_20\"\n        android:paddingRight=\"@dimen/dp_20\"\n        android:paddingTop=\"@dimen/dp_24\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"@dimen/sp_14\" />\n\n</LinearLayout>"
  },
  {
    "path": "baselibs/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#008577</color>\n    <color name=\"colorPrimaryDark\">#00574B</color>\n    <color name=\"colorAccent\">#D81B60</color>\n\n    <color name=\"white\">#ffffff</color>\n    <color name=\"transparent_75\">#ba000000</color>\n\n</resources>"
  },
  {
    "path": "baselibs/src/main/res/values/dimen.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <dimen name=\"dp_20\">20dp</dimen>\n    <dimen name=\"dp_24\">24dp</dimen>\n\n    <dimen name=\"sp_14\">14sp</dimen>\n\n</resources>"
  },
  {
    "path": "baselibs/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<resources>\n    <item name=\"statusbarutil_fake_status_bar_view\" type=\"id\" />\n    <item name=\"statusbarutil_translucent_view\" type=\"id\" />\n</resources>\n"
  },
  {
    "path": "baselibs/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">baselibs</string>\n</resources>\n"
  },
  {
    "path": "baselibs/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n    <style name=\"AppTheme.AppBarOverlay\" parent=\"ThemeOverlay.AppCompat.Dark.ActionBar\" />\n\n    <style name=\"AppTheme.PopupOverlay\" parent=\"ThemeOverlay.AppCompat.Light\" />\n\n    <style name=\"LoadingDialog\" parent=\"@android:style/Theme.Dialog\">\n        <item name=\"android:windowFrame\">@null</item>\n        <item name=\"android:windowIsFloating\">true</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:windowAnimationStyle\">@android:style/Animation.Dialog</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowNoTitle\">true</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "baselibs/src/main/res/xml/file_paths.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths>\n\n    <root-path\n        name=\"root\"\n        path=\"\" />\n\n    <files-path\n        name=\"files\"\n        path=\".\" />\n\n    <cache-path\n        name=\"cache\"\n        path=\".\" />\n\n    <external-path\n        name=\"external\"\n        path=\".\" />\n\n    <external-files-path\n        name=\"external_file_path\"\n        path=\".\" />\n\n    <external-cache-path\n        name=\"external_cache_path\"\n        path=\".\" />\n\n</paths>"
  },
  {
    "path": "baselibs/src/main/res/xml/network_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<network-security-config>\n    <base-config cleartextTrafficPermitted=\"true\" />\n</network-security-config>"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\napply from :\"config.gradle\"\n\nbuildscript {\n    ext.kotlin_version = '1.3.50'\n    repositories {\n        google()\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.5.2'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        jcenter()\n        maven { url 'https://jitpack.io' }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "config.gradle",
    "content": "ext {\n    android = [\n            compileSdkVersion: 30,\n            buildToolsVersion: \"30.0.3\",\n            minSdkVersion    : 16,\n            targetSdkVersion : 30,\n            versionCode      : 1,\n            versionName      : \"1.0.0\"\n    ]\n\n    dependVersion = [\n            supportSdkVersion: \"28.0.0\",\n            retrofit         : \"2.4.0\",\n            glide            : \"4.12.0\",\n            arouter          : \"1.5.1\",\n            rxJava           : \"2.2.2\",\n            rxAndroid        : \"2.1.0\",\n            rxKotlin         : \"2.3.0\",\n            anko             : \"0.10.7\"\n    ]\n\n    androidxDeps = [\n            \"appcompat\"       : 'androidx.appcompat:appcompat:1.2.0',\n            \"material\"        : 'com.google.android.material:material:1.2.1',\n            \"constraintlayout\": 'androidx.constraintlayout:constraintlayout:2.0.4',\n            \"androidx-core\"   : 'androidx.core:core-ktx:1.3.2',\n            \"recyclerview\"    : \"androidx.recyclerview:recyclerview:1.2.0\",\n    ]\n\n//    supportDeps = [\n//            \"supportv4\"        : \"com.android.support:support-v4:${dependVersion.supportSdkVersion}\",\n//            \"appcompatv7\"      : \"com.android.support:appcompat-v7:${dependVersion.supportSdkVersion}\",\n//            \"cardview\"         : \"com.android.support:cardview-v7:${dependVersion.supportSdkVersion}\",\n//            \"design\"           : \"com.android.support:design:${dependVersion.supportSdkVersion}\",\n//            \"constraint-layout\": \"com.android.support.constraint:constraint-layout:1.1.3\",\n//            \"annotations\"      : \"com.android.support:support-annotations:${dependVersion.supportSdkVersion}\"\n//    ]\n\n    retrofit = [\n            \"retrofit\"                : \"com.squareup.retrofit2:retrofit:${dependVersion.retrofit}\",\n            \"retrofitConverterGson\"   : \"com.squareup.retrofit2:converter-gson:${dependVersion.retrofit}\",\n            \"retrofitAdapterRxjava2\"  : \"com.squareup.retrofit2:adapter-rxjava2:${dependVersion.retrofit}\",\n            \"okhttp3LoggerInterceptor\": 'com.squareup.okhttp3:logging-interceptor:3.11.0',\n            \"retrofitConverterMoshi\"  : 'com.squareup.retrofit2:converter-moshi:2.4.0',\n            \"retrofitKotlinMoshi\"     : \"com.squareup.moshi:moshi-kotlin:1.7.0\"\n    ]\n\n    rxJava = [\n            \"rxJava\"   : \"io.reactivex.rxjava2:rxjava:${dependVersion.rxJava}\",\n            \"rxAndroid\": \"io.reactivex.rxjava2:rxandroid:${dependVersion.rxAndroid}\",\n            \"rxKotlin\" : \"io.reactivex.rxjava2:rxkotlin:${dependVersion.rxKotlin}\",\n            \"anko\"     : \"org.jetbrains.anko:anko:${dependVersion.anko}\"\n    ]\n\n    testDeps = [\n            \"junit\"                    : 'junit:junit:4.12',\n            \"runner\"                   : 'androidx.test.ext:junit:1.1.1',\n            \"espresso-core\"            : 'androidx.test.espresso:espresso-core:3.3.0',\n            \"leakcanary-debug\"         : 'com.squareup.leakcanary:leakcanary-android:1.6.1',\n            \"leakcanary-release\"       : 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1',\n            \"leakcanary-debug-fragment\": 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.1',\n            \"debug-db\"                 : 'com.amitshekhar.android:debug-db:1.0.4'\n    ]\n\n    commonDeps = [\n            \"multidex\"     : 'com.android.support:multidex:2.0.1',\n            \"logger\"       : 'com.orhanobut:logger:2.2.0',\n            \"glide\"        : \"com.github.bumptech.glide:glide:${dependVersion.glide}\",\n            \"eventbus\"     : 'org.greenrobot:eventbus:3.1.1',\n            \"spinkit\"      : 'com.github.ybq:Android-SpinKit:1.2.0',\n            \"arouter\"      : \"com.alibaba:arouter-api:${dependVersion.arouter}\",\n            \"rxpermissions\": 'com.github.tbruyelle:rxpermissions:0.10.2'\n    ]\n\n    annotationProcessorDeps = [\n            \"arouter-compiler\": \"com.alibaba:arouter-compiler:${dependVersion.arouter}\",\n            \"glide-compiler\"  : \"com.github.bumptech.glide:compiler:${dependVersion.glide}\"\n    ]\n\n    // supportLibs = supportDeps.values()\n    androisxLibs = androidxDeps.values()\n    networkLibs = retrofit.values()\n    rxJavaLibs = rxJava.values()\n    commonLibs = commonDeps.values()\n    annotationProcessorLibs = annotationProcessorDeps.values()\n\n}"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Sun Oct 27 19:16:55 CST 2019\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-5.4.1-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n\n# Disables R8 for Android Library modules only.\nandroid.enableR8.libraries = false\n# Disables R8 for all modules.\nandroid.enableR8 = false\n\n# trueǡɿģʽ  falseģʽл\nisModule=false\nisMeModule=false\nisNewsModule=false\nisVideoModule=false\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "module_me/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "module_me/build.gradle",
    "content": "if (isMeModule.toBoolean()) {\n    apply plugin: 'com.android.application'\n} else {\n    apply plugin: 'com.android.library'\n}\n\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\napply plugin: 'kotlin-kapt'\n\nkapt {\n    arguments {\n        arg(\"AROUTER_MODULE_NAME\", project.getName())\n    }\n    generateStubs = true\n}\n\nandroid {\n    compileSdkVersion rootProject.ext.android.compileSdkVersion\n    defaultConfig {\n        if (isMeModule.toBoolean()) {\n            applicationId \"com.cxz.module.me\"\n        }\n        minSdkVersion rootProject.ext.android.minSdkVersion\n        targetSdkVersion rootProject.ext.android.targetSdkVersion\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'\n\n    }\n\n    resourcePrefix \"me_\"\n\n    sourceSets {\n        main {\n            if (isMeModule.toBoolean()) {\n                manifest.srcFile 'src/main/module/AndroidManifest.xml'\n            } else {\n                manifest.srcFile 'src/main/AndroidManifest.xml'\n                //集成开发模式下排除debug文件夹中的所有Java文件\n                java {\n                    exclude 'debug/**'\n                }\n                kotlin {\n                    exclude 'debug/**'\n                }\n            }\n        }\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    testImplementation rootProject.ext.testDeps[\"junit\"]\n    androidTestImplementation rootProject.ext.testDeps[\"runner\"]\n    androidTestImplementation rootProject.ext.testDeps[\"espresso-core\"]\n    api project(':baselibs')\n    kapt rootProject.ext.annotationProcessorDeps[\"arouter-compiler\"]\n}\n"
  },
  {
    "path": "module_me/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "module_me/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.cxz.module.me\">\n\n    <application>\n        <activity android:name=\".MeMainActivity\"></activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "module_me/src/main/java/com/cxz/module/me/MeMainActivity.kt",
    "content": "package com.cxz.module.me\n\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.cxz.kotlin.baselibs.base.BaseMvpActivity\nimport com.cxz.module.me.mvp.contract.MeMainContract\nimport com.cxz.module.news.mvp.persenter.MeMainPresenter\n\n@Route(path = \"/me/main\")\nclass MeMainActivity : BaseMvpActivity<MeMainContract.View, MeMainContract.Presenter>(), MeMainContract.View {\n\n    override fun initView() {\n        super.initView()\n    }\n\n    override fun start() {\n    }\n\n    override fun createPresenter(): MeMainContract.Presenter = MeMainPresenter()\n\n    override fun attachLayoutRes(): Int = R.layout.me_activity_me_main\n\n}\n"
  },
  {
    "path": "module_me/src/main/java/com/cxz/module/me/mvp/contract/MeMainContract.kt",
    "content": "package com.cxz.module.me.mvp.contract\n\nimport com.cxz.kotlin.baselibs.mvp.IModel\nimport com.cxz.kotlin.baselibs.mvp.IPresenter\nimport com.cxz.kotlin.baselibs.mvp.IView\n\n/**\n * @author chenxz\n * @date 2018/12/25\n * @desc\n */\ninterface MeMainContract {\n\n    interface View : IView {\n\n    }\n\n    interface Presenter : IPresenter<View> {\n\n    }\n\n    interface Model : IModel {\n\n    }\n\n}"
  },
  {
    "path": "module_me/src/main/java/com/cxz/module/me/mvp/model/MeMainModel.kt",
    "content": "package com.cxz.module.me.mvp.model\n\nimport com.cxz.kotlin.baselibs.mvp.BaseModel\nimport com.cxz.module.me.mvp.contract.MeMainContract\n\n/**\n * @author chenxz\n * @date 2018/12/25\n * @desc\n */\nclass MeMainModel : BaseModel(), MeMainContract.Model {\n}"
  },
  {
    "path": "module_me/src/main/java/com/cxz/module/me/mvp/persenter/MeMainPresenter.kt",
    "content": "package com.cxz.module.news.mvp.persenter\n\nimport com.cxz.kotlin.baselibs.mvp.BasePresenter\nimport com.cxz.module.me.mvp.contract.MeMainContract\nimport com.cxz.module.me.mvp.model.MeMainModel\n\n/**\n * @author chenxz\n * @date 2018/12/25\n * @desc\n */\nclass MeMainPresenter : BasePresenter<MeMainContract.Model, MeMainContract.View>(), MeMainContract.Presenter {\n\n    override fun createModel(): MeMainContract.Model? = MeMainModel()\n\n}"
  },
  {
    "path": "module_me/src/main/java/debug/MeApplication.kt",
    "content": "package debug\n\nimport android.app.Application\nimport com.cxz.kotlin.baselibs.config.AppConfig\n\n/**\n * @author chenxz\n * @date 2018/12/22\n * @desc\n */\nclass MeApplication : Application() {\n\n    override fun onCreate() {\n        super.onCreate()\n        AppConfig.init(this)\n    }\n\n}"
  },
  {
    "path": "module_me/src/main/module/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.cxz.module.me\">\n\n    <application\n        android:name=\"debug.MeApplication\"\n        android:allowBackup=\"true\"\n        android:label=\"@string/me_app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\".MeMainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n                <action android:name=\"android.intent.action.VIEW\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "module_me/src/main/res/layout/me_activity_me_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".MeMainActivity\">\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"MeMain\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "module_me/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n</resources>\n"
  },
  {
    "path": "module_me/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"me_app_name\">module_me</string>\n</resources>\n"
  },
  {
    "path": "module_me/src/main/res/values/styles.xml",
    "content": "<resources>\n</resources>\n"
  },
  {
    "path": "module_news/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "module_news/build.gradle",
    "content": "if (isNewsModule.toBoolean()) {\n    apply plugin: 'com.android.application'\n} else {\n    apply plugin: 'com.android.library'\n}\n\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\napply plugin: 'kotlin-kapt'\n\nkapt {\n    arguments {\n        arg(\"AROUTER_MODULE_NAME\", project.getName())\n    }\n    generateStubs = true\n}\n\nandroid {\n    compileSdkVersion rootProject.ext.android.compileSdkVersion\n    defaultConfig {\n        if (isNewsModule.toBoolean()) {\n            applicationId \"com.cxz.module.news\"\n        }\n        minSdkVersion rootProject.ext.android.minSdkVersion\n        targetSdkVersion rootProject.ext.android.targetSdkVersion\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'\n\n    }\n\n    resourcePrefix \"news_\"\n\n    sourceSets {\n        main {\n            if (isNewsModule.toBoolean()) {\n                manifest.srcFile 'src/main/module/AndroidManifest.xml'\n            } else {\n                manifest.srcFile 'src/main/AndroidManifest.xml'\n                //集成开发模式下排除debug文件夹中的所有Java文件\n                java {\n                    exclude 'debug/**'\n                }\n                kotlin {\n                    exclude 'debug/**'\n                }\n            }\n        }\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    testImplementation rootProject.ext.testDeps[\"junit\"]\n    androidTestImplementation rootProject.ext.testDeps[\"runner\"]\n    androidTestImplementation rootProject.ext.testDeps[\"espresso-core\"]\n    api project(':baselibs')\n    kapt rootProject.ext.annotationProcessorDeps[\"arouter-compiler\"]\n}\n"
  },
  {
    "path": "module_news/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "module_news/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"com.cxz.module.news\">\n\n    <application>\n        <activity android:name=\".NewsMainActivity\">\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "module_news/src/main/java/com/cxz/module/news/NewsMainActivity.kt",
    "content": "package com.cxz.module.news\n\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.cxz.kotlin.baselibs.base.BaseMvpActivity\nimport com.cxz.kotlin.baselibs.ext.showToast\nimport com.cxz.module.news.mvp.contract.NewsMainContract\nimport com.cxz.module.news.mvp.persenter.NewsMainPresenter\n\n@Route(path = \"/news/main\")\nclass NewsMainActivity : BaseMvpActivity<NewsMainContract.View, NewsMainContract.Presenter>(), NewsMainContract.View {\n\n    @Autowired\n    @JvmField\n    var key1: String? = null\n    @Autowired\n    @JvmField\n    var key2: String? = null\n\n    override fun initView() {\n        ARouter.getInstance().inject(this)\n        super.initView()\n        showToast(\"key1: $key1, key2: $key2\")\n    }\n\n    override fun start() {\n    }\n\n    override fun createPresenter(): NewsMainContract.Presenter = NewsMainPresenter()\n\n    override fun attachLayoutRes(): Int = R.layout.news_activity_news_main\n\n}\n"
  },
  {
    "path": "module_news/src/main/java/com/cxz/module/news/NewsServiceImpl.kt",
    "content": "package com.cxz.module.news\n\nimport android.content.Context\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.cxz.kotlin.baselibs.provider.NewsService\n\n/**\n * @author chenxz\n * @date 2018/12/22\n * @desc\n */\n@Route(path = \"/news/service\", name = \"News\")\nclass NewsServiceImpl : NewsService {\n\n    override fun getNewsName(): String = \"这是一条新闻信息\"\n\n    override fun init(context: Context?) {\n    }\n}"
  },
  {
    "path": "module_news/src/main/java/com/cxz/module/news/mvp/contract/NewsMainContract.kt",
    "content": "package com.cxz.module.news.mvp.contract\n\nimport com.cxz.kotlin.baselibs.mvp.IModel\nimport com.cxz.kotlin.baselibs.mvp.IPresenter\nimport com.cxz.kotlin.baselibs.mvp.IView\n\n/**\n * @author chenxz\n * @date 2018/12/25\n * @desc\n */\ninterface NewsMainContract {\n\n    interface View : IView {\n\n    }\n\n    interface Presenter : IPresenter<View> {\n\n    }\n\n    interface Model : IModel {\n\n    }\n\n}"
  },
  {
    "path": "module_news/src/main/java/com/cxz/module/news/mvp/model/NewsMainModel.kt",
    "content": "package com.cxz.module.news.mvp.model\n\nimport com.cxz.kotlin.baselibs.mvp.BaseModel\nimport com.cxz.module.news.mvp.contract.NewsMainContract\n\n/**\n * @author chenxz\n * @date 2018/12/25\n * @desc\n */\nclass NewsMainModel : BaseModel(), NewsMainContract.Model {\n}"
  },
  {
    "path": "module_news/src/main/java/com/cxz/module/news/mvp/persenter/NewsMainPresenter.kt",
    "content": "package com.cxz.module.news.mvp.persenter\n\nimport com.cxz.kotlin.baselibs.mvp.BasePresenter\nimport com.cxz.module.news.mvp.contract.NewsMainContract\nimport com.cxz.module.news.mvp.model.NewsMainModel\n\n/**\n * @author chenxz\n * @date 2018/12/25\n * @desc\n */\nclass NewsMainPresenter : BasePresenter<NewsMainContract.Model, NewsMainContract.View>(), NewsMainContract.Presenter {\n\n    override fun createModel(): NewsMainContract.Model? = NewsMainModel()\n\n}"
  },
  {
    "path": "module_news/src/main/java/debug/NewsApplication.kt",
    "content": "package debug\n\nimport android.app.Application\nimport com.cxz.kotlin.baselibs.config.AppConfig\n\n/**\n * @author chenxz\n * @date 2018/12/22\n * @desc\n */\nclass NewsApplication : Application() {\n\n    override fun onCreate() {\n        super.onCreate()\n        AppConfig.init(this)\n    }\n\n}"
  },
  {
    "path": "module_news/src/main/module/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.cxz.module.news\">\n\n    <application\n        android:name=\"debug.NewsApplication\"\n        android:allowBackup=\"true\"\n        android:label=\"@string/news_app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\".NewsMainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n                <action android:name=\"android.intent.action.VIEW\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "module_news/src/main/res/layout/news_activity_news_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".NewsMainActivity\">\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"NewsMain\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "module_news/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n</resources>\n"
  },
  {
    "path": "module_news/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"news_app_name\">module_news</string>\n</resources>\n"
  },
  {
    "path": "module_news/src/main/res/values/styles.xml",
    "content": "<resources>\n</resources>\n"
  },
  {
    "path": "module_video/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "module_video/build.gradle",
    "content": "if (isVideoModule.toBoolean()) {\n    apply plugin: 'com.android.application'\n} else {\n    apply plugin: 'com.android.library'\n}\n\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\napply plugin: 'kotlin-kapt'\n\nkapt {\n    arguments {\n        arg(\"AROUTER_MODULE_NAME\", project.getName())\n    }\n    generateStubs = true\n}\n\nandroid {\n    compileSdkVersion rootProject.ext.android.compileSdkVersion\n    defaultConfig {\n        if (isVideoModule.toBoolean()) {\n            applicationId \"com.cxz.module.video\"\n        }\n        minSdkVersion rootProject.ext.android.minSdkVersion\n        targetSdkVersion rootProject.ext.android.targetSdkVersion\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'\n\n    }\n\n    resourcePrefix \"video_\"\n\n    sourceSets {\n        main {\n            if (isVideoModule.toBoolean()) {\n                manifest.srcFile 'src/main/module/AndroidManifest.xml'\n            } else {\n                manifest.srcFile 'src/main/AndroidManifest.xml'\n                //集成开发模式下排除debug文件夹中的所有Java文件\n                java {\n                    exclude 'debug/**'\n                }\n                kotlin {\n                    exclude 'debug/**'\n                }\n            }\n        }\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    testImplementation rootProject.ext.testDeps[\"junit\"]\n    androidTestImplementation rootProject.ext.testDeps[\"runner\"]\n    androidTestImplementation rootProject.ext.testDeps[\"espresso-core\"]\n    api project(':baselibs')\n    kapt rootProject.ext.annotationProcessorDeps[\"arouter-compiler\"]\n}\n"
  },
  {
    "path": "module_video/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "module_video/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"com.cxz.module.video\">\n\n    <application>\n        <activity android:name=\".VideoMainActivity\">\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "module_video/src/main/java/com/cxz/module/video/VideoMainActivity.kt",
    "content": "package com.cxz.module.video\n\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.cxz.kotlin.baselibs.base.BaseMvpActivity\nimport com.cxz.kotlin.baselibs.ext.showToast\nimport com.cxz.module.news.mvp.contract.VideoMainContract\nimport com.cxz.module.news.mvp.persenter.VideoMainPresenter\n\n@Route(path = \"/video/main\")\nclass VideoMainActivity : BaseMvpActivity<VideoMainContract.View, VideoMainContract.Presenter>(), VideoMainContract.View {\n\n    @Autowired\n    @JvmField\n    var key1: String? = null\n    @Autowired\n    @JvmField\n    var key2: String? = null\n\n    override fun initView() {\n        ARouter.getInstance().inject(this)\n        super.initView()\n\n        showToast(\"key1: $key1, key2: $key2\")\n\n    }\n\n    override fun start() {\n    }\n\n    override fun createPresenter(): VideoMainContract.Presenter = VideoMainPresenter()\n\n    override fun attachLayoutRes(): Int = R.layout.video_activity_video_main\n\n}\n"
  },
  {
    "path": "module_video/src/main/java/com/cxz/module/video/mvp/contract/VideoMainContract.kt",
    "content": "package com.cxz.module.news.mvp.contract\n\nimport com.cxz.kotlin.baselibs.mvp.IModel\nimport com.cxz.kotlin.baselibs.mvp.IPresenter\nimport com.cxz.kotlin.baselibs.mvp.IView\n\n/**\n * @author chenxz\n * @date 2018/12/25\n * @desc\n */\ninterface VideoMainContract {\n\n    interface View : IView {\n\n    }\n\n    interface Presenter : IPresenter<View> {\n\n    }\n\n    interface Model : IModel {\n\n    }\n\n}"
  },
  {
    "path": "module_video/src/main/java/com/cxz/module/video/mvp/model/VideoMainModel.kt",
    "content": "package com.cxz.module.news.mvp.model\n\nimport com.cxz.kotlin.baselibs.mvp.BaseModel\nimport com.cxz.module.news.mvp.contract.VideoMainContract\n\n/**\n * @author chenxz\n * @date 2018/12/25\n * @desc\n */\nclass VideoMainModel : BaseModel(), VideoMainContract.Model {\n}"
  },
  {
    "path": "module_video/src/main/java/com/cxz/module/video/mvp/persenter/VideoMainPresenter.kt",
    "content": "package com.cxz.module.news.mvp.persenter\n\nimport com.cxz.kotlin.baselibs.mvp.BasePresenter\nimport com.cxz.module.news.mvp.contract.VideoMainContract\nimport com.cxz.module.news.mvp.model.VideoMainModel\n\n/**\n * @author chenxz\n * @date 2018/12/25\n * @desc\n */\nclass VideoMainPresenter : BasePresenter<VideoMainContract.Model, VideoMainContract.View>(), VideoMainContract.Presenter {\n\n    override fun createModel(): VideoMainContract.Model? = VideoMainModel()\n\n}"
  },
  {
    "path": "module_video/src/main/java/debug/VideoApplication.kt",
    "content": "package debug\n\nimport android.app.Application\nimport com.cxz.kotlin.baselibs.config.AppConfig\n\n\n/**\n * @author chenxz\n * @date 2018/12/22\n * @desc\n */\nclass VideoApplication : Application() {\n\n    override fun onCreate() {\n        super.onCreate()\n        AppConfig.init(this)\n    }\n\n}"
  },
  {
    "path": "module_video/src/main/module/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.cxz.module.video\">\n\n    <application\n        android:name=\"debug.VideoApplication\"\n        android:allowBackup=\"true\"\n        android:label=\"@string/video_app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\".VideoMainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n                <action android:name=\"android.intent.action.VIEW\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "module_video/src/main/res/layout/video_activity_video_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".VideoMainActivity\">\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"VideoMain\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"/>\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "module_video/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n</resources>\n"
  },
  {
    "path": "module_video/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"video_app_name\">module_video</string>\n</resources>\n"
  },
  {
    "path": "module_video/src/main/res/values/styles.xml",
    "content": "<resources>\n\n</resources>\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app', ':baselibs', ':module_news', ':module_me', ':module_video'\n"
  }
]