[
  {
    "path": ".circleci/config.yml",
    "content": "references:\n  cache_key: &cache_key\n    key: jars-{{ checksum \"build.gradle\" }}-{{ checksum  \"app/build.gradle\" }}-{{ checksum \"gradle/wrapper/gradle-wrapper.properties\" }}\n\njobs:\n  build:\n    docker:\n      - image: circleci/android:api-28-alpha\n    environment:\n      JAVA_TOOL_OPTIONS: \"-Xmx1024m\"\n      GRADLE_OPTS: \"-Dorg.gradle.daemon=false -Dorg.gradle.workers.max=2\"\n      TERM: dumb\n    steps:\n      - checkout\n      - restore_cache:\n          <<: *cache_key\n      - run:\n          name: Download Dependencies\n          command: ./gradlew dependencies\n      - save_cache:\n          <<: *cache_key\n          paths:\n            - ~/.gradle/caches\n            - ~/.gradle/wrapper\n      - run:\n          name: Run JVM Tests & Lint\n          command: ./gradlew check\n      - store_artifacts:\n          path: app/build/reports\n          destination: reports\n      - store_test_results:\n          path: app/build/test-results"
  },
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n"
  },
  {
    "path": ".idea/codeStyles/Project.xml",
    "content": "<component name=\"ProjectCodeStyleConfiguration\">\n  <code_scheme name=\"Project\" version=\"173\">\n    <codeStyleSettings language=\"XML\">\n      <indentOptions>\n        <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\n      </indentOptions>\n      <arrangement>\n        <rules>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>xmlns:android</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>xmlns:.*</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n              <order>BY_NAME</order>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*:id</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*:name</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>name</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>style</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n              <order>BY_NAME</order>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                </AND>\n              </match>\n              <order>ANDROID_ATTRIBUTE_ORDER</order>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>.*</XML_NAMESPACE>\n                </AND>\n              </match>\n              <order>BY_NAME</order>\n            </rule>\n          </section>\n        </rules>\n      </arrangement>\n    </codeStyleSettings>\n  </code_scheme>\n</component>"
  },
  {
    "path": ".idea/codeStyles/codeStyleConfig.xml",
    "content": "<component name=\"ProjectCodeStyleConfiguration\">\n  <state>\n    <option name=\"PREFERRED_PROJECT_CODE_STYLE\" value=\"Default (1)\" />\n  </state>\n</component>"
  },
  {
    "path": ".idea/dictionaries/andy.xml",
    "content": "<component name=\"ProjectDictionaryState\">\n  <dictionary name=\"andy\" />\n</component>"
  },
  {
    "path": ".idea/encodings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"Encoding\" defaultCharsetForPropertiesFiles=\"UTF-8\">\n    <file url=\"PROJECT\" charset=\"UTF-8\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/gradle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linkedExternalProjectsSettings\">\n      <GradleProjectSettings>\n        <option name=\"distributionType\" value=\"DEFAULT_WRAPPED\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"gradleHome\" value=\"$USER_HOME$/Downloads/gradle-4.6\" />\n        <option name=\"modules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n            <option value=\"$PROJECT_DIR$/app\" />\n          </set>\n        </option>\n        <option name=\"resolveModulePerSourceSet\" value=\"false\" />\n      </GradleProjectSettings>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/misc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"NullableNotNullManager\">\n    <option name=\"myDefaultNullable\" value=\"android.support.annotation.Nullable\" />\n    <option name=\"myDefaultNotNull\" value=\"android.support.annotation.NonNull\" />\n    <option name=\"myNullables\">\n      <value>\n        <list size=\"12\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.Nullable\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nullable\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"javax.annotation.CheckForNull\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.Nullable\" />\n          <item index=\"4\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.Nullable\" />\n          <item index=\"5\" class=\"java.lang.String\" itemvalue=\"androidx.annotation.Nullable\" />\n          <item index=\"6\" class=\"java.lang.String\" itemvalue=\"androidx.annotation.RecentlyNullable\" />\n          <item index=\"7\" class=\"java.lang.String\" itemvalue=\"org.checkerframework.checker.nullness.qual.Nullable\" />\n          <item index=\"8\" class=\"java.lang.String\" itemvalue=\"org.checkerframework.checker.nullness.compatqual.NullableDecl\" />\n          <item index=\"9\" class=\"java.lang.String\" itemvalue=\"org.checkerframework.checker.nullness.compatqual.NullableType\" />\n          <item index=\"10\" class=\"java.lang.String\" itemvalue=\"android.annotation.Nullable\" />\n          <item index=\"11\" class=\"java.lang.String\" itemvalue=\"com.android.annotations.Nullable\" />\n        </list>\n      </value>\n    </option>\n    <option name=\"myNotNulls\">\n      <value>\n        <list size=\"11\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.NotNull\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nonnull\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.NonNull\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.NonNull\" />\n          <item index=\"4\" class=\"java.lang.String\" itemvalue=\"androidx.annotation.NonNull\" />\n          <item index=\"5\" class=\"java.lang.String\" itemvalue=\"androidx.annotation.RecentlyNonNull\" />\n          <item index=\"6\" class=\"java.lang.String\" itemvalue=\"org.checkerframework.checker.nullness.qual.NonNull\" />\n          <item index=\"7\" class=\"java.lang.String\" itemvalue=\"org.checkerframework.checker.nullness.compatqual.NonNullDecl\" />\n          <item index=\"8\" class=\"java.lang.String\" itemvalue=\"org.checkerframework.checker.nullness.compatqual.NonNullType\" />\n          <item index=\"9\" class=\"java.lang.String\" itemvalue=\"android.annotation.NonNull\" />\n          <item index=\"10\" class=\"java.lang.String\" itemvalue=\"com.android.annotations.NonNull\" />\n        </list>\n      </value>\n    </option>\n  </component>\n  <component name=\"ProjectRootManager\" version=\"2\" languageLevel=\"JDK_1_8\" project-jdk-name=\"1.8\" project-jdk-type=\"JavaSDK\">\n    <output url=\"file://$PROJECT_DIR$/build/classes\" />\n  </component>\n  <component name=\"ProjectType\">\n    <option name=\"id\" value=\"Android\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/modules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n      <module fileurl=\"file://$PROJECT_DIR$/.idea/SimpleEyes.iml\" filepath=\"$PROJECT_DIR$/.idea/SimpleEyes.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/app/app.iml\" filepath=\"$PROJECT_DIR$/app/app.iml\" />\n    </modules>\n  </component>\n</project>"
  },
  {
    "path": ".idea/runConfigurations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <option name=\"ignoredProducers\">\n      <set>\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer\" />\n      </set>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": "LICENSE",
    "content": "   Copyright [2019] [AndyJennifer]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# SimpleEyes\n\n[![CircleCI](https://circleci.com/gh/AndyJennifer/SimpleEyes.svg?style=shield)](https://circleci.com/gh/AndyJennifer/SimpleEyes)\n[![API](https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=21)\n\nSimpleEyes 一款基于 Kotlin 开发的短视频项目。该项目为如下两个分支:\n\n- [master 分支](https://github.com/AndyJennifer/SimpleEyes/tree/master) ：MVP + Retrofit + Rxjava\n- [jetpack 分支](https://github.com/AndyJennifer/SimpleEyes/tree/simpleeyes-jetpack)：MVVM + Android Jetpack + Retrofit + RxJava \n\n你可以根据自己的需要，选择不同的分支。\n\n## 项目展示 ☘\n\n![picture_1.png](https://upload-images.jianshu.io/upload_images/2824145-9c4c8943bc9eebc7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)\n\n## 闪光点 ✨✨\n\n- 100% 纯 Kotlin 开发。巧妙了运用了 Kotlin 的诸多特性，如扩展函数，数据类，委托等。\n- 丰富的动画与自定义View。如下拉刷新、视差动画、TextView打字动画、嵌套滑动，状态布局。\n- RxJava 常用操作符的使用，并新增了全局错误处理。\n- 支持视频的横竖屏切换、音量控制、亮度控制等。\n- 良好的代码规范与注释。\n- 良好的设计模式与架构。\n- .....\n\n## 完成的功能  💼\n\n- 闪屏页实现\n  - [x] 开场短视频\n  - [x] 开场动画效果1、2\n- 首页\n  - [x] 底部 Tab 切换\n  - [x] 顶部轮播\n  - [x] 下拉视差刷新，加载更多\n  - [x] 视频搜索\n  - [x] 每日精选\n  - [x] 登录页\n- 发现\n  - [x] 热门、分类、作者、\n  - [x] 全部分类\n  - [x] 视频搜索\n- 关注\n  - [x] 全部作者\n  - [x] 作者简介及视频\n- 我的\n  - [ ] 我的消息\n  - [ ] 我的缓存\n\n## 使用的库 💪\n\n站在巨人的肩膀上。可以看得更远。该项目中运用了以下开源库：\n\n- [Fresco](https://github.com/facebook/fresco)\n- [ARouter](https://github.com/alibaba/ARouter)\n- [Fragmentation](https://github.com/YoKeyword/Fragmentation)\n- [RxJava](https://github.com/ReactiveX/RxJava)\n- [Retrofit](https://github.com/square/retrofit)\n- [BaseRecyclerViewAdapterHelper](https://github.com/CymChad/BaseRecyclerViewAdapterHelper)\n- [IjkPlayer](https://github.com/Bilibili/ijkplayer)\n- [FlexBox-Layout](https://github.com/google/flexbox-layout)\n- [Banner](https://github.com/youth5201314/banner)\n- [Android KTX](https://developer.android.google.cn/kotlin/ktx)\n- [Koin](https://github.com/InsertKoinIO/koin)\n\n## 声明 📢\n\n感谢 [开眼App](http://www.kaiyanapp.com) 提供参考，本人是豆瓣粉丝，使用了其中的 Api ，并非攻击。如构成侵权，请及时通知我删除或者修改。数据来源来自[开眼](https://www.kaiyanapp.com/) ，一切解释权归开眼所有。\n\n## 最后\n\n注意：此开源项目仅做学习交流使用。如用到实际项目，还需多考虑其他因素，请多多斟酌。如果你觉得该项目不错，欢迎点击 star ❤️，follow，也可以帮忙分享给你更多的朋友。你的支持与鼓励是给我继续做好该项目的最大动力。\n\n## 联系我\n\n- QQ:443696320\n- 简书：[AndyandJennifer](https://www.jianshu.com/users/921c778fb5e1/timeline)\n- 掘金：[AndyandJennifer](https://juejin.im/user/5acc1ea06fb9a028bc2e0fc1)\n- Email: [andyjennifer@126.com](andyjennifer@126.com)\n\n## License\n\n```text\n   Copyright [2019] [AndyJennifer]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n```\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\napply plugin: 'kotlin-android'\n\napply plugin: 'kotlin-android-extensions'\n\napply plugin: 'kotlin-kapt'\n\nkapt {\n    arguments {\n        arg(\"AROUTER_MODULE_NAME\", project.getName())\n    }\n}\nandroid {\n    compileSdkVersion 28\n    buildToolsVersion '28.0.3'\n    defaultConfig {\n        applicationId \"com.jennifer.andy.simpleeyes\"\n        minSdkVersion 21\n        targetSdkVersion 28\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility 1.8\n        targetCompatibility 1.8\n    }\n    \n    kotlinOptions{\n        jvmTarget = \"1.8\"\n    }\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n    implementation 'androidx.appcompat:appcompat:1.1.0'\n    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'\n    testImplementation 'junit:junit:4.12'\n    androidTestImplementation 'androidx.test:runner:1.2.0'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'\n    implementation 'com.google.android.material:material:1.0.0'\n\n    //RxJava\n    implementation 'io.reactivex.rxjava2:rxjava:2.2.0'\n    implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'\n\n    //retrofit\n    implementation 'com.squareup.retrofit2:retrofit:2.4.0'\n    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'\n    implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'\n    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'\n\n    //fragmentation\n    implementation 'me.yokeyword:fragmentationx:1.0.1'\n\n    //recyclerView\n    implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.44'\n\n    //fresco\n    implementation 'com.facebook.fresco:fresco:1.11.0'\n\n    //ijk\n    implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'\n    implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8'\n    implementation 'tv.danmaku.ijk.media:ijkplayer-armv5:0.8.8'\n    implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'\n    implementation 'tv.danmaku.ijk.media:ijkplayer-x86:0.8.8'\n    implementation 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.8.8'\n\n    //loadingView\n    implementation 'com.wang.avi:library:2.1.3'\n    implementation 'com.youth.banner:banner:1.4.10'\n\n    //flex\n    implementation 'com.google.android:flexbox:0.3.2'\n\n    //pageIndicator\n    implementation 'com.romandanylyk:pageindicatorview:1.0.1@aar'\n\n    //ARouter\n    api 'com.alibaba:arouter-api:1.4.1'\n    kapt 'com.alibaba:arouter-compiler:1.2.2'\n\n    //LeakCanary\n    debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'\n    releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'\n    debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'\n\n    //ktx\n    implementation 'androidx.core:core-ktx:1.1.0'\n\n    //autodispose\n    implementation 'com.uber.autodispose:autodispose-android:1.4.0'\n    implementation 'com.uber.autodispose:autodispose-android-archcomponents:1.4.0'\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/andy/Library/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\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/jennifer/andy/simpleeyes/ExampleInstrumentedTest.kt",
    "content": "package com.jennifer.andy.simpleeyes\n\nimport androidx.test.InstrumentationRegistry\nimport androidx.test.runner.AndroidJUnit4\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\nimport org.junit.runner.RunWith\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.jennifer.andy.simplemusic\", 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    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"com.jennifer.andy.simpleeyes\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n\n    <application\n        android:name=\".AndyApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:networkSecurityConfig=\"@xml/network_security_config\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\"\n        tools:ignore=\"UnusedAttribute\">\n\n        <!--中间跳转页-->\n        <activity android:name=\".router.SchemeFilterActivity\">\n            <!--Scheme-->\n            <intent-filter>\n                <data android:scheme=\"eyepetizer\" />\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n            </intent-filter>\n\n\n            <!-- 网页跳转到App-->\n            <intent-filter android:autoVerify=\"true\">\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data\n                    android:host=\"www.kaiyanapp.com\"\n                    android:scheme=\"http\" />\n                <data\n                    android:host=\"www.kaiyanapp.com/\"\n                    android:scheme=\"https\" />\n            </intent-filter>\n        </activity>\n\n        <!--加载界面-->\n        <activity\n            android:name=\".ui.splash.LandingActivity\"\n            android:screenOrientation=\"portrait\"\n            android:theme=\"@style/SplashTheme\"\n            tools:ignore=\"LockedOrientationActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n\n        <!--主界面-->\n        <activity\n            android:name=\".ui.MainActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n\n        <!--登录界面-->\n        <activity\n            android:name=\".ui.login.LoginActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n\n        <!--视频播放界面-->\n        <activity\n            android:name=\".ui.video.VideoDetailActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n        <!--搜索界面-->\n        <activity\n            android:name=\".ui.search.SearchHotActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n        <!--每日精选-->\n        <activity\n            android:name=\".ui.home.DailyEliteActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n        <!--全部分类-->\n        <activity\n            android:name=\".ui.feed.AllCategoryActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n\n        <!--网页展示Activity-->\n        <activity\n            android:name=\".ui.feed.WebViewActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n\n        <!--根据视频id获取视频信息-->\n        <activity\n            android:name=\".ui.video.VideoInfoByIdActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n        <!--排行榜-->\n        <activity\n            android:name=\".ui.feed.RankListActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n\n        <!--热门专题-->\n        <activity\n            android:name=\".ui.feed.TopicActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n\n        <!--360全景-->\n        <activity\n            android:name=\".ui.feed.TagActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n\n        <!--种类下tab信息-->\n        <activity\n            android:name=\".ui.feed.CategoryTabActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n\n        <!--全部作者-->\n        <activity\n            android:name=\".ui.follow.AllAuthorActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n\n        <!--作者信息详细界面-->\n        <activity\n            android:name=\".ui.author.AuthorTagDetailActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n\n        <!--公共RecyclerView界面，根据url加载数据-->\n        <activity\n            android:name=\".ui.common.CommonRecyclerActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            tools:ignore=\"LockedOrientationActivity\" />\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/AndyApplication.kt",
    "content": "package com.jennifer.andy.simpleeyes\n\nimport android.app.Application\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.facebook.drawee.backends.pipeline.Fresco\nimport com.facebook.imagepipeline.core.ImagePipelineConfig\nimport com.squareup.leakcanary.LeakCanary\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/10/15 09:27\n * Description:\n */\n\nclass AndyApplication : Application() {\n\n    companion object {\n\n        lateinit var INSTANCE: Application\n\n    }\n\n    override fun onCreate() {\n        super.onCreate()\n        INSTANCE = this\n        initARoute()\n        initFresco()\n        initLeakCanary()\n    }\n\n    /**\n     * 初始化路由操作\n     */\n    private fun initARoute() {\n        if (BuildConfig.DEBUG) {\n            ARouter.openLog()\n            ARouter.openDebug()\n        }\n        ARouter.init(this)\n    }\n\n    /**\n     * 初始化Fresco,打开压缩\n     */\n    private fun initFresco() {\n        val config = ImagePipelineConfig.newBuilder(this)\n                .setDownsampleEnabled(true)\n                .build()\n        Fresco.initialize(this, config)\n    }\n\n    /**\n     * 初始化LeakCanary，检测内存泄露\n     */\n    private fun initLeakCanary() {\n        if (LeakCanary.isInAnalyzerProcess(this)) {\n            return\n        }\n        LeakCanary.install(this)\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/UserPreferences.kt",
    "content": "package com.jennifer.andy.simpleeyes\n\nimport android.content.Context\nimport android.content.SharedPreferences\nimport androidx.core.content.edit\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/9 09:34\n * Description:用户配置信息\n */\n\nobject UserPreferences {\n\n    private const val NAME = \"andy.\"\n    private const val KEY_IS_USER_LOGIN = \"is_user_login\"\n    private const val KEY_IS_FIRST_LOGIN = \"is_first_login\"\n    private const val KEY_IS_SHOW_USER_ANIM = \"key_is_show_user_anim\"\n\n    /**\n     * 保存用户是否登录配置\n     */\n    fun saveUserIsLogin(isUserLogin: Boolean) {\n        getSharedPreferences().edit {\n            putBoolean(KEY_IS_USER_LOGIN, isUserLogin)\n        }\n    }\n\n    /**\n     * 是否像用户展示过动画\n     */\n    fun saveShowUserAnim(isUserLogin: Boolean) {\n        getSharedPreferences().edit {\n            putBoolean(KEY_IS_SHOW_USER_ANIM, isUserLogin)\n        }\n    }\n\n    /**\n     * 保存用户是否是第一次登录\n     */\n    fun saveUserIsFirstLogin(isFirstLogin: Boolean) {\n        getSharedPreferences().edit {\n            putBoolean(KEY_IS_FIRST_LOGIN, isFirstLogin)\n        }\n    }\n\n    /**\n     * 获取用户是否登录\n     */\n    fun getUserIsLogin() = getSharedPreferences().getBoolean(KEY_IS_USER_LOGIN, false)\n\n    /**\n     * 是否像用户展示过动画\n     */\n    fun getShowUserAnim() = getSharedPreferences().getBoolean(KEY_IS_SHOW_USER_ANIM, false);\n\n    /**\n     * 获取用户是否是第一次登录\n     */\n    fun getUserIsFirstLogin() = getSharedPreferences().getBoolean(KEY_IS_FIRST_LOGIN, true)\n\n\n    private fun getSharedPreferences(): SharedPreferences {\n        return AndyApplication.INSTANCE.getSharedPreferences(NAME, Context.MODE_PRIVATE)\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/entity/AndyInfo.kt",
    "content": "package com.jennifer.andy.simpleeyes.entity\n\nimport java.io.Serializable\n\n/**\n * Author:  andy.xwt\n * Date:    2017/10/19 19:00\n * Description: 页面数据信息,主要是视频信息\n */\n\n\ndata class AndyInfo(var count: Int,\n                    var total: Int,\n                    var nextPageUrl: String?,\n                    var date: Long,\n                    var nextPublishTime: Long,\n                    var topIssue: TopIssue,\n                    var refreshCount: Int,\n                    var lastStartId: Int,\n                    var itemList: MutableList<Content>) : Serializable\n\ndata class JenniferInfo(var nextPageUrl: String?,\n                        var date: Long,\n                        var nextPublishTime: Long,\n                        var topIssue: TopIssue,\n                        var refreshCount: Int,\n                        var lastStartId: Int,\n                        var issueList: MutableList<ContentBean>) : Serializable\n\ndata class Content(var type: String,\n                   var data: ContentBean,\n                   var id: String,\n                   var date: Long,\n                   var tag: String) : Serializable\n\n\ndata class ContentBean(var dataType: String,\n                       var header: Header?,\n                       var follow: FollowInfo?,\n                       var content: Content?,\n                       var itemList: MutableList<Content>,\n                       var id: String,\n                       var title: String,\n                       var description: String,\n                       var library: String,\n                       var tags: MutableList<TagsBean>,\n                       var consumption: ConsumptionBean,\n                       var image: String,\n                       var resourceType: String,\n                       var slogan: String,\n                       var provider: ProviderBean,\n                       var category: String,\n                       var author: AuthorBean,\n                       var cover: CoverBean,\n                       var playUrl: String,\n                       var thumbPlayUrl: String,\n                       var duration: Int,\n                       var webUrl: WebUrlBean,\n                       var releaseTime: Long,\n                       var playInfo: MutableList<PlayInfoBean>,\n                       var campaign: String,\n                       var waterMarks: String,\n                       var type: String?,\n                       var titlePgc: String,\n                       var descriptionPgc: String,\n                       var remark: String,\n                       var idx: Int,\n                       var shareAdTrack: String,\n                       var favoriteAdTrack: String,\n                       var webAdTrack: String,\n                       var date: Long,\n                       val label: Label?,\n                       var labelList: MutableList<*>,\n                       var descriptionEditor: String,\n                       var isCollected: Boolean,\n                       var isPlayed: Boolean,\n                       var subtitles: MutableList<*>,\n                       var lastViewTime: String,\n                       var playlists: String,\n                       var text: String,\n                       var icon: String,\n                       var iconType: String,\n                       var height: Int,\n                       var src: Int,\n                       var actionUrl: String?) : Serializable\n\n\ndata class Header(var id: String,\n                  var title: String?,\n                  var subTitle: String?,\n                  var subTitleFont: String,\n                  var textAlign: String?,\n                  var font: String,\n                  var cover: String,\n                  var label: Any,\n                  var actionUrl: String?,\n                  var labelList: MutableList<*>,\n                  var icon: String?,\n                  var iconType: String,\n                  var description: String?,\n                  var follow: FollowInfo?,\n                  var time: Long?) : Serializable\n\n\ndata class Label(var text: String,\n                 var card: String,\n                 var detail: String) : Serializable\n\ndata class TagsBean(var id: Int,\n                    var name: String,\n                    var actionUrl: String,\n                    var adTrack: String) : Serializable\n\n\ndata class ConsumptionBean(var collectionCount: String,\n                           var shareCount: String,\n                           var replyCount: String) : Serializable\n\n\ndata class TopIssue(var type: String,\n                    var data: TopIssueBean,\n                    var tag: String) : Serializable\n\ndata class TopIssueBean(var dataType: String,\n                        var count: Int,\n                        var itemList: MutableList<Content>) : Serializable\n\n\ndata class ProviderBean(var name: String,\n                        var alias: String,\n                        var icon: String) : Serializable\n\ndata class AuthorBean(var id: String,\n                      var icon: String,\n                      var name: String,\n                      var description: String,\n                      var link: String,\n                      var latestReleaseTime: Long,\n                      var videoNum: Int,\n                      var adTrack: String,\n                      var follow: FollowInfo,\n                      var shield: ShieldBean,\n                      var approvedNotReadyVideoCount: Int,\n                      var isIfPgc: Boolean = false) : Serializable\n\n\ndata class ShieldBean(\n        var itemType: String,\n        var itemId: Int,\n        var isShielded: Boolean) : Serializable\n\n\ndata class CoverBean(var feed: String,\n                     var detail: String,\n                     var blurred: String,\n                     var sharing: String,\n                     var homepage: String) : Serializable\n\n\ndata class WebUrlBean(var raw: String,\n                      var forWeibo: String) : Serializable\n\n\ndata class PlayInfoBean(var height: Int,\n                        var width: Int,\n                        var name: String,\n                        var type: String,\n                        var url: String,\n                        var urlList: MutableList<UrlListBean>) : Serializable\n\ndata class UrlListBean(var name: String,\n                       var url: String,\n                       var size: Double) : Serializable\n\n\ndata class Category(var categoryInfo: CategoryInfo,\n                    var tabInfo: TabInfo)\n\ndata class CategoryInfo(var dataType: String,\n                        var id: String,\n                        var name: String,\n                        var description: String,\n                        var headerImage: String,\n                        var actionUrl: String,\n                        var followInfo: FollowInfo)\n\n\ndata class Tab(var tabInfo: TabInfo,\n               var pgcInfo: PgcInfo) : Serializable\n\ndata class TabInfo(var tabList: MutableList<TabDetailInfo>,\n                   var defaultIdx: Int) : Serializable\n\n\ndata class TabDetailInfo(var id: Int,\n                         var name: String,\n                         var apiUrl: String) : Serializable\n\n\ndata class FollowInfo(var itemType: String,\n                      var itemId: String,\n                      var followed: Boolean) : Serializable\n\n\ndata class PgcInfo(var dataType: String,\n                   var id: Int,\n                   var icon: String,\n                   var name: String,\n                   var brief: String,\n                   var description: String,\n                   var actionUrl: String,\n                   var followCount: Int,\n                   var shareCount: Int,\n                   var videoCount: Int,\n                   var collectCount: Int,\n                   var followInfo: FollowInfo) : Serializable"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/manager/ActivityManager.kt",
    "content": "package com.jennifer.andy.simpleeyes.manager\n\nimport android.app.Activity\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/8/14 00:15\n * Description:Activity管理类\n */\n\nclass ActivityManager private constructor() {\n\n    private val sActivityList = ArrayList<Activity>()\n\n    companion object {\n        fun getInstance(): ActivityManager {\n            return BaseAppManagerHolder.instance\n        }\n    }\n\n    private object BaseAppManagerHolder {\n        val instance = ActivityManager()\n    }\n\n    /**\n     * 当前activity的个数\n     */\n    val mActivitySize: Int get() = sActivityList.size\n\n    val mForwardActivity: Activity? @Synchronized get() = if (mActivitySize > 0) sActivityList[mActivitySize - 1] else null\n\n    /**\n     * 添加相应activity\n     */\n    @Synchronized\n    fun removeActivity(activity: Activity) {\n        if (activity in sActivityList) sActivityList.remove(activity)\n    }\n\n    /**\n     * 添加相应的activity\n     */\n    @Synchronized\n    fun addActivity(activity: Activity) {\n        sActivityList.add(activity)\n    }\n\n    /**\n     * 清除栈内activity\n     */\n    @Synchronized\n    fun clear() {\n        var i = sActivityList.size - 1\n        while (i > -1) {\n            val activity = sActivityList[i]\n            removeActivity(activity)\n            activity.finish()\n            i = sActivityList.size\n            i--\n        }\n    }\n\n    /**\n     * 清除栈顶activity\n     */\n    @Synchronized\n    fun clearToTop() {\n        var i = sActivityList.size - 2\n        while (i > -1) {\n            val activity = sActivityList[i]\n            removeActivity(activity)\n            activity.finish()\n            i = sActivityList.size - 1\n            i--\n        }\n    }\n\n    /**\n     * 获取最上层的Activity\n     */\n    @Synchronized\n    fun getTopActivity(): Activity? {\n        return if (sActivityList.isNotEmpty()) {\n            sActivityList[sActivityList.size - 1]\n        } else {\n            null\n        }\n\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/net/Api.kt",
    "content": "package com.jennifer.andy.simpleeyes.net\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/10/15 09:54\n * Description:\n */\n\nobject Api {\n\n\n    /**\n     * 主域名\n     */\n    val BASE_URL: String get() = \"http://baobab.kaiyanapp.com/\"\n\n    /**\n     * 获取默认Service\n     */\n    fun getDefault() = RetrofitConfig.getDefaultService()\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/net/ApiService.kt",
    "content": "package com.jennifer.andy.simpleeyes.net\n\nimport com.jennifer.andy.simpleeyes.entity.*\nimport io.reactivex.Observable\nimport okhttp3.ResponseBody\nimport retrofit2.Call\nimport retrofit2.http.*\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/10/10 22:46\n * Description:\n */\n\ninterface ApiService {\n\n    ///////////////////////////////////////////////////////////////////////////\n    // 首页相关\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * 首页\n     */\n    @GET(\"api/v4/tabs/selected\")\n    fun getHomeInfo(): Observable<AndyInfo>\n\n    /**\n     * 获取热门关键词\n     */\n    @GET(\"api/v3/queries/hot\")\n    fun getHotWord(): Observable<MutableList<String>>\n\n    /**\n     * 关键词搜索\n     */\n    @GET(\"api/v1/search\")\n    fun searchVideoByWord(@Query(\"query\") word: String): Observable<AndyInfo>\n\n    /**\n     * 每日精选旁的日历显示\n     */\n    @GET(\"api/v3/issueNavigationList\")\n    fun getIssueNaviGationList(): Observable<JenniferInfo>\n\n    ///////////////////////////////////////////////////////////////////////////\n    // 发现相关\n    ///////////////////////////////////////////////////////////////////////////\n    /**\n     * 发现\n     */\n    @GET(\"api/v4/discovery\")\n    fun getDiscoveryTab(): Observable<Tab>\n\n    /**\n     * 获取全部分类信息\n     */\n    @GET(\"api/v4/categories/all\")\n    fun getAllCategoriesInfo(): Observable<AndyInfo>\n\n    /**\n     * 获取排行榜tab信息\n     */\n    @GET(\"api/v4/rankList\")\n    fun getRankListTab(): Observable<Tab>\n\n    /**\n     * 获取专题信息\n     */\n    @GET(\"api/v3/specialTopics\")\n    fun getTopicInfo(): Observable<AndyInfo>\n\n    /**\n     * 获取tag信息\n     * @param tagId tagId\n     * @param strategy tag模式\n     */\n    @GET(\"api/v3/tag/videos\")\n    fun getTagInfo(@Query(\"tagId\") tagId: String, @Query(\"strategy\") strategy: String): Observable<AndyInfo>\n\n    /**\n     * 获取种类下tab详细信息\n     */\n    @GET(\"api/v4/categories/detail/tab\")\n    fun getCategoryTabInfo(@Query(\"id\") id: String): Observable<Category>\n\n    /**\n     * 获取种类下tab下列表集合\n     */\n    @GET(\"api/v4/categories/detail/index\")\n    fun getCategroyTabListItemInfo(@Query(\"id\") id: String): Observable<AndyInfo>\n\n    ///////////////////////////////////////////////////////////////////////////\n    // 关注相关\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * 关注\n     */\n    @GET(\"api/v4/tabs/follow\")\n    fun getFollowInfo(): Observable<AndyInfo>\n\n    /**\n     * 全部作者\n     */\n    @GET(\"api/v4/pgcs/all\")\n    fun getAllAuthor(): Observable<AndyInfo>\n\n    /**\n     * 作者详细信息\n     */\n    @GET(\"api/v4/pgcs/detail/tab\")\n    fun getAuthorTagDetail(@Query(\"id\") id: String): Observable<Tab>\n\n    ///////////////////////////////////////////////////////////////////////////\n    // 公共接口\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * 根据url,获取数据\n     * @url tab请求地址\n     */\n    @GET\n    fun getDataInfoFromUrl(@Url url: String?): Observable<AndyInfo>\n\n    /**\n     * 根据url,获取更多信息\n     * @param url 下一页请求地址\n     */\n    @GET\n    fun getMoreAndyInfo(@Url url: String?): Observable<AndyInfo>\n\n\n    /**\n     * 根据url,获取更多信息\n     * @param url 下一页请求地址\n     */\n    @GET\n    fun getMoreJenniferInfo(@Url url: String?): Observable<JenniferInfo>\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // 视频相关\n    ///////////////////////////////////////////////////////////////////////////\n    /**\n     * 根据视频id,获取相关信息\n     */\n    @GET(\"api/v2/video/{id}\")\n    fun getVideoInfoById(@Path(\"id\") id: String): Observable<ContentBean>\n\n    /**\n     * 获取相关视频信息\n     * @id 视频id\n     */\n    @GET(\"api/v4/video/related\")\n    fun getRelatedVideo(@Query(\"id\") id: String): Observable<AndyInfo>\n\n    /**\n     * 每日编辑精选\n     */\n    @GET(\"api/v2/feed?num=3\")\n    fun getDailyElite(): Observable<JenniferInfo>\n\n\n    /**\n     * 下载视频\n     */\n    @Streaming\n    @GET\n    fun downloadVideo(@Url url: String): Call<ResponseBody>\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/net/Extras.kt",
    "content": "package com.jennifer.andy.simpleeyes.net\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/10/31 14:16\n * Description:\n */\n\nobject Extras {\n\n    /**\n     * 更新参数\n     */\n    const val UPDATE_PARAMS = \"update_params\"\n    const val VIDEO_INFO = \"video_info\"\n    const val VIDEO_INFO_INDEX = \"video_info_index\"\n    const val VIDEO_LIST_INFO = \"video_list_info\"\n    const val SLOGAN_ENGLISH = \"slogan_english\"\n    const val SLOGAN_CHINESE = \"slogan_chinese\"\n    const val API_URL = \"api_url\"\n    const val ID = \"id\"\n    const val TAB_INDEX = \"tab_index\"\n    const val TITLE = \"title\"\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/net/RetrofitConfig.kt",
    "content": "package com.jennifer.andy.simpleeyes.net\n\nimport com.google.gson.GsonBuilder\nimport com.jennifer.andy.simpleeyes.AndyApplication\nimport okhttp3.Cache\nimport okhttp3.Interceptor\nimport okhttp3.OkHttpClient\nimport okhttp3.logging.HttpLoggingInterceptor\nimport retrofit2.Retrofit\nimport retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory\nimport retrofit2.converter.gson.GsonConverterFactory\nimport java.io.File\nimport java.util.concurrent.TimeUnit\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/10/10 22:41\n * Description:\n */\nclass RetrofitConfig private constructor() {\n\n\n    private lateinit var mRetrofit: Retrofit\n    private lateinit var mHttpLoggingInterceptor: HttpLoggingInterceptor\n    private lateinit var mRequestInterceptor: Interceptor\n\n    private lateinit var mOkHttpClient: OkHttpClient\n    private lateinit var mCache: Cache\n\n    companion object {\n\n        private const val READ_TIME_OUT = 5000L// 读取超过时间 单位:毫秒\n        private const val WRITE_TIME_OUT = 5000L//写超过时间 单位:毫秒\n        private const val FILE_CACHE_SIZE = 1024 * 1024 * 100L//缓存大小100Mb\n        private val Instance: RetrofitConfig by lazy { RetrofitConfig() }\n\n        /**\n         * 获取默认请求接口\n         */\n        @JvmStatic\n        fun getDefaultService(): ApiService {\n            return Instance.apiService\n        }\n\n    }\n\n\n    private lateinit var apiService: ApiService\n\n    init {\n        initRequestInterceptor()\n        initLoggingInterceptor()\n        initCachePathAndSize()\n        initOkHttpClient()\n        initRetrofit()\n    }\n\n    /**\n     * 初始化请求拦截，添加缓存头,与全局请求参数\n     */\n    private fun initRequestInterceptor() {\n        mRequestInterceptor = Interceptor { chain ->\n            //注意全局请求参数都是死的\n            val urlBuilder = chain.request().url()\n                    .newBuilder()\n                    .setEncodedQueryParameter(\"udid\", \"d0f6190461864a3a978bdbcb3fe9b48709f1f390\")\n                    .setEncodedQueryParameter(\"vc\", \"225\")\n                    .setEncodedQueryParameter(\"vn\", \"3.12.0\")\n                    .setEncodedQueryParameter(\"deviceModel\", \"Redmi%204\")\n                    .setEncodedQueryParameter(\"first_channel\", \"eyepetizer_xiaomi_market\")\n                    .setEncodedQueryParameter(\"last_channel\", \"eyepetizer_xiaomi_market\")\n                    .setEncodedQueryParameter(\"system_version_code\", \"23\")\n\n            val request = chain.request().newBuilder()\n                    .addHeader(\"Content-Type\", \"application/json\")\n                    .addHeader(\"Cookie\", \"ky_auth=;sdk=23\")\n                    .addHeader(\"model\", \"Android\")\n                    .url(urlBuilder.build())\n                    .build()\n\n            chain.proceed(request)\n        }\n    }\n\n    /**\n     * 初始化日志拦截\n     */\n    private fun initLoggingInterceptor() {\n        mHttpLoggingInterceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)\n    }\n\n    /**\n     * 配置缓存大小与缓存地址\n     */\n    private fun initCachePathAndSize() {\n        val cacheFile = File(AndyApplication.INSTANCE.cacheDir, \"cache\")\n        mCache = Cache(cacheFile, FILE_CACHE_SIZE)\n    }\n\n\n    /**\n     * 配置okHttp\n     */\n    private fun initOkHttpClient() {\n        mOkHttpClient = OkHttpClient.Builder()\n                .readTimeout(READ_TIME_OUT, TimeUnit.MILLISECONDS)\n                .writeTimeout(WRITE_TIME_OUT, TimeUnit.MILLISECONDS)\n                .cache(mCache)\n                .addInterceptor(mRequestInterceptor)\n                .addInterceptor(mHttpLoggingInterceptor)\n                .build()\n    }\n\n    /**\n     * 配置retrofit\n     */\n    private fun initRetrofit() {\n        val gson = GsonBuilder()\n                .setDateFormat(\"yyyy-MM-dd hh:mm:ss\")\n                .create()\n        mRetrofit = Retrofit.Builder()\n                .client(mOkHttpClient)\n                .addConverterFactory(GsonConverterFactory.create(gson))\n                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())\n                .baseUrl(Api.BASE_URL)\n                .build()\n        apiService = mRetrofit.create(ApiService::class.java)\n\n    }\n\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/FileMediaDataSource.kt",
    "content": "package com.jennifer.andy.simpleeyes.player\n\nimport tv.danmaku.ijk.media.player.misc.IMediaDataSource\nimport java.io.File\nimport java.io.IOException\nimport java.io.RandomAccessFile\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/10 13:29\n * Description:\n */\n\nclass FileMediaDataSource(file: File) : IMediaDataSource {\n\n    private var mFile: RandomAccessFile = RandomAccessFile(file, \"r\")\n    var mFileSize = mFile.length()\n\n    @Throws(IOException::class)\n    override fun readAt(position: Long, buffer: ByteArray?, offset: Int, size: Int): Int {\n        if (mFile.filePointer != position) mFile.seek(position)\n        return if (size == 0) 0 else mFile.read(buffer, 0, size)\n    }\n\n    @Throws(IOException::class)\n    override fun getSize(): Long {\n        return mFileSize\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n        mFileSize = 0\n        mFile.close()\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/IjkMediaController.kt",
    "content": "package com.jennifer.andy.simpleeyes.player\n\nimport android.app.Activity\nimport android.content.Context\nimport android.graphics.PixelFormat\nimport android.media.AudioManager\nimport android.util.Log\nimport android.view.*\nimport android.view.View.OnLayoutChangeListener\nimport android.view.View.OnTouchListener\nimport android.widget.FrameLayout\nimport android.widget.MediaController.MediaPlayerControl\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.ContentBean\nimport com.jennifer.andy.simpleeyes.player.event.VideoProgressEvent\nimport com.jennifer.andy.simpleeyes.player.view.ControllerView\nimport com.jennifer.andy.simpleeyes.player.view.ControllerViewFactory\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/10 17:38\n * Description:IjkMediaController 用于视频播放的UI，内部原理是通过window进行展示，其中window的位置与大小\n * 是根据[mAnchor]的位置动态调整的。\n *\n */\n\nclass IjkMediaController(context: Context) : FrameLayout(context) {\n\n    private lateinit var player: MediaPlayerControl\n\n    private lateinit var windowManager: WindowManager\n    private lateinit var window: Window\n    private lateinit var decorLayoutParams: WindowManager.LayoutParams\n    private lateinit var decorView: View\n\n    private lateinit var mAnchor: View//锚点view，用于更新window的位置\n\n    var isShowing = false//当前window是否展示\n\n    var currentVideoInfo: ContentBean? = null\n    var totalCount = 0\n    var currentIndex = 0\n\n    private var showMode1 = ControllerViewFactory.TINY_MODE //当前window展示的类型，默认情况下是小视图\n\n    var controllerListener: ControllerListener? = null\n    var controllerView: ControllerView? = null\n\n    private val mControllerViewFactory = ControllerViewFactory()\n\n    companion object {\n        private const val WINDOW_TIME_OUT = 3500L //默认window消失时间 3.5秒\n    }\n\n    init {\n        initFloatingWindowLayout()\n        initFloatingWindowConfig()\n    }\n\n    /**\n     * 初始化window布局\n     */\n    private fun initFloatingWindowLayout() {\n        decorLayoutParams = WindowManager.LayoutParams().apply {\n            gravity = Gravity.TOP or Gravity.LEFT\n            height = LayoutParams.WRAP_CONTENT\n            x = 0\n            format = PixelFormat.TRANSLUCENT\n            type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL\n            flags = flags or (WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM\n                    or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL\n                    or WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)\n            token = null\n            windowAnimations = 0\n        }\n\n    }\n\n    /**\n     * 初始化window参数\n     */\n    private fun initFloatingWindowConfig() {\n        windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager\n        window = PolicyCompat().createPhoneWindow(context).apply {\n            setWindowManager(windowManager, null, null)\n            requestFeature(Window.FEATURE_NO_TITLE)\n            setContentView(this@IjkMediaController)\n            setBackgroundDrawableResource(R.color.transparent)\n            volumeControlStream = AudioManager.STREAM_MUSIC\n        }\n\n        decorView = window.decorView\n        decorView.setOnTouchListener(mTouchListener)\n        isFocusable = true\n        isFocusableInTouchMode = true\n        descendantFocusability = ViewGroup.FOCUS_AFTER_DESCENDANTS\n        requestFocus()\n    }\n\n\n    /**\n     * 初始化数据\n     *\n     * @param currentIndex     当前视频角标\n     * @param totalCount       视频集合总数\n     * @param currentVideoInfo 当前视频信息\n     */\n    fun initData(currentIndex: Int, totalCount: Int, currentVideoInfo: ContentBean?) {\n        this.currentIndex = currentIndex\n        this.totalCount = totalCount\n        this.currentVideoInfo = currentVideoInfo\n    }\n\n    /**\n     * 当锚点布局发生改变时，动态更新window的位置，高度与宽度\n     */\n    private fun updateFloatingWindowLayout() {\n        val anchorPos = IntArray(2)\n        mAnchor.getLocationOnScreen(anchorPos)\n\n        //重新测量\n        decorView.measure(MeasureSpec.makeMeasureSpec(mAnchor.width, MeasureSpec.AT_MOST),\n                MeasureSpec.makeMeasureSpec(mAnchor.height, MeasureSpec.AT_MOST))\n\n        decorLayoutParams.apply {\n            width = mAnchor.width\n            x = anchorPos[0]\n            y = anchorPos[1]\n            height = mAnchor.height\n        }\n\n    }\n\n    fun setMediaPlayer(player: MediaPlayerControl) {\n        this.player = player\n        switchControllerView(showMode1)\n    }\n\n    /**\n     * 当锚点view布局发生改变的时,重新设置当前window的布局参数\n     */\n    private val mLayoutChangeListener = OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->\n        updateFloatingWindowLayout()\n        if (isShowing) windowManager.updateViewLayout(decorView, decorLayoutParams)\n\n    }\n\n    /**\n     * 将控制层view与锚点的关联\n     */\n    fun setAnchorView(view: View) {\n        mAnchor = view\n        mAnchor.addOnLayoutChangeListener(mLayoutChangeListener)\n        updateFloatingWindowLayout()\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // Window显示相关\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * 触摸监听，如果当前window正在显示，再次按下走默认时间内该window会消失\n     */\n    private val mTouchListener = OnTouchListener { _, event ->\n        if (event.action == MotionEvent.ACTION_DOWN) {\n            if (isShowing) {\n                removeCallbacks(mFadeOut)\n                postDelayed(mFadeOut, WINDOW_TIME_OUT)\n            }\n        }\n        false\n    }\n\n    /**\n     * 隐藏控制层view\n     */\n    private val mFadeOut = Runnable {\n        if (isShowing) hide()\n    }\n\n    /**\n     * 第一次显示，不显示控制层\n     */\n    fun firstShow() {\n        if (!isShowing && controllerView != null) {\n            controllerView?.display()\n        }\n    }\n\n    /**\n     * 将控制器显示在屏幕上，当到达过期时间时会自动消失。\n     *\n     * @param timeout 过期时间(毫秒) 如果设置为0，那么会直到调用hide方法才会消失\n     */\n    @JvmOverloads\n    fun show(timeout: Long = WINDOW_TIME_OUT) {\n        if (!isShowing) {\n            windowManager.addView(decorView, decorLayoutParams)\n            isShowing = true\n        }\n        if (timeout != 0L) {\n            removeCallbacks(mFadeOut)\n            postDelayed(mFadeOut, timeout.toLong())\n        }\n        controllerView?.display()\n        controllerListener?.onShowController(true)\n\n    }\n\n    /**\n     * 隐藏window\n     */\n    fun hide() {\n        try {\n            removeCallbacks(mFadeOut)\n            isShowing = false\n            controllerListener?.onShowController(false)\n            windowManager.removeView(decorView)\n        } catch (ex: IllegalArgumentException) {\n            Log.w(\"MediaController\", \"already removed\")\n        }\n    }\n\n    /**\n     * 一直显示window\n     */\n    fun showAllTheTime() {\n        show(3600000)\n        removeCallbacks(mFadeOut)\n    }\n\n    override fun dispatchKeyEvent(event: KeyEvent): Boolean {\n        val keyCode = event.keyCode\n        val uniqueDown = (event.repeatCount == 0 && event.action == KeyEvent.ACTION_DOWN)\n        if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {\n            if (uniqueDown) {\n                hide()\n                //如果传入的是activity直接退出\n                if (context is Activity) (context as Activity).finish()\n\n            }\n            return true\n        }\n        return super.dispatchKeyEvent(event)\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // Window显示内容相关\n    ///////////////////////////////////////////////////////////////////////////\n\n\n    /**\n     * 根据显示模式切换展示的控制UI\n     *\n     * @param showMode\n     */\n    fun switchControllerView(showMode: Int) {\n        showMode1 = showMode\n        removeAllViews()\n        controllerView = mControllerViewFactory.create(showMode, context)\n        controllerView?.initControllerAndData(player, this, currentVideoInfo)\n        controllerView?.display()\n        addView(controllerView)\n    }\n\n    /**\n     * 显示错误布局\n     */\n    fun showErrorView() {\n        if (!isShowing) {\n            controllerView?.showErrorView()\n            windowManager.addView(decorView, decorLayoutParams)\n            isShowing = true\n        }\n    }\n\n    fun updateProgressAndTime(videoProgressEvent: VideoProgressEvent?) {\n        controllerView?.updateProgressAndTime(videoProgressEvent!!)\n    }\n\n    fun resetType() {\n        controllerView?.showContent()\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // 数据相关\n    ///////////////////////////////////////////////////////////////////////////\n\n\n    /**\n     * 判断是否拥有上一个视频\n     */\n    fun isHavePreVideo() = totalCount > 0 && currentIndex > 0\n\n\n    /**\n     * 是否拥有下一个视频\n     */\n    fun isHaveNextVideo() = totalCount > 0 && currentIndex < totalCount - 1\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // 回调监听\n    ///////////////////////////////////////////////////////////////////////////\n\n    interface ControllerListener {\n        //退出点击\n        fun onBackClick()\n\n        //上一页点击\n        fun onPreClick()\n\n        //下一页点击\n        fun onNextClick()\n\n        //全屏点击\n        fun onFullScreenClick()\n\n        //退出全屏\n        fun onTinyScreenClick()\n\n        //错误界面点击\n        fun onErrorViewClick()\n\n        //是否显示控制层\n        fun onShowController(isShowController: Boolean)\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/IjkVideoView.kt",
    "content": "package com.jennifer.andy.simpleeyes.player\n\nimport android.annotation.TargetApi\nimport android.content.Context\nimport android.graphics.Color\nimport android.graphics.drawable.ColorDrawable\nimport android.media.AudioManager\nimport android.media.MediaPlayer\nimport android.net.Uri\nimport android.os.Build\nimport android.text.TextUtils\nimport android.util.AttributeSet\nimport android.util.Log\nimport android.view.Gravity\nimport android.view.KeyEvent\nimport android.view.MotionEvent\nimport android.view.View\nimport android.widget.FrameLayout\nimport android.widget.MediaController.MediaPlayerControl\nimport com.jennifer.andy.simpleeyes.player.event.VideoProgressEvent\nimport com.jennifer.andy.simpleeyes.player.render.IRenderView\nimport com.jennifer.andy.simpleeyes.player.render.IRenderView.IRenderCallback\nimport com.jennifer.andy.simpleeyes.player.render.IRenderView.ISurfaceHolder\nimport com.jennifer.andy.simpleeyes.player.render.SurfaceRenderView\nimport com.jennifer.andy.simpleeyes.player.render.TextureRenderView\nimport com.jennifer.andy.simpleeyes.player.view.ControllerViewFactory\nimport com.jennifer.andy.simpleeyes.rx.RxBus\nimport io.reactivex.Observable\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactivex.disposables.Disposable\nimport tv.danmaku.ijk.media.player.IMediaPlayer\nimport tv.danmaku.ijk.media.player.IjkMediaPlayer\nimport tv.danmaku.ijk.media.player.misc.IMediaDataSource\nimport java.io.File\nimport java.io.IOException\nimport java.util.*\nimport java.util.concurrent.TimeUnit\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/12 23:03\n * Description:\n */\n\nclass IjkVideoView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)\n    : FrameLayout(context, attrs, defStyleAttr), MediaPlayerControl {\n\n    private var mUri: Uri? = null\n    private var mHeaders: Map<String, String>? = null\n\n    private var mSurfaceHolder: ISurfaceHolder? = null\n    private var mMediaPlayer: IMediaPlayer? = null\n    private var mVideoWidth = 0\n    private var mVideoHeight = 0\n    private var mSurfaceWidth = 0\n    private var mSurfaceHeight = 0\n\n    private var mRenderView: IRenderView? = null\n    private var mCurrentRender = RENDER_NONE\n    private var mRenderUIView: View? = null\n\n    private var mVideoRotationDegree = 0\n    private var mVideoSarNum = 0\n    private var mVideoSarDen = 0\n    private var mCurrentAspectRatio = allAspectRatio[0]\n    private var mMediaController: IjkMediaController? = null\n\n\n    private var mOnCompletionListener: IMediaPlayer.OnCompletionListener? = null\n    private var mOnPreparedListener: IMediaPlayer.OnPreparedListener? = null\n    private var mOnErrorListener: IMediaPlayer.OnErrorListener? = null\n    private var mOnInfoListener: IMediaPlayer.OnInfoListener? = null\n\n    private var mCurrentBufferPercentage = 0\n    private var mSeekWhenPrepared = 0\n\n    private val mCanPause = true\n    private val mCanSeekBack = true\n    private val mCanSeekForward = true\n\n    private lateinit var mAppContext: Context\n\n\n    private var mCurrentState = STATE_IDLE\n    private var mTargetState = STATE_IDLE\n    var screenState = ControllerViewFactory.TINY_MODE\n\n\n    private val mSHCallback: IRenderCallback = object : IRenderCallback {\n        override fun onSurfaceChanged(holder: ISurfaceHolder, format: Int, w: Int, h: Int) {\n            if (holder.renderView !== mRenderView) {\n                Log.e(TAG, \"onSurfaceChanged: unmatched render callback\\n\")\n                return\n            }\n            mSurfaceWidth = w\n            mSurfaceHeight = h\n            val isValidState = mTargetState == STATE_PLAYING\n            val hasValidSize = !mRenderView!!.shouldWaitForResize() || mVideoWidth == w && mVideoHeight == h\n            if (mMediaPlayer != null && isValidState && hasValidSize) {\n                if (mSeekWhenPrepared != 0) {\n                    seekTo(mSeekWhenPrepared)\n                }\n                start()\n            }\n        }\n\n        override fun onSurfaceCreated(holder: ISurfaceHolder, width: Int, height: Int) {\n            if (holder.renderView !== mRenderView) {\n                Log.e(TAG, \"onSurfaceCreated: unmatched render callback\\n\")\n                return\n            }\n            mSurfaceHolder = holder\n            if (mMediaPlayer != null) bindSurfaceHolder(mMediaPlayer, holder) else openVideo()\n        }\n\n        override fun onSurfaceDestroyed(holder: ISurfaceHolder) {\n            if (holder.renderView !== mRenderView) {\n                Log.e(TAG, \"onSurfaceDestroyed: unmatched render callback\\n\")\n                return\n            }\n            // after we return from this we can't use the surface any more\n            mSurfaceHolder = null\n            // REMOVED: if (mMediaController != null) mMediaController.hide();\n            // REMOVED: release(true);\n            releaseWithoutStop()\n        }\n    }\n\n    companion object {\n\n        private const val STATE_ERROR = -1\n        private const val STATE_IDLE = 0\n        private const val STATE_PREPARING = 1\n        private const val STATE_PREPARED = 2\n        private const val STATE_PLAYING = 3\n        private const val STATE_PAUSED = 4\n        private const val STATE_PLAYBACK_COMPLETED = 5\n\n        private val allAspectRatio = intArrayOf(\n                IRenderView.AR_ASPECT_FIT_PARENT,\n                IRenderView.AR_ASPECT_FILL_PARENT,\n                IRenderView.AR_ASPECT_WRAP_CONTENT,\n                IRenderView.AR_16_9_FIT_PARENT,\n                IRenderView.AR_4_3_FIT_PARENT)\n        const val RENDER_NONE = 0\n        const val RENDER_SURFACE_VIEW = 1\n        const val RENDER_TEXTURE_VIEW = 2\n        private const val TAG = \"IjkVideoView\"\n    }\n\n    init {\n        initVideoView(context)\n    }\n\n    fun toggleAspectRatio(currentAspectRatio: Int): Int {\n        mRenderView?.setAspectRatio(currentAspectRatio)\n        mCurrentAspectRatio = currentAspectRatio\n        return mCurrentAspectRatio\n    }\n\n    private fun initVideoView(context: Context) {\n        mAppContext = context.applicationContext\n        initRenders()\n        //初始化宽高\n        mVideoWidth = 0\n        mVideoHeight = 0\n        //获取焦点\n        isFocusable = true\n        isFocusableInTouchMode = true\n        requestFocus()\n        //初始化状态\n        mCurrentState = STATE_IDLE\n        mTargetState = STATE_IDLE\n        //设置背景\n        background = ColorDrawable(Color.BLACK)\n    }\n\n    /**\n     * 设置渲染Render\n     * 4.0 及以上版本使用 TextureView\n     * 4.0 版本以下使用 SurfaceView\n     */\n    private fun initRenders() {\n        mCurrentRender = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { //4.0以上\n            RENDER_TEXTURE_VIEW\n        } else {\n            RENDER_SURFACE_VIEW\n        }\n        setRender(mCurrentRender)\n    }\n\n    /**\n     * 设置渲染Render\n     */\n    private fun setRender(render: Int) {\n        when (render) {\n            RENDER_NONE -> setRenderView(null)\n            RENDER_TEXTURE_VIEW -> {\n                //TextureView\n                val renderView = TextureRenderView(context)\n                if (mMediaPlayer != null) {\n                    renderView.getSurfaceHolder().bindToMediaPlayer(mMediaPlayer)\n                    renderView.setVideoSize(mMediaPlayer!!.videoWidth, mMediaPlayer!!.videoHeight)\n                    renderView.setVideoSampleAspectRatio(mMediaPlayer!!.videoSarNum, mMediaPlayer!!.videoSarDen)\n                    renderView.setAspectRatio(mCurrentAspectRatio)\n                }\n                setRenderView(renderView)\n            }\n            RENDER_SURFACE_VIEW -> {\n                //surfaceView\n                val renderView = SurfaceRenderView(context)\n                setRenderView(renderView)\n            }\n            else -> Log.e(TAG, String.format(Locale.getDefault(), \"invalid render %d\\n\", render))\n        }\n    }\n\n\n    /**\n     * 设置渲染界面，初始化当前的渲染界面参数，并设置渲染回调等初始化操作\n     */\n    private fun setRenderView(renderView: IRenderView?) {\n        if (mRenderView != null) {\n            if (mMediaPlayer != null)\n                mMediaPlayer?.setDisplay(null)\n            val renderUIView = mRenderView?.getView()\n            mRenderView?.removeRenderCallback(mSHCallback)\n            mRenderView = null\n            removeView(renderUIView)\n        }\n        if (renderView == null) return\n        mRenderView = renderView\n        renderView.setAspectRatio(mCurrentAspectRatio) //设置适配比例\n        if (mVideoWidth > 0 && mVideoHeight > 0)\n            renderView.setVideoSize(mVideoWidth, mVideoHeight) //设置视频的宽高\n        if (mVideoSarNum > 0 && mVideoSarDen > 0) //设置采样比例\n            renderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen)\n\n        mRenderUIView = mRenderView?.getView().apply {\n            layoutParams = LayoutParams(\n                    LayoutParams.WRAP_CONTENT,\n                    LayoutParams.WRAP_CONTENT,\n                    Gravity.CENTER)\n        }\n        addView(mRenderUIView)\n        mRenderView?.addRenderCallback(mSHCallback) //添加回调\n        mRenderView?.setVideoRotation(mVideoRotationDegree) //设置视频旋转\n    }\n\n    /**\n     * 设置视频播放路径\n     *\n     * @param path 视频路径\n     */\n    fun setVideoPath(path: String?) {\n        setVideoURI(Uri.parse(path))\n    }\n\n    /**\n     * 设置视频URI\n     *\n     * @param uri 视频URI\n     */\n    fun setVideoURI(uri: Uri) {\n        setVideoURI(uri, null)\n    }\n\n    /**\n     * 设置带header 的 视频URI\n     *\n     *\n     * 注意，默认情况下允许跨域重定向，\n     * 但可以通过以“android allow cross domain redirect”为键,\n     * 以“0”或“1”为值的headers，以禁止或允许跨域重定向。\n     *\n     * @param uri     视频URI.\n     * @param headers URI请求需要的header\n     */\n    private fun setVideoURI(uri: Uri, headers: Map<String, String>?) {\n        mUri = uri\n        mHeaders = headers\n        mSeekWhenPrepared = 0\n        openVideo()\n        requestLayout()\n        invalidate()\n    }\n\n    fun stopPlayback() {\n        mMediaPlayer?.let {\n            mMediaPlayer?.stop()\n            mMediaPlayer?.release()\n            mMediaPlayer = null\n            mCurrentState = STATE_IDLE\n            mTargetState = STATE_IDLE\n\n            mMediaController?.let {\n                mMediaController?.hide()\n                mMediaController = null\n            }\n\n            val am = mAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager\n            am.abandonAudioFocus(null)\n            cancelProgressRunnable()\n        }\n    }\n\n    /**\n     * 打开视频\n     */\n    @TargetApi(Build.VERSION_CODES.M)\n    private fun openVideo() {\n        if (mUri == null || mSurfaceHolder == null) { // not ready for playback just yet, will try again later\n            return\n        }\n        // we shouldn't clear the target state, because somebody might have\n        // called start() previously\n        release(false)\n        val am = mAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager\n        am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)\n        try {\n            mMediaPlayer = createPlayer()\n            mMediaPlayer?.setOnPreparedListener(mPreparedListener)\n            mMediaPlayer?.setOnVideoSizeChangedListener(mSizeChangedListener)\n            mMediaPlayer?.setOnCompletionListener(mCompletionListener)\n            mMediaPlayer?.setOnErrorListener(mErrorListener)\n            mMediaPlayer?.setOnInfoListener(mInfoListener)\n            mMediaPlayer?.setOnBufferingUpdateListener(mBufferingUpdateListener)\n            mMediaPlayer?.setOnSeekCompleteListener(mSeekCompleteListener)\n            mCurrentBufferPercentage = 0\n            val scheme = mUri?.scheme\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && (TextUtils.isEmpty(scheme)\n                            || scheme.equals(\"file\", ignoreCase = true))) {\n                val dataSource: IMediaDataSource = FileMediaDataSource(File(mUri.toString()))\n                mMediaPlayer?.setDataSource(dataSource)\n            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n                mMediaPlayer?.setDataSource(mAppContext, mUri, mHeaders)\n            } else {\n                mMediaPlayer?.dataSource = mUri.toString()\n            }\n            bindSurfaceHolder(mMediaPlayer, mSurfaceHolder)\n            mMediaPlayer?.setAudioStreamType(AudioManager.STREAM_MUSIC)\n            mMediaPlayer?.setScreenOnWhilePlaying(true)\n            mMediaPlayer?.prepareAsync()\n            mCurrentState = STATE_PREPARING\n            attachMediaController()\n        } catch (ex: IOException) {\n            Log.w(TAG, \"Unable to open content: $mUri\", ex)\n            mCurrentState = STATE_ERROR\n            mTargetState = STATE_ERROR\n            mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0)\n        } catch (ex: IllegalArgumentException) {\n            Log.w(TAG, \"Unable to open content: $mUri\", ex)\n            mCurrentState = STATE_ERROR\n            mTargetState = STATE_ERROR\n            mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0)\n        } finally { // REMOVED: mPendingSubtitleTracks.clear();\n        }\n    }\n\n    fun setMediaController(controller: IjkMediaController?) {\n        mMediaController = controller\n        attachMediaController()\n    }\n\n    fun getMediaController(): IjkMediaController? {\n        return mMediaController\n    }\n\n    private fun attachMediaController() {\n        if (mMediaPlayer != null && mMediaController != null) {\n            mMediaController?.setMediaPlayer(this)\n            val anchorView = if (this.parent is View) this.parent as View else this\n            mMediaController?.setAnchorView(anchorView)\n            mMediaController?.isEnabled = isInPlaybackState()\n        }\n    }\n\n    var mSizeChangedListener = IMediaPlayer.OnVideoSizeChangedListener { mp, _, _, _, _ ->\n        mVideoWidth = mp.videoWidth\n        mVideoHeight = mp.videoHeight\n        mVideoSarNum = mp.videoSarNum\n        mVideoSarDen = mp.videoSarDen\n        if (mVideoWidth != 0 && mVideoHeight != 0) {\n            mRenderView?.let {\n                mRenderView?.setVideoSize(mVideoWidth, mVideoHeight)\n                mRenderView?.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen)\n            }\n            // REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight);\n            requestLayout()\n        }\n    }\n    /**\n     * 视频播放准备完成监听\n     */\n    var mPreparedListener = IMediaPlayer.OnPreparedListener { mp ->\n        mCurrentState = STATE_PREPARED\n        if (mOnPreparedListener != null) {\n            mOnPreparedListener!!.onPrepared(mMediaPlayer)\n        }\n        if (mMediaController != null) {\n            mMediaController!!.isEnabled = true\n        }\n        mVideoWidth = mp.videoWidth\n        mVideoHeight = mp.videoHeight\n        val seekToPosition = mSeekWhenPrepared // mSeekWhenPrepared may be changed after seekTo() call\n        if (seekToPosition != 0) {\n            seekTo(seekToPosition)\n        }\n        if (mVideoWidth != 0 && mVideoHeight != 0) {\n            mRenderView?.let {\n                mRenderView?.setVideoSize(mVideoWidth, mVideoHeight)\n                mRenderView?.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen)\n                if (!mRenderView!!.shouldWaitForResize() || mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {\n                    // We didn't actually change the size (it was already at the size\n                    // we need), so we won't get a \"surface changed\" callback, so\n                    // start the video here instead of in the callback.\n                    if (mTargetState == STATE_PLAYING) {\n                        start()\n                        mMediaController?.firstShow()\n                    } else if (!isPlaying && (seekToPosition != 0 || currentPosition > 0)) {\n                        // Show the media controls when we're paused into a video and make 'em stick.\n                        mMediaController?.show(0)\n                    }\n                }\n            }\n        } else { // We don't know the video size yet, but should start anyway.\n            // The video size might be reported to us later.\n            if (mTargetState == STATE_PLAYING) {\n                start()\n            }\n        }\n    }\n    private val mCompletionListener = IMediaPlayer.OnCompletionListener {\n        mCurrentState = STATE_PLAYBACK_COMPLETED\n        mTargetState = STATE_PLAYBACK_COMPLETED\n        mMediaController?.hide()\n        mOnCompletionListener?.onCompletion(mMediaPlayer)\n    }\n    private val mInfoListener = IMediaPlayer.OnInfoListener { mp, arg1, arg2 ->\n        mOnInfoListener?.onInfo(mp, arg1, arg2)\n        when (arg1) {\n            IMediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING -> Log.d(TAG, \"MEDIA_INFO_VIDEO_TRACK_LAGGING:\")\n            IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START -> Log.d(TAG, \"MEDIA_INFO_VIDEO_RENDERING_START:\")\n            IMediaPlayer.MEDIA_INFO_BUFFERING_START -> Log.d(TAG, \"MEDIA_INFO_BUFFERING_START:\")\n            IMediaPlayer.MEDIA_INFO_BUFFERING_END -> Log.d(TAG, \"MEDIA_INFO_BUFFERING_END:\")\n            IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH -> Log.d(TAG, \"MEDIA_INFO_NETWORK_BANDWIDTH: $arg2\")\n            IMediaPlayer.MEDIA_INFO_BAD_INTERLEAVING -> Log.d(TAG, \"MEDIA_INFO_BAD_INTERLEAVING:\")\n            IMediaPlayer.MEDIA_INFO_NOT_SEEKABLE -> Log.d(TAG, \"MEDIA_INFO_NOT_SEEKABLE:\")\n            IMediaPlayer.MEDIA_INFO_METADATA_UPDATE -> Log.d(TAG, \"MEDIA_INFO_METADATA_UPDATE:\")\n            IMediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE -> Log.d(TAG, \"MEDIA_INFO_UNSUPPORTED_SUBTITLE:\")\n            IMediaPlayer.MEDIA_INFO_SUBTITLE_TIMED_OUT -> Log.d(TAG, \"MEDIA_INFO_SUBTITLE_TIMED_OUT:\")\n            IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED -> {\n                mVideoRotationDegree = arg2\n                Log.d(TAG, \"MEDIA_INFO_VIDEO_ROTATION_CHANGED: $arg2\")\n                if (mRenderView != null) mRenderView!!.setVideoRotation(arg2)\n            }\n            IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START -> Log.d(TAG, \"MEDIA_INFO_AUDIO_RENDERING_START:\")\n        }\n        true\n    }\n    private val mErrorListener = IMediaPlayer.OnErrorListener { mp, framework_err, impl_err ->\n        Log.d(TAG, \"Error: $framework_err,$impl_err\")\n        mCurrentState = STATE_ERROR\n        mTargetState = STATE_ERROR\n        /* If an error handler has been supplied, use it and finish. */if (mOnErrorListener != null) {\n        if (mOnErrorListener!!.onError(mMediaPlayer, framework_err, impl_err)) {\n            return@OnErrorListener true\n        }\n    }\n        true\n    }\n    private val mBufferingUpdateListener = IMediaPlayer.OnBufferingUpdateListener { _, percent ->\n        mCurrentBufferPercentage = percent\n    }\n    private val mSeekCompleteListener = IMediaPlayer.OnSeekCompleteListener { }\n\n    /**\n     * Register a callback to be invoked when the media file\n     * is loaded and ready to go.\n     *\n     * @param l The callback that will be run\n     */\n    fun setOnPreparedListener(l: IMediaPlayer.OnPreparedListener?) {\n        mOnPreparedListener = l\n    }\n\n    /**\n     * Register a callback to be invoked when the end of a media file\n     * has been reached during playback.\n     *\n     * @param l The callback that will be run\n     */\n    fun setOnCompletionListener(l: IMediaPlayer.OnCompletionListener?) {\n        mOnCompletionListener = l\n    }\n\n    /**\n     * Register a callback to be invoked when an error occurs\n     * during playback or setup.  If no listener is specified,\n     * or if the listener returned false, VideoView will inform\n     * the user of any errors.\n     *\n     * @param l The callback that will be run\n     */\n    fun setOnErrorListener(l: IMediaPlayer.OnErrorListener?) {\n        mOnErrorListener = l\n    }\n\n    /**\n     * Register a callback to be invoked when an informational event\n     * occurs during playback or setup.\n     *\n     * @param l The callback that will be run\n     */\n    fun setOnInfoListener(l: IMediaPlayer.OnInfoListener?) {\n        mOnInfoListener = l\n    }\n\n    // REMOVED: mSHCallback\n    private fun bindSurfaceHolder(mp: IMediaPlayer?, holder: ISurfaceHolder?) {\n        if (mp == null) return\n        if (holder == null) {\n            mp.setDisplay(null)\n            return\n        }\n        holder.bindToMediaPlayer(mp)\n    }\n\n    fun releaseWithoutStop() {\n        if (mMediaPlayer != null) mMediaPlayer!!.setDisplay(null)\n    }\n\n    /*\n     * release the media player in any state\n     */\n    fun release(clearTargetState: Boolean) {\n        if (mMediaPlayer != null) {\n            mMediaPlayer!!.reset()\n            mMediaPlayer!!.release()\n            mMediaPlayer = null\n            mMediaController!!.hide()\n            if (clearTargetState) {\n                mTargetState = STATE_IDLE\n            }\n            setRenderView(null)\n            initRenders()\n            val am = mAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager\n            am.abandonAudioFocus(null)\n            cancelProgressRunnable()\n        }\n    }\n\n    override fun onTrackballEvent(ev: MotionEvent?): Boolean {\n        if (isInPlaybackState() && mMediaController != null) {\n            toggleMediaControlsVisible()\n        }\n        return false\n    }\n\n    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {\n        val isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK && keyCode != KeyEvent.KEYCODE_VOLUME_UP && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_VOLUME_MUTE && keyCode != KeyEvent.KEYCODE_MENU && keyCode != KeyEvent.KEYCODE_CALL && keyCode != KeyEvent.KEYCODE_ENDCALL\n        if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {\n            if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||\n                    keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {\n                if (mMediaPlayer!!.isPlaying) {\n                    pause()\n                    mMediaController!!.show()\n                } else {\n                    start()\n                    mMediaController!!.hide()\n                }\n                return true\n            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {\n                if (!mMediaPlayer!!.isPlaying) {\n                    start()\n                    mMediaController!!.hide()\n                }\n                return true\n            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP\n                    || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {\n                if (mMediaPlayer!!.isPlaying) {\n                    pause()\n                    mMediaController!!.show()\n                }\n                return true\n            } else {\n                toggleMediaControlsVisible()\n            }\n        }\n        return super.onKeyDown(keyCode, event)\n    }\n\n    fun toggleMediaControlsVisible() {\n\n        if (mMediaController!!.isShowing) {\n            mMediaController?.hide()\n        } else {\n            mMediaController?.show()\n        }\n    }\n\n    private var mDisposable: Disposable? = null\n\n\n    /**\n     * 进度与时间更新线程\n     */\n    private fun startProgressRunnable() {\n        if (mDisposable == null || mDisposable!!.isDisposed) {\n            mDisposable = Observable.interval(1, TimeUnit.SECONDS)\n                    .observeOn(AndroidSchedulers.mainThread())\n                    .subscribe {\n                        val position = currentPosition\n                        val duration = duration\n                        if (duration >= 0 && bufferPercentage > 0) {\n                            val progress = 1000L * position / duration\n                            val secondProgress = bufferPercentage * 10\n                            //发送进度\n                            val event = VideoProgressEvent(duration, position, progress.toInt(), secondProgress)\n                            //这里在更新进度条时，有可能在切换控制器，所以这里要排除空的情况\n                            if (mMediaController != null) {\n                                mMediaController!!.updateProgressAndTime(event)\n                            }\n                            RxBus.post(event)\n                        }\n                    }\n        }\n    }\n\n    /**\n     * 取消进度与时间更新线程\n     */\n    private fun cancelProgressRunnable() {\n        if (mDisposable != null && !mDisposable!!.isDisposed) {\n            mDisposable?.dispose()\n            mDisposable = null\n        }\n    }\n\n    override fun start() {\n        if (isInPlaybackState()) {\n            mMediaPlayer?.start()\n            mCurrentState = STATE_PLAYING\n            startProgressRunnable()\n        }\n        mTargetState = STATE_PLAYING\n    }\n\n    override fun pause() {\n        if (isInPlaybackState()) {\n            if (mMediaPlayer!!.isPlaying) {\n                mMediaPlayer?.pause()\n                mCurrentState = STATE_PAUSED\n                cancelProgressRunnable()\n            }\n        }\n        mTargetState = STATE_PAUSED\n    }\n\n    fun suspend() {\n        release(false)\n        cancelProgressRunnable()\n    }\n\n    fun resume() {\n        openVideo()\n    }\n\n    override fun getDuration(): Int {\n        return if (isInPlaybackState()) {\n            mMediaPlayer?.duration!!.toInt()\n        } else -1\n    }\n\n    override fun getCurrentPosition(): Int {\n        return if (isInPlaybackState()) {\n            mMediaPlayer?.currentPosition!!.toInt()\n        } else 0\n    }\n\n    override fun seekTo(msec: Int) {\n        mSeekWhenPrepared = if (isInPlaybackState()) {\n            mMediaPlayer?.seekTo(msec.toLong())\n            0\n        } else {\n            msec\n        }\n    }\n\n    override fun isPlaying(): Boolean {\n        return isInPlaybackState() && mMediaPlayer!!.isPlaying\n    }\n\n    override fun getBufferPercentage(): Int {\n        return if (mMediaPlayer != null) {\n            mCurrentBufferPercentage\n        } else 0\n    }\n\n    fun isInPlaybackState(): Boolean {\n        return mMediaPlayer != null\n                && mCurrentState != STATE_ERROR\n                && mCurrentState != STATE_IDLE\n                && mCurrentState != STATE_PREPARING\n    }\n\n    override fun canPause() = mCanPause\n\n\n    override fun canSeekBackward() = mCanSeekBack\n\n    override fun canSeekForward() = mCanSeekForward\n\n    override fun getAudioSessionId(): Int {\n        return 0\n    }\n\n    private fun createPlayer(): IMediaPlayer? {\n        if (mUri != null) {\n            return IjkMediaPlayer().apply {\n                IjkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG)\n                setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, \"mediacodec\", 0)\n                setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, \"opensles\", 0)\n                setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, \"overlay-format\", IjkMediaPlayer.SDL_FCC_RV32.toLong())\n                setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, \"framedrop\", 1)\n                setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, \"start-on-prepared\", 0)\n                setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, \"http-detect-range-support\", 0)\n                setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, \"skip_loop_filter\", 48)\n            }\n        }\n        return null\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/IjkVideoViewWrapper.kt",
    "content": "package com.jennifer.andy.simpleeyes.player\n\nimport android.app.Dialog\nimport android.content.Context\nimport android.content.pm.ActivityInfo\nimport android.graphics.drawable.ColorDrawable\nimport android.media.AudioManager\nimport android.provider.Settings\nimport android.provider.Settings.SettingNotFoundException\nimport android.util.AttributeSet\nimport android.view.*\nimport android.widget.FrameLayout\nimport android.widget.ProgressBar\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.player.view.ControllerViewFactory\nimport com.jennifer.andy.simpleeyes.utils.*\nimport tv.danmaku.ijk.media.player.IMediaPlayer\nimport kotlin.math.abs\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/14 13:53\n * Description: IjkVideoViewWrapper 包含了对声音亮度的控制，同时代理了[IjkVideoView]的相关方法\n */\n\nclass IjkVideoViewWrapper @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)\n    : FrameLayout(context, attrs, defStyleAttr), GestureDetector.OnGestureListener {\n\n    private val mIjkVideoView: IjkVideoView\n\n    private val mPlaceImage: SimpleDraweeView\n    private val mCenterProgress: ProgressBar\n    private val mGestureDetector: GestureDetector\n    private val mAudioManager: AudioManager\n\n    private var mLightDialog: Dialog? = null\n    private var mLightProgress: ProgressBar? = null\n    private var mVolumeDialog: Dialog? = null\n    private var mVolumeProgress: ProgressBar? = null\n\n    private var isShowVolume = false\n    private var isShowLight = false\n    private var isShowPosition = false\n\n    private val mScreenHeight: Int\n    private var mParent: ViewGroup? = null\n\n\n    companion object {\n        private const val MIN_SCROLL = 3\n    }\n\n    init {\n        LayoutInflater.from(context).inflate(R.layout.layout_ijk_wrapper, this, true)\n        mGestureDetector = GestureDetector(context, this)\n        mAudioManager = getContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager\n        mScreenHeight = getContext().getScreenHeight()\n\n        mIjkVideoView = findViewById(R.id.video_view)\n        mPlaceImage = findViewById(R.id.iv_place_image)\n        mCenterProgress = findViewById(R.id.progress)\n    }\n\n    fun setPlaceImageUrl(url: String?) {\n        togglePlaceImage(true)\n        mPlaceImage.setImageURI(url)\n    }\n\n    fun togglePlaceImage(visibility: Boolean) {\n        mPlaceImage.visibility = if (visibility) View.VISIBLE else View.GONE\n        mCenterProgress.visibility = if (visibility) View.VISIBLE else View.GONE\n    }\n\n    fun hidePlaceImage() {\n        mPlaceImage.handler.postDelayed({ togglePlaceImage(false) }, 500)\n    }\n\n    override fun onTouchEvent(event: MotionEvent): Boolean {\n        val relValue = mGestureDetector.onTouchEvent(event)\n        val action = event.action\n        if (action == MotionEvent.ACTION_UP) { //当手指抬起的时候\n            onUp()\n        } else if (action == MotionEvent.ACTION_CANCEL) {\n            onCancel()\n        }\n        return relValue\n    }\n\n    override fun onDown(e: MotionEvent?): Boolean {\n        // 单击，触摸屏按下时立刻触发\n        return true\n    }\n\n    override fun onShowPress(e: MotionEvent?) {\n        // 短按，触摸屏按下后片刻后抬起，会触发这个手势，如果迅速抬起则不会\n    }\n\n    override fun onSingleTapUp(e: MotionEvent?): Boolean {\n        // 抬起，手指离开触摸屏时触发(长按、滚动、滑动时，不会触发这个手势)\n        if (mIjkVideoView.isInPlaybackState() && mIjkVideoView.getMediaController() != null && (!isShowVolume || !isShowLight || isShowPosition)) {\n            mIjkVideoView.toggleMediaControlsVisible()\n        }\n        return true\n    }\n\n    // 滚动，触摸屏按下后移动\n    override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {\n        if (mIjkVideoView.screenState == ControllerViewFactory.FULL_SCREEN_MODE) { //判断是否是全屏\n            val downX = e1.x\n            val actuallyDy = e2.y - e1.y\n            val actuallyDx = e2.x - e1.x\n            val absDy = abs(actuallyDy)\n            val absDx = abs(actuallyDx)\n            //左右移动\n            if (absDx >= MIN_SCROLL && absDx > absDy) {\n                showMoveToPositionDialog()\n            }\n            //上下移动\n            if (abs(distanceY) >= MIN_SCROLL && absDy > absDx) {\n                if (downX <= mScreenHeight * 0.5f) { //左边，改变声音\n                    showVolumeDialog(distanceY)\n                } else { //右边改变亮度\n                    showLightDialog(distanceY)\n                }\n            }\n        }\n        return true\n    }\n\n    /**\n     * 移动到相应位置\n     */\n    private fun showMoveToPositionDialog() {\n        // TODO: 2018/2/26 xwt 移动到相应的位置\n        isShowPosition = true\n    }\n\n    private fun onCancel() {\n        onUp()\n    }\n\n    private fun onUp() {\n        dismissLightDialog()\n        dismissVolumeDialog()\n    }\n\n    //手势识别中不需要使用的方法\n    override fun onLongPress(e: MotionEvent?) {}\n\n    //手势识别中不需要使用的方法\n    override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {\n        return false\n    }\n\n    /**\n     * 显示声音控制对话框\n     */\n    private fun showVolumeDialog(deltaY: Float) {\n        isShowVolume = true\n        //记录滑动时候当前的声音\n        var currentVideoVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)\n        val maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)\n        if (deltaY < 0) { //向下滑动\n            currentVideoVolume--\n        } else { //向下滑动\n            currentVideoVolume++\n        }\n        //设置声音\n        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVideoVolume, 0)\n        if (mVolumeDialog == null) {\n            val view = LayoutInflater.from(context).inflate(R.layout.dialog_volume_controller, null)\n            mVolumeProgress = view.findViewById(R.id.pb_volume_progress)\n            mVolumeDialog = createDialogWithView(view, Gravity.START or Gravity.CENTER_VERTICAL)\n        }\n        if (!mVolumeDialog!!.isShowing) {\n            mVolumeDialog!!.show()\n        }\n        //设置进度条\n        val nextVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)\n        var volumePercent = (nextVolume * 1f / maxVolume * 100f).toInt()\n        if (volumePercent > 100) {\n            volumePercent = 100\n        } else if (volumePercent < 0) {\n            volumePercent = 0\n        }\n        mVolumeProgress!!.progress = volumePercent\n    }\n\n    /**\n     * 隐藏声音对话框\n     */\n    private fun dismissVolumeDialog() {\n        if (mVolumeDialog != null) {\n            mVolumeDialog!!.dismiss()\n            isShowVolume = false\n        }\n    }\n\n    /**\n     * 显示控制亮度对话框\n     */\n    private fun showLightDialog(deltaY: Float) {\n        isShowLight = true\n        var screenBrightness = 0f\n        //记录滑动前的亮度\n        val lp = getWindow(context)!!.attributes\n        if (lp.screenBrightness < 0) {\n            try {\n                screenBrightness = Settings.System.getInt(context.contentResolver, Settings.System.SCREEN_BRIGHTNESS).toFloat()\n            } catch (e: SettingNotFoundException) {\n                e.printStackTrace()\n            }\n        } else {\n            screenBrightness = lp.screenBrightness\n        }\n        if (deltaY < 0) { //向下滑动\n            screenBrightness -= 0.03f\n        } else { //向下滑动\n            screenBrightness += 0.03f\n        }\n        when {\n            screenBrightness >= 1 -> {\n                lp.screenBrightness = 1f\n            }\n            screenBrightness < 0 -> {\n                lp.screenBrightness = 0.1f\n            }\n            else -> {\n                lp.screenBrightness = screenBrightness\n            }\n        }\n        getWindow(context)!!.attributes = lp\n        //设置亮度百分比\n        if (mLightProgress == null) {\n            val view = LayoutInflater.from(context).inflate(R.layout.dialog_light_controller, null)\n            mLightProgress = view.findViewById(R.id.pb_light_progress)\n            mLightDialog = createDialogWithView(view, Gravity.END or Gravity.CENTER_VERTICAL)\n        }\n        if (!mLightDialog!!.isShowing) {\n            mLightDialog?.show()\n        }\n        val lightPercent = (screenBrightness * 100f).toInt()\n        mLightProgress?.progress = lightPercent\n    }\n\n    private fun dismissLightDialog() {\n        mLightDialog?.let {\n            mLightDialog!!.dismiss()\n            isShowLight = false\n        }\n    }\n\n    /**\n     * 根据View与位置创建dialog\n     *\n     * @param localView 内容布局\n     * @param gravity   位置\n     */\n    fun createDialogWithView(localView: View?, gravity: Int): Dialog {\n        return Dialog(context, R.style.VideoProgress).apply {\n            setContentView(localView)\n            window.apply {\n                addFlags(Window.FEATURE_ACTION_BAR)\n                addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)\n                addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)\n                setLayout(-2, -2)\n                setBackgroundDrawable(ColorDrawable())\n                val localLayoutParams = attributes\n                localLayoutParams.gravity = gravity\n                attributes = localLayoutParams\n            }\n        }\n    }\n\n\n    /**\n     * 进入全屏\n     */\n    fun enterFullScreen() {\n        hideActionBar(context)\n        getActivity(context)!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE\n        val contentView = getActivity(context)!!.findViewById<ViewGroup>(Window.ID_ANDROID_CONTENT)\n        mParent = parent as ViewGroup\n        mParent!!.removeView(this)\n        mIjkVideoView.screenState = ControllerViewFactory.FULL_SCREEN_MODE\n        val params = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)\n        contentView.addView(this, params)\n    }\n\n    /**\n     * 退出全屏\n     */\n    fun exitFullScreen() {\n        showActionBar(context)\n        val contentView = getActivity(context)!!.findViewById<ViewGroup>(Window.ID_ANDROID_CONTENT)\n        getActivity(context)!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT\n        contentView.removeView(this)\n        mIjkVideoView.screenState = ControllerViewFactory.TINY_MODE\n        val params = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)\n        mParent!!.addView(this, params)\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // 代理IjkVideoView方法\n    ///////////////////////////////////////////////////////////////////////////\n\n    fun setVideoPath(playUrl: String) {\n        mIjkVideoView.setVideoPath(playUrl)\n    }\n\n    fun start() {\n        mIjkVideoView.start()\n    }\n\n    fun setMediaController(controller: IjkMediaController?) {\n        mIjkVideoView.setMediaController(controller)\n    }\n\n    fun toggleAspectRatio(currentAspectRatio: Int) {\n        mIjkVideoView.toggleAspectRatio(currentAspectRatio)\n    }\n\n    fun setOnPreparedListener(onPreparedListener: IMediaPlayer.OnPreparedListener) {\n        mIjkVideoView.setOnPreparedListener(onPreparedListener)\n    }\n\n    fun setOnErrorListener(onErrorListener: IMediaPlayer.OnErrorListener) {\n        mIjkVideoView.setOnErrorListener(onErrorListener)\n    }\n\n    fun setOnCompletionListener(onCompletionListener: IMediaPlayer.OnCompletionListener) {\n        mIjkVideoView.setOnCompletionListener(onCompletionListener)\n    }\n\n    fun stopPlayback() {\n        mIjkVideoView.stopPlayback()\n    }\n\n    fun isPlaying(): Boolean {\n        return mIjkVideoView.isPlaying\n    }\n\n    fun pause() {\n        mIjkVideoView.pause()\n    }\n\n    fun release(clearTargetState: Boolean) {\n        mIjkVideoView.release(clearTargetState)\n    }\n\n    fun showErrorView(): Boolean {\n        mCenterProgress.handler.postDelayed({\n            mCenterProgress.visibility = View.GONE\n            mIjkVideoView.getMediaController()!!.showErrorView()\n        }, 1000)\n        return false\n    }\n\n    fun resetType() {\n        mIjkVideoView.getMediaController()?.resetType()\n    }\n\n    fun setDragging(dragging: Boolean) {\n        mIjkVideoView.getMediaController()!!.controllerView!!.isDragging = dragging\n    }\n\n    fun isDragging(): Boolean {\n        return mIjkVideoView.getMediaController()!!.controllerView!!.isDragging\n    }\n\n    fun showControllerAllTheTime() {\n        mIjkVideoView.getMediaController()?.showAllTheTime()\n    }\n\n    fun getDuration(): Int {\n        return mIjkVideoView.duration\n    }\n\n    fun showController() {\n        mIjkVideoView.getMediaController()!!.show()\n    }\n\n    fun seekTo(msec: Int) {\n        mIjkVideoView.seekTo(msec)\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/MeasureHelper.kt",
    "content": "package com.jennifer.andy.simpleeyes.player\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.player.render.IRenderView\nimport java.lang.ref.WeakReference\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/9 15:09\n * Description:\n */\n\n\n/**\n * 测量帮助类，根据宽高比及方向，获得测量后的宽高\n */\nclass MeasureHelper(view: View) {\n\n    private val mWeakView: WeakReference<View>?\n    private var mVideoWidth = 0//视频布局的宽度\n    private var mVideoHeight = 0//视频布局的高度\n    private var mVideoSarNum = 0 //实际视频的宽\n\n    private var mVideoSarDen = 0//实际视频的高\n    private var mVideoRotationDegree = 0 //屏幕旋转度数\n\n    var measuredWidth = 0//测量后的宽\n    var measuredHeight = 0 //测量后的高\n\n    private var mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT //宽高比例\n\n\n    val view: View? get() = mWeakView?.get()\n\n\n    init {\n        mWeakView = WeakReference(view)\n    }\n\n    fun setVideoSize(videoWidth: Int, videoHeight: Int) {\n        mVideoWidth = videoWidth\n        mVideoHeight = videoHeight\n    }\n\n    fun setVideoSampleAspectRatio(videoSarNum: Int, videoSarDen: Int) {\n        mVideoSarNum = videoSarNum\n        mVideoSarDen = videoSarDen\n    }\n\n    fun setVideoRotation(videoRotationDegree: Int) {\n        mVideoRotationDegree = videoRotationDegree\n    }\n\n    /**\n     * 当 View.onMeasure(int, int) 时调用，用于输出视频校正后的宽高\n     *\n     * @param widthMeasureSpec  宽度测量规则\n     * @param heightMeasureSpec 高度测量规则\n     */\n    fun doMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n\n        var widthMeasureSpec = widthMeasureSpec\n        var heightMeasureSpec = heightMeasureSpec\n\n        if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {\n            //如果屏幕为横屏，将宽高进行对调\n            val tempSpec = widthMeasureSpec\n            widthMeasureSpec = heightMeasureSpec\n            heightMeasureSpec = tempSpec\n        }\n        //获取父布局宽高\n        var width = View.getDefaultSize(mVideoWidth, widthMeasureSpec)\n        var height = View.getDefaultSize(mVideoHeight, heightMeasureSpec)\n        if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) {\n            //如果宽高比例是匹配父布局，宽高不变\n            width = widthMeasureSpec\n            height = heightMeasureSpec\n        }\n\n        //设置的视频宽高大于0时\n        else if (mVideoWidth > 0 && mVideoHeight > 0) {\n            val widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec)\n            val widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec)\n            val heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec)\n            val heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec)\n\n            //当前控件测量模式为包裹模式时，根据视频比例与渲染比例，计算出视频显示的宽高比\n            if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) {\n                val specAspectRatio = widthSpecSize.toFloat() / heightSpecSize.toFloat()\n                var displayAspectRatio: Float\n                when (mCurrentAspectRatio) {\n                    IRenderView.AR_16_9_FIT_PARENT -> {\n                        displayAspectRatio = 16.0f / 9.0f\n                        if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)\n                            displayAspectRatio = 1.0f / displayAspectRatio\n                    }\n                    IRenderView.AR_4_3_FIT_PARENT -> {\n                        displayAspectRatio = 4.0f / 3.0f\n                        if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)\n                            displayAspectRatio = 1.0f / displayAspectRatio\n                    }\n                    IRenderView.AR_ASPECT_FIT_PARENT,\n                    IRenderView.AR_ASPECT_FILL_PARENT,\n                    IRenderView.AR_ASPECT_WRAP_CONTENT -> {\n                        displayAspectRatio = mVideoWidth.toFloat() / mVideoHeight.toFloat()\n                        if (mVideoSarNum > 0 && mVideoSarDen > 0)\n                            displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen\n                    }\n                    else -> {\n                        displayAspectRatio = mVideoWidth.toFloat() / mVideoHeight.toFloat()\n                        if (mVideoSarNum > 0 && mVideoSarDen > 0)\n                            displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen\n                    }\n                }\n\n                val shouldBeWider = displayAspectRatio > specAspectRatio\n                when (mCurrentAspectRatio) {\n\n                    //以下三种模式，以大的为固定边，小的边按比例缩放\n                    IRenderView.AR_ASPECT_FIT_PARENT,\n                    IRenderView.AR_16_9_FIT_PARENT,\n                    IRenderView.AR_4_3_FIT_PARENT ->\n                        if (shouldBeWider) {\n                            //高度偏小，则宽度不变，对高进行校正\n                            width = widthSpecSize\n                            height = (width / displayAspectRatio).toInt()\n                        } else {\n                            //宽度偏小，则高度不变，对宽进行校正\n                            height = heightSpecSize\n                            width = (height * displayAspectRatio).toInt()\n                        }\n\n                    //填充模式下，以小的为固定边，大的边按比例缩放\n                    IRenderView.AR_ASPECT_FILL_PARENT ->\n                        if (shouldBeWider) {\n                            //高度偏小，则高度不变，对宽进行校正\n                            height = heightSpecSize\n                            width = (height * displayAspectRatio).toInt()\n                        } else {\n                            //宽度偏小，则宽度不变，对高进行校正\n                            width = widthSpecSize\n                            height = (width / displayAspectRatio).toInt()\n                        }\n\n                    //包裹模式，以视频采样的宽高为准。\n                    IRenderView.AR_ASPECT_WRAP_CONTENT ->\n                        if (shouldBeWider) {\n                            //高度偏小，将视频宽度与父布局宽度进行比较，取最小。并重新计算高度\n                            width = mVideoWidth.coerceAtMost(widthSpecSize)\n                            height = (width / displayAspectRatio).toInt()\n                        } else {\n                            //宽度偏小，将视频高度与父布局高度进行比较，取最小。重新计算宽度\n                            height = mVideoHeight.coerceAtMost(heightSpecSize)\n                            width = (height * displayAspectRatio).toInt()\n                        }\n                    //默认情况下\n                    else -> if (shouldBeWider) {\n                        width = mVideoWidth.coerceAtMost(widthSpecSize)\n                        height = (width / displayAspectRatio).toInt()\n                    } else {\n                        height = mVideoHeight.coerceAtMost(heightSpecSize)\n                        width = (height * displayAspectRatio).toInt()\n                    }\n                }\n            }\n\n            //视频宽高为准确模式下,以小的为固定边，大的边按比例缩放\n            else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) {\n                width = widthSpecSize\n                height = heightSpecSize\n                if (mVideoWidth * height < width * mVideoHeight) {// videoWidth/videoHeight < width / height\n                    //宽度过大，重新调整宽度\n                    width = height * mVideoWidth / mVideoHeight\n                } else if (mVideoWidth * height > width * mVideoHeight) {\n                    //高度过大，重新调整高度\n                    height = width * mVideoHeight / mVideoWidth\n                }\n            }\n\n            // 只有宽在准确模式下，宽度以实际为准，高度不能超过父布局\n            else if (widthSpecMode == View.MeasureSpec.EXACTLY) {\n                width = widthSpecSize\n                height = width * mVideoHeight / mVideoWidth\n                if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {\n                    height = heightSpecSize\n                }\n            }\n\n            //只有高在准确模式模式下，高度以实际为准，宽度不能超过父布局\n            else if (heightSpecMode == View.MeasureSpec.EXACTLY) {\n                height = heightSpecSize\n                width = height * mVideoWidth / mVideoHeight\n                if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {\n                    width = widthSpecSize\n                }\n            }\n            // 当宽高都不固定时，使用实际的视频宽高\n            else {\n                width = mVideoWidth\n                height = mVideoHeight\n                if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {\n                    //太高，重新校正宽高\n                    height = heightSpecSize\n                    width = height * mVideoWidth / mVideoHeight\n                }\n                if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {\n                    //太宽，重新校正宽高\n                    width = widthSpecSize\n                    height = width * mVideoHeight / mVideoWidth\n                }\n            }\n        } else { // no size yet, just adopt the given spec sizes\n        }\n        measuredWidth = width\n        measuredHeight = height\n    }\n\n    fun setAspectRatio(aspectRatio: Int) {\n        mCurrentAspectRatio = aspectRatio\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/PolicyCompat.kt",
    "content": "package com.jennifer.andy.simpleeyes.player\n\nimport android.content.Context\nimport android.view.Window\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/9 15:07\n * Description:\n */\n\nprivate const val PHONE_WINDOW_CLASS_NAME = \"com.android.internal.policy.PhoneWindow\"\nprivate const val POLICY_MANAGER_CLASS_NAME = \"com.android.internal.policy.PolicyManager\"\n\n\nclass PolicyCompat {\n\n    /**\n     * 通过反射拿不到phoneWindow，当该方法失败时，通过[makeNewWindow]\n     */\n    fun createPhoneWindow(context: Context): Window {\n        return try {\n            val clazz = Class.forName(PHONE_WINDOW_CLASS_NAME)\n            val c = clazz.getConstructor(Context::class.java)\n            c.newInstance(context) as Window\n        } catch (e: Exception) {\n            makeNewWindow(context)\n        }\n    }\n\n    private fun makeNewWindow(context: Context): Window {\n        return try {\n            val clazz = Class.forName(POLICY_MANAGER_CLASS_NAME)\n            val m = clazz.getMethod(\"makeNewWindow\", Context::class.java)\n            m.invoke(null, context) as Window\n        } catch (e: Exception) {\n            throw RuntimeException(e.message)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/event/VideoProgressEvent.kt",
    "content": "package com.jennifer.andy.simpleeyes.player.event\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/9 10:02 PM\n * Description: 视频播放进度事件\n */\n\ndata class VideoProgressEvent(\n        var duration: Int,//总时间\n        var currentPosition: Int,//当前播放时间\n        var progress: Int,//第一进度（播放进度）\n        var secondaryProgress: Int//第二进度（下载进度）\n)\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/render/IRenderView.kt",
    "content": "package com.jennifer.andy.simpleeyes.player.render\n\nimport android.graphics.SurfaceTexture\nimport android.view.Surface\nimport android.view.SurfaceHolder\nimport android.view.View\nimport tv.danmaku.ijk.media.player.IMediaPlayer\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/9 22:44\n * Description:\n */\n\ninterface IRenderView {\n\n    companion object {\n        const val AR_ASPECT_FIT_PARENT = 0 // 适应父布局模式\n        const val AR_ASPECT_FILL_PARENT = 1 //填充父布局模式\n        const val AR_ASPECT_WRAP_CONTENT = 2 //包裹模式\n        const val AR_MATCH_PARENT = 3 //匹配父布局模式\n        const val AR_16_9_FIT_PARENT = 4 //16:9适配屏幕\n        const val AR_4_3_FIT_PARENT = 5 //4:3适配屏幕\n    }\n\n    /**\n     * 获取当前view\n     */\n    fun getView(): View?\n\n    /**\n     * 是否需要等待重新测量\n     */\n    fun shouldWaitForResize(): Boolean\n\n    /**\n     * 设置视屏的宽高\n     */\n    fun setVideoSize(videoWidth: Int, videoHeight: Int)\n\n    /**\n     * 设置视频采样方向比例\n     */\n    fun setVideoSampleAspectRatio(videoSarNum: Int, videoSarDen: Int)\n\n    /**\n     * 设置视频旋转角度\n     */\n    fun setVideoRotation(degree: Int)\n\n    /**\n     * 设置方向比例\n     */\n    fun setAspectRatio(aspectRatio: Int)\n\n    /**\n     * 设置渲染回调\n     */\n    fun addRenderCallback(callback: IRenderCallback)\n\n    /**\n     * 移除渲染回调\n     */\n    fun removeRenderCallback(callback: IRenderCallback)\n\n    /**\n     * surfaceHolder管理相关接口\n     */\n    interface ISurfaceHolder {\n        /**\n         * 绑定当前媒体播放器\n         */\n        fun bindToMediaPlayer(mp: IMediaPlayer?)\n\n        /**\n         * 获取当前渲染的view\n         */\n        val renderView: IRenderView\n\n        /**\n         * 获取当前surfaceHolder\n         */\n        val surfaceHolder: SurfaceHolder?\n\n        /**\n         * 打开surface\n         */\n        fun openSurface(): Surface?\n\n        /**\n         * 获取SurfaceTexture\n         */\n        val surfaceTexture: SurfaceTexture?\n    }\n\n    interface IRenderCallback {\n        fun onSurfaceCreated(holder: ISurfaceHolder, width: Int, height: Int)\n        fun onSurfaceChanged(holder: ISurfaceHolder, format: Int, width: Int, height: Int)\n        fun onSurfaceDestroyed(holder: ISurfaceHolder)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/render/SurfaceRenderView.kt",
    "content": "package com.jennifer.andy.simpleeyes.player.render\n\nimport android.annotation.TargetApi\nimport android.content.Context\nimport android.graphics.SurfaceTexture\nimport android.os.Build\nimport android.util.AttributeSet\nimport android.util.Log\nimport android.view.Surface\nimport android.view.SurfaceHolder\nimport android.view.SurfaceView\nimport android.view.accessibility.AccessibilityEvent\nimport android.view.accessibility.AccessibilityNodeInfo\nimport com.jennifer.andy.simpleeyes.player.MeasureHelper\nimport com.jennifer.andy.simpleeyes.player.render.IRenderView.IRenderCallback\nimport com.jennifer.andy.simpleeyes.player.render.IRenderView.ISurfaceHolder\nimport tv.danmaku.ijk.media.player.IMediaPlayer\nimport tv.danmaku.ijk.media.player.ISurfaceTextureHolder\nimport java.lang.ref.WeakReference\nimport java.util.concurrent.ConcurrentHashMap\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/9 22:47\n * Description:surfaceView渲染界面\n */\n\nclass SurfaceRenderView : SurfaceView, IRenderView {\n\n    private lateinit var mMeasureHelper: MeasureHelper\n\n    constructor(context: Context) : super(context) {\n        initView(context)\n    }\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {\n        initView(context)\n    }\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        initView(context)\n    }\n\n    @TargetApi(Build.VERSION_CODES.LOLLIPOP)\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {\n        initView(context)\n    }\n\n    private fun initView(context: Context) {\n        mMeasureHelper = MeasureHelper(this)\n        mSurfaceCallback = SurfaceCallback(this)\n        holder.addCallback(mSurfaceCallback)\n        holder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL)\n    }\n\n    override fun getView() = this\n\n    override fun shouldWaitForResize() = true\n\n\n    //--------------------\n    // Layout & Measure\n    //--------------------\n    override fun setVideoSize(videoWidth: Int, videoHeight: Int) {\n        if (videoWidth > 0 && videoHeight > 0) {\n            mMeasureHelper.setVideoSize(videoWidth, videoHeight)\n            holder.setFixedSize(videoWidth, videoHeight)\n            requestLayout()\n        }\n    }\n\n    override fun setVideoSampleAspectRatio(videoSarNum: Int, videoSarDen: Int) {\n        if (videoSarNum > 0 && videoSarDen > 0) {\n            mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen)\n            requestLayout()\n        }\n    }\n\n    override fun setVideoRotation(degree: Int) {\n        Log.e(\"\", \"SurfaceView doesn't support rotation ($degree)!\\n\")\n    }\n\n    override fun setAspectRatio(aspectRatio: Int) {\n        mMeasureHelper.setAspectRatio(aspectRatio)\n        requestLayout()\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec)\n        setMeasuredDimension(mMeasureHelper.measuredWidth, mMeasureHelper.measuredHeight)\n    }\n\n    //--------------------\n    // SurfaceViewHolder\n    //--------------------\n    private class InternalSurfaceHolder(private val surfaceView: SurfaceRenderView,\n                                        override val surfaceHolder: SurfaceHolder?) : ISurfaceHolder {\n\n        override fun bindToMediaPlayer(mp: IMediaPlayer?) {\n            if (mp != null) {\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&\n                        mp is ISurfaceTextureHolder) {\n                    val textureHolder = mp as ISurfaceTextureHolder\n                    textureHolder.surfaceTexture = null\n                }\n                mp.setDisplay(surfaceHolder)\n            }\n        }\n\n        override val renderView: IRenderView\n            get() = surfaceView\n\n        override val surfaceTexture: SurfaceTexture?\n            get() = null\n\n        override fun openSurface(): Surface? {\n            return surfaceHolder?.surface\n        }\n\n    }\n\n    //-------------------------\n    // SurfaceHolder.Callback\n    //-------------------------\n    override fun addRenderCallback(callback: IRenderCallback) {\n        mSurfaceCallback.addRenderCallback(callback)\n    }\n\n    override fun removeRenderCallback(callback: IRenderCallback) {\n        mSurfaceCallback.removeRenderCallback(callback)\n    }\n\n    private lateinit var mSurfaceCallback: SurfaceCallback\n\n    private class SurfaceCallback(surfaceView: SurfaceRenderView) : SurfaceHolder.Callback {\n        private var mSurfaceHolder: SurfaceHolder? = null\n        private var mIsFormatChanged = false\n        private var mFormat = 0\n        private var mWidth = 0\n        private var mHeight = 0\n        private val mWeakSurfaceView: WeakReference<SurfaceRenderView> = WeakReference(surfaceView)\n        private val mRenderCallbackMap: MutableMap<IRenderCallback, Any> = ConcurrentHashMap()\n\n        fun addRenderCallback(callback: IRenderCallback) {\n            mRenderCallbackMap[callback] = callback\n            var surfaceHolder: ISurfaceHolder? = null\n            if (mSurfaceHolder != null) {\n                if (surfaceHolder == null) surfaceHolder = InternalSurfaceHolder(mWeakSurfaceView.get()!!, mSurfaceHolder)\n                callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight)\n            }\n            if (mIsFormatChanged) {\n                if (surfaceHolder == null) surfaceHolder = InternalSurfaceHolder(mWeakSurfaceView.get()!!, mSurfaceHolder)\n                callback.onSurfaceChanged(surfaceHolder, mFormat, mWidth, mHeight)\n            }\n        }\n\n        fun removeRenderCallback(callback: IRenderCallback) {\n            mRenderCallbackMap.remove(callback)\n        }\n\n        override fun surfaceCreated(holder: SurfaceHolder) {\n            mSurfaceHolder = holder\n            mIsFormatChanged = false\n            mFormat = 0\n            mWidth = 0\n            mHeight = 0\n            //调用监听\n            val surfaceHolder: ISurfaceHolder = InternalSurfaceHolder(mWeakSurfaceView.get()!!, mSurfaceHolder)\n            for (renderCallback in mRenderCallbackMap.keys) {\n                renderCallback.onSurfaceCreated(surfaceHolder, 0, 0)\n            }\n        }\n\n        override fun surfaceDestroyed(holder: SurfaceHolder) {\n            mSurfaceHolder = null\n            mIsFormatChanged = false\n            mFormat = 0\n            mWidth = 0\n            mHeight = 0\n            val surfaceHolder: ISurfaceHolder = InternalSurfaceHolder(mWeakSurfaceView.get()!!, mSurfaceHolder)\n            for (renderCallback in mRenderCallbackMap.keys) {\n                renderCallback.onSurfaceDestroyed(surfaceHolder)\n            }\n        }\n\n        override fun surfaceChanged(holder: SurfaceHolder, format: Int,\n                                    width: Int, height: Int) {\n            mSurfaceHolder = holder\n            mIsFormatChanged = true\n            mFormat = format\n            mWidth = width\n            mHeight = height\n            // mMeasureHelper.setVideoSize(width, height);\n            val surfaceHolder: ISurfaceHolder = InternalSurfaceHolder(mWeakSurfaceView.get()!!, mSurfaceHolder)\n            for (renderCallback in mRenderCallbackMap.keys) {\n                renderCallback.onSurfaceChanged(surfaceHolder, format, width, height)\n            }\n        }\n\n    }\n\n    //--------------------\n    // Accessibility\n    //--------------------\n    override fun onInitializeAccessibilityEvent(event: AccessibilityEvent) {\n        super.onInitializeAccessibilityEvent(event)\n        event.className = SurfaceRenderView::class.java.name\n    }\n\n    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)\n    override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {\n        super.onInitializeAccessibilityNodeInfo(info)\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n            info.className = SurfaceRenderView::class.java.name\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/render/TextureRenderView.kt",
    "content": "package com.jennifer.andy.simpleeyes.player.render\n\nimport android.annotation.TargetApi\nimport android.content.Context\nimport android.graphics.SurfaceTexture\nimport android.os.Build\nimport android.util.AttributeSet\nimport android.util.Log\nimport android.view.Surface\nimport android.view.SurfaceHolder\nimport android.view.TextureView\nimport android.view.View\nimport android.view.accessibility.AccessibilityEvent\nimport android.view.accessibility.AccessibilityNodeInfo\nimport com.jennifer.andy.simpleeyes.player.MeasureHelper\nimport com.jennifer.andy.simpleeyes.player.render.IRenderView.IRenderCallback\nimport com.jennifer.andy.simpleeyes.player.render.IRenderView.ISurfaceHolder\nimport tv.danmaku.ijk.media.player.IMediaPlayer\nimport tv.danmaku.ijk.media.player.ISurfaceTextureHolder\nimport tv.danmaku.ijk.media.player.ISurfaceTextureHost\nimport java.lang.ref.WeakReference\nimport java.util.concurrent.ConcurrentHashMap\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/9 22:56\n * Description:\n */\n\n@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)\nclass TextureRenderView : TextureView, IRenderView {\n\n    private lateinit var mMeasureHelper: MeasureHelper\n\n    constructor(context: Context) : super(context) {\n        initView(context)\n    }\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {\n        initView(context)\n    }\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        initView(context)\n    }\n\n    @TargetApi(Build.VERSION_CODES.LOLLIPOP)\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {\n        initView(context)\n    }\n\n    private fun initView(context: Context) {\n        mMeasureHelper = MeasureHelper(this)\n        mSurfaceCallback = SurfaceCallback(this)\n        surfaceTextureListener = mSurfaceCallback\n    }\n\n    override fun getView(): View {\n        return this\n    }\n\n    override fun shouldWaitForResize(): Boolean {\n        return false\n    }\n\n    override fun onDetachedFromWindow() {\n        mSurfaceCallback.willDetachFromWindow()\n        super.onDetachedFromWindow()\n        mSurfaceCallback.didDetachFromWindow()\n    }\n\n    //--------------------\n    // Layout & Measure\n    //--------------------\n    override fun setVideoSize(videoWidth: Int, videoHeight: Int) {\n        if (videoWidth > 0 && videoHeight > 0) {\n            mMeasureHelper.setVideoSize(videoWidth, videoHeight)\n            requestLayout()\n        }\n    }\n\n    override fun setVideoSampleAspectRatio(videoSarNum: Int, videoSarDen: Int) {\n        if (videoSarNum > 0 && videoSarDen > 0) {\n            mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen)\n            requestLayout()\n        }\n    }\n\n    override fun setVideoRotation(degree: Int) {\n        mMeasureHelper.setVideoRotation(degree)\n        rotation = degree.toFloat()\n    }\n\n    override fun setAspectRatio(aspectRatio: Int) {\n        mMeasureHelper.setAspectRatio(aspectRatio)\n        requestLayout()\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec)\n        setMeasuredDimension(mMeasureHelper.measuredWidth, mMeasureHelper.measuredHeight)\n    }\n\n    //--------------------\n    // TextureViewHolder\n    //--------------------\n    fun getSurfaceHolder(): ISurfaceHolder {\n        return InternalSurfaceHolder(this, mSurfaceCallback.mSurfaceTexture, mSurfaceCallback)\n    }\n\n    /**\n     * 内部surfaceHolder\n     */\n    private class InternalSurfaceHolder(private val textureView: TextureRenderView,\n                                        override val surfaceTexture: SurfaceTexture?,\n                                        private val mSurfaceTextureHost: ISurfaceTextureHost) : ISurfaceHolder {\n        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n        override fun bindToMediaPlayer(mp: IMediaPlayer?) {\n            if (mp == null) return\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&\n                    mp is ISurfaceTextureHolder) {\n                val textureHolder = mp as ISurfaceTextureHolder\n                textureView.mSurfaceCallback.setOwnSurfaceTexture(false)\n                val surfaceTexture = textureHolder.surfaceTexture\n                if (surfaceTexture != null) {\n                    textureView.surfaceTexture = surfaceTexture\n                } else {\n                    textureHolder.surfaceTexture = this.surfaceTexture\n                    textureHolder.setSurfaceTextureHost(textureView.mSurfaceCallback)\n                }\n            } else {\n                mp.setSurface(openSurface())\n            }\n        }\n\n        override val renderView: IRenderView\n            get() = textureView\n\n        override val surfaceHolder: SurfaceHolder?\n            get() = null\n\n        override fun openSurface(): Surface? {\n            return if (surfaceTexture == null) null else Surface(surfaceTexture)\n        }\n\n    }\n\n    //-------------------------\n    // SurfaceHolder.Callback\n    //-------------------------\n    override fun addRenderCallback(callback: IRenderCallback) {\n        mSurfaceCallback.addRenderCallback(callback)\n    }\n\n    override fun removeRenderCallback(callback: IRenderCallback) {\n        mSurfaceCallback.removeRenderCallback(callback)\n    }\n\n    private lateinit var mSurfaceCallback: SurfaceCallback\n\n    /**\n     * SurfaceTexture监听\n     */\n    private class SurfaceCallback(renderView: TextureRenderView) : SurfaceTextureListener, ISurfaceTextureHost {\n        var mSurfaceTexture: SurfaceTexture? = null\n        private var mIsFormatChanged = false\n        private var mWidth = 0\n        private var mHeight = 0\n        private var mOwnSurfaceTexture = true\n        private var mWillDetachFromWindow = false\n        private var mDidDetachFromWindow = false\n        private val mWeakRenderView: WeakReference<TextureRenderView> = WeakReference(renderView)\n        private val mRenderCallbackMap: MutableMap<IRenderCallback, Any> = ConcurrentHashMap()\n\n        fun setOwnSurfaceTexture(ownSurfaceTexture: Boolean) {\n            mOwnSurfaceTexture = ownSurfaceTexture\n        }\n\n        /**\n         * 添加渲染回调\n         */\n        fun addRenderCallback(callback: IRenderCallback) {\n            mRenderCallbackMap[callback] = callback\n            var surfaceHolder: ISurfaceHolder? = null\n            if (mSurfaceTexture != null) {\n                if (surfaceHolder == null) surfaceHolder = InternalSurfaceHolder(mWeakRenderView.get()!!, mSurfaceTexture, this)\n                callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight)\n            }\n            if (mIsFormatChanged) {\n                if (surfaceHolder == null) surfaceHolder = InternalSurfaceHolder(mWeakRenderView.get()!!, mSurfaceTexture, this)\n                callback.onSurfaceChanged(surfaceHolder, 0, mWidth, mHeight)\n            }\n        }\n\n        fun removeRenderCallback(callback: IRenderCallback) {\n            mRenderCallbackMap.remove(callback)\n        }\n\n        /**\n         * 当纹理面板可用的时候\n         */\n        override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {\n            mSurfaceTexture = surface\n            mIsFormatChanged = false\n            mWidth = 0\n            mHeight = 0\n            val surfaceHolder: ISurfaceHolder = InternalSurfaceHolder(mWeakRenderView.get()!!, surface, this)\n            for (renderCallback in mRenderCallbackMap.keys) {\n                renderCallback.onSurfaceCreated(surfaceHolder, 0, 0)\n            }\n        }\n\n        /**\n         * 当纹理面板大小发生改变的时候\n         */\n        override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {\n            mSurfaceTexture = surface\n            mIsFormatChanged = true\n            mWidth = width\n            mHeight = height\n            val surfaceHolder: ISurfaceHolder = InternalSurfaceHolder(mWeakRenderView.get()!!, surface, this)\n            for (renderCallback in mRenderCallbackMap.keys) {\n                renderCallback.onSurfaceChanged(surfaceHolder, 0, width, height)\n            }\n        }\n\n        /**\n         * 当表面纹理摧毁的时候\n         */\n        override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {\n            mSurfaceTexture = surface\n            mIsFormatChanged = false\n            mWidth = 0\n            mHeight = 0\n            val surfaceHolder: ISurfaceHolder = InternalSurfaceHolder(mWeakRenderView.get()!!, surface, this)\n            for (renderCallback in mRenderCallbackMap.keys) {\n                renderCallback.onSurfaceDestroyed(surfaceHolder)\n            }\n            Log.d(TAG, \"onSurfaceTextureDestroyed: destroy: $mOwnSurfaceTexture\")\n            return mOwnSurfaceTexture\n        }\n\n        /**\n         * 当表面纹理更新的时候\n         */\n        override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {}\n\n        //-------------------------\n        // ISurfaceTextureHost\n        //-------------------------\n        override fun releaseSurfaceTexture(surfaceTexture: SurfaceTexture) {\n            if (surfaceTexture == null) {\n                Log.d(TAG, \"releaseSurfaceTexture: null\")\n            } else if (mDidDetachFromWindow) {\n                if (surfaceTexture !== mSurfaceTexture) {\n                    Log.d(TAG, \"releaseSurfaceTexture: didDetachFromWindow(): release different SurfaceTexture\")\n                    surfaceTexture.release()\n                } else if (!mOwnSurfaceTexture) {\n                    Log.d(TAG, \"releaseSurfaceTexture: didDetachFromWindow(): release detached SurfaceTexture\")\n                    surfaceTexture.release()\n                } else {\n                    Log.d(TAG, \"releaseSurfaceTexture: didDetachFromWindow(): already released by TextureView\")\n                }\n            } else if (mWillDetachFromWindow) {\n                if (surfaceTexture !== mSurfaceTexture) {\n                    Log.d(TAG, \"releaseSurfaceTexture: willDetachFromWindow(): release different SurfaceTexture\")\n                    surfaceTexture.release()\n                } else if (!mOwnSurfaceTexture) {\n                    Log.d(TAG, \"releaseSurfaceTexture: willDetachFromWindow(): re-attach SurfaceTexture to TextureView\")\n                    setOwnSurfaceTexture(true)\n                } else {\n                    Log.d(TAG, \"releaseSurfaceTexture: willDetachFromWindow(): will released by TextureView\")\n                }\n            } else {\n                if (surfaceTexture !== mSurfaceTexture) {\n                    Log.d(TAG, \"releaseSurfaceTexture: alive: release different SurfaceTexture\")\n                    surfaceTexture.release()\n                } else if (!mOwnSurfaceTexture) {\n                    Log.d(TAG, \"releaseSurfaceTexture: alive: re-attach SurfaceTexture to TextureView\")\n                    setOwnSurfaceTexture(true)\n                } else {\n                    Log.d(TAG, \"releaseSurfaceTexture: alive: will released by TextureView\")\n                }\n            }\n        }\n\n        /**\n         * 将要从window消失\n         */\n        fun willDetachFromWindow() {\n            Log.d(TAG, \"willDetachFromWindow()\")\n            mWillDetachFromWindow = true\n        }\n\n        /**\n         * 已经从window消失\n         */\n        fun didDetachFromWindow() {\n            Log.d(TAG, \"didDetachFromWindow()\")\n            mDidDetachFromWindow = true\n        }\n\n    }\n\n    //--------------------\n    // Accessibility\n    //--------------------\n    override fun onInitializeAccessibilityEvent(event: AccessibilityEvent) {\n        super.onInitializeAccessibilityEvent(event)\n        event.className = TextureRenderView::class.java.name\n    }\n\n    override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {\n        super.onInitializeAccessibilityNodeInfo(info)\n        info.className = TextureRenderView::class.java.name\n    }\n\n    companion object {\n        private const val TAG = \"TextureRenderView\"\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/view/ControllerView.kt",
    "content": "package com.jennifer.andy.simpleeyes.player.view\n\nimport android.content.Context\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.FrameLayout\nimport android.widget.MediaController.MediaPlayerControl\nimport com.jennifer.andy.simpleeyes.entity.ContentBean\nimport com.jennifer.andy.simpleeyes.player.IjkMediaController\nimport com.jennifer.andy.simpleeyes.player.event.VideoProgressEvent\nimport com.jennifer.andy.simpleeyes.utils.stringForTime\nimport java.util.*\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/9 23:00\n * Description:\n */\n\nabstract class ControllerView(context: Context) : FrameLayout(context) {\n\n    protected lateinit var mPlayer: MediaPlayerControl\n    protected lateinit var mController: IjkMediaController\n    protected var mCurrentVideoInfo: ContentBean? = null\n\n    var isDragging = false//是否正在拖动\n    private var mErrorView: ErrorView? = null\n    private val mContentViews: MutableList<View> = ArrayList()\n\n    abstract val layoutId: Int\n\n    fun initControllerAndData(player: MediaPlayerControl, controller: IjkMediaController, currentVideoInfo: ContentBean?) {\n        mPlayer = player\n        mController = controller\n        mCurrentVideoInfo = currentVideoInfo\n        LayoutInflater.from(context).inflate(layoutId, this, true)\n        initView()\n        initData()\n    }\n\n    /**\n     * 初始化布局\n     */\n    abstract fun initView()\n\n    /**\n     * 初始化数据\n     */\n    abstract fun initData()\n\n    /**\n     * 显示\n     */\n    fun display() {\n        updateTogglePauseUI(mPlayer.isPlaying)\n    }\n\n    /**\n     * 更新暂停切换UI\n     *\n     * @param isPlaying 是否播放\n     */\n    open fun updateTogglePauseUI(isPlaying: Boolean) {}\n\n    /**\n     * 更新当前视频播放进度\n     *\n     * @param progress          第一进度\n     * @param secondaryProgress 第二进度\n     */\n    open fun updateProgress(progress: Int, secondaryProgress: Int) {}\n\n    /**\n     * 更新当前视频播放时间\n     *\n     * @param currentTime 当前时间\n     * @param endTime     结束时间\n     */\n    open fun updateTime(currentTime: String, endTime: String) {}\n\n    /**\n     * 根据传入的[videoProgressEvent] 更新播放进度与时间\n     *\n     * @param videoProgressEvent 播放进度事件\n     */\n    fun updateProgressAndTime(videoProgressEvent: VideoProgressEvent) {\n        if (!isDragging) { //没在拖动的时候跟新进度条\n            updateProgress(videoProgressEvent.progress, videoProgressEvent.secondaryProgress)\n            updateTime(stringForTime(videoProgressEvent.currentPosition), stringForTime(videoProgressEvent.duration))\n        }\n    }\n\n    /**\n     * 暂停切换\n     */\n    fun togglePause() {\n        if (mPlayer.isPlaying) {\n            mPlayer.pause()\n        } else {\n            mPlayer.start()\n        }\n        updateTogglePauseUI(mPlayer.isPlaying)\n    }\n\n\n    internal enum class State {\n        ERROR\n    }\n\n    override fun addView(child: View?, params: ViewGroup.LayoutParams?) {\n        super.addView(child, params)\n        if (child != null && child.tag !== State.ERROR) mContentViews.add(child)\n    }\n\n    /**\n     * 显示错误界面\n     */\n    fun showErrorView() {\n        for (contentView in mContentViews) {\n            contentView.visibility = View.GONE\n        }\n        if (mErrorView == null) {\n            mErrorView = ErrorView(context).apply {\n                setController(mController)\n                tag = State.ERROR\n            }\n            addView(mErrorView)\n        } else {\n            mErrorView!!.visibility = View.VISIBLE\n        }\n    }\n\n    /**\n     * 显示内容界面，默认会将错误界面隐藏\n     */\n    fun showContent() {\n        for (contentView in mContentViews) {\n            contentView.visibility = View.VISIBLE\n        }\n        if (mErrorView != null) {\n            mErrorView!!.visibility = View.GONE\n        }\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/view/ControllerViewFactory.kt",
    "content": "package com.jennifer.andy.simpleeyes.player.view\n\nimport android.content.Context\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/9 23:26\n * Description:\n */\n\nclass ControllerViewFactory {\n\n    companion object {\n        const val TINY_MODE = 0\n        const val FULL_SCREEN_MODE = 1\n    }\n\n    fun create(showMode: Int, context: Context): ControllerView {\n        return when (showMode) {\n            TINY_MODE -> TinyControllerView(context)\n            FULL_SCREEN_MODE -> FullScreenControllerView(context)\n            else -> throw IllegalArgumentException(\"not correct showMode\")\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/view/ErrorView.kt",
    "content": "package com.jennifer.andy.simpleeyes.player.view\n\nimport android.animation.Animator\nimport android.animation.AnimatorListenerAdapter\nimport android.animation.ObjectAnimator\nimport android.content.Context\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.widget.FrameLayout\nimport android.widget.ImageView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.player.IjkMediaController\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/9 9:19 PM\n * Description: 错误界面，用于[ControllerView]显示的网络错误界面。\n */\nclass ErrorView(context: Context) : FrameLayout(context), View.OnClickListener {\n\n    private val mIvReload: ImageView\n    private lateinit var mController: IjkMediaController\n\n    fun setController(controller: IjkMediaController) {\n        mController = controller\n    }\n\n    override fun onClick(v: View) {\n        val rotation = ObjectAnimator.ofFloat(mIvReload, \"rotation\", 0f, 360f)\n        rotation.addListener(object : AnimatorListenerAdapter() {\n            override fun onAnimationEnd(animation: Animator) { //执行选中动画，然后开始刷新操作\n                mController.hide()\n                mController.controllerListener?.onErrorViewClick()\n            }\n        })\n        rotation.duration = 500\n        rotation.start()\n    }\n\n    init {\n        LayoutInflater.from(context).inflate(R.layout.layout_video_error, this, true)\n        mIvReload = findViewById(R.id.iv_reload)\n        setOnClickListener(this)\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/view/FullScreenControllerView.kt",
    "content": "package com.jennifer.andy.simpleeyes.player.view\n\nimport android.content.Context\nimport android.view.View\nimport android.widget.ImageView\nimport android.widget.SeekBar\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.utils.stringForTime\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/9 23:17\n * Description:\n */\n\nclass FullScreenControllerView(context: Context) : ControllerView(context), View.OnClickListener {\n\n    private lateinit var mMinScreen: ImageView\n    private lateinit var mPreButton: ImageView\n    private lateinit var mPauseButton: ImageView\n    private lateinit var mNextButton: ImageView\n    private lateinit var mProgress: SeekBar\n    private lateinit var mTitle: CustomFontTextView\n    private lateinit var mEndTime: CustomFontTextView\n    private lateinit var mCurrentTime: CustomFontTextView\n\n    private var mChangeProgress = 0\n\n    companion object {\n        const val FULL_SCREEN_ID = 100\n    }\n\n    override val layoutId = R.layout.layout_media_controller_full_screen\n\n    override fun initView() {\n        mMinScreen = findViewById(R.id.iv_min_screen)\n        mProgress = findViewById(R.id.sb_progress)\n        mTitle = findViewById(R.id.tv_title)\n        mPreButton = findViewById(R.id.iv_previous)\n        mPauseButton = findViewById(R.id.iv_pause)\n        mNextButton = findViewById(R.id.iv_next)\n        mProgress = findViewById(R.id.sb_progress)\n        mCurrentTime = findViewById(R.id.tv_currentTime)\n        mEndTime = findViewById(R.id.tv_end_time)\n        initListener()\n    }\n\n    private fun initListener() {\n        mPreButton.setOnClickListener(this)\n        mMinScreen.setOnClickListener(this)\n        mPauseButton.setOnClickListener(this)\n        mNextButton.setOnClickListener(this)\n        setOnClickListener(this)\n\n        mProgress.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {\n            override fun onStartTrackingTouch(bar: SeekBar) { //控制的时候停止更新进度条，同时禁止隐藏\n                isDragging = true\n                mController.showAllTheTime()\n            }\n\n            override fun onProgressChanged(bar: SeekBar, progress: Int, fromUser: Boolean) {\n                if (fromUser) { //更新当前播放时间\n                    val duration = mPlayer.duration.toLong()\n                    val newPosition = duration * progress / 1000L\n                    mChangeProgress = progress\n                    mCurrentTime.text = stringForTime(newPosition.toInt())\n                }\n            }\n\n            override fun onStopTrackingTouch(bar: SeekBar) { //定位都拖动位置\n                val newPosition = mPlayer.duration * mChangeProgress / 1000L\n                mPlayer.seekTo(newPosition.toInt())\n                mController.show() //开启延时隐藏\n                isDragging = false\n            }\n        })\n    }\n\n    override fun initData() {\n        //初始化标题\n        mTitle.text = mCurrentVideoInfo!!.title\n\n        //初始化播放时间\n        val position = mPlayer.currentPosition\n        val duration = mPlayer.duration\n\n        mCurrentTime.text = stringForTime(position)\n        mEndTime.text = stringForTime(duration)\n\n        //初始化进度条\n        mProgress.apply {\n            max = 1000\n            if (duration >= 0 && mPlayer.bufferPercentage > 0) {\n                val firstProgress = 1000L * position / duration\n                val secondProgress = mPlayer.bufferPercentage * 10\n                progress = firstProgress.toInt()\n                secondaryProgress = secondProgress\n            }\n            setPadding(0, 0, 0, 0)\n        }\n        updatePreNextButton()\n        id = FULL_SCREEN_ID\n    }\n\n\n    //判断是否显示上一个按钮与下一个按钮\n    private fun updatePreNextButton() {\n        mPreButton.visibility = if (mController.isHavePreVideo()) View.VISIBLE else View.GONE\n        mNextButton.visibility = if (mController.isHaveNextVideo()) View.VISIBLE else View.GONE\n    }\n\n\n    override fun onClick(v: View) {\n        when (v.id) {\n            FULL_SCREEN_ID -> mController.hide()\n            R.id.iv_pause -> {\n                togglePause()\n                mController.show()\n            }\n            R.id.iv_previous -> {\n                mController.controllerListener?.onPreClick()\n                updatePreNextButton()\n            }\n            R.id.iv_next -> {\n                mController.controllerListener?.onNextClick()\n                updatePreNextButton()\n            }\n            R.id.iv_min_screen -> {\n                mController.switchControllerView(ControllerViewFactory.TINY_MODE)\n                mController.controllerListener?.onTinyScreenClick()\n            }\n        }\n    }\n\n    override fun updateProgress(progress: Int, secondaryProgress: Int) {\n        mProgress.progress = progress\n        mProgress.secondaryProgress = secondaryProgress\n    }\n\n    override fun updateTime(currentTime: String, endTime: String) {\n        mCurrentTime.text = currentTime\n        mEndTime.text = endTime\n    }\n\n    override fun updateTogglePauseUI(isPlaying: Boolean) {\n        if (isPlaying) {\n            mPauseButton.setImageResource(R.drawable.ic_player_pause)\n        } else {\n            mPauseButton.setImageResource(R.drawable.ic_player_play)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/player/view/TinyControllerView.kt",
    "content": "package com.jennifer.andy.simpleeyes.player.view\n\nimport android.content.Context\nimport android.view.View\nimport android.widget.ImageView\nimport android.widget.TextView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.utils.stringForTime\n\n\n/**\n * Author:  andy.xwt\n * Date:    2020/3/9 23:37\n * Description:\n */\n\nclass TinyControllerView(context: Context) : ControllerView(context), View.OnClickListener {\n\n    private lateinit var mPauseButton: ImageView\n    private lateinit var mPreButton: ImageView\n    private lateinit var mBackButton: ImageView\n    private lateinit var mNextButton: ImageView\n    private lateinit var mFullScreen: ImageView\n\n    private lateinit var mEndTime: TextView\n    private lateinit var mCurrentTime: TextView\n\n    override val layoutId = R.layout.layout_media_controller_tiny\n\n    override fun initView() {\n        mPauseButton = findViewById(R.id.iv_pause)\n        mPreButton = findViewById(R.id.iv_previous)\n        mBackButton = findViewById(R.id.iv_back)\n        mNextButton = findViewById(R.id.iv_next)\n        mFullScreen = findViewById(R.id.iv_full_screen)\n        mEndTime = findViewById(R.id.tv_end_time)\n        mCurrentTime = findViewById(R.id.tv_currentTime)\n        initListener()\n    }\n\n    private fun initListener() {\n        mPreButton.setOnClickListener(this)\n        mPauseButton.setOnClickListener(this)\n        mNextButton.setOnClickListener(this)\n        mBackButton.setOnClickListener(this)\n        mFullScreen.setOnClickListener(this)\n    }\n\n    override fun initData() { //初始化开始时间\n        val position = mPlayer.currentPosition\n        val duration = mPlayer.duration\n        mCurrentTime.text = stringForTime(position)\n        mEndTime.text = \"/${stringForTime(duration)}\"\n        updatePreNextButton()\n    }\n\n\n    //判断是否显示上一个按钮与下一个按钮\n    private fun updatePreNextButton() {\n        mPreButton.visibility = if (mController.isHavePreVideo()) View.VISIBLE else View.GONE\n        mNextButton.visibility = if (mController.isHaveNextVideo()) View.VISIBLE else View.GONE\n    }\n\n    override fun onClick(v: View) {\n        when (v.id) {\n            R.id.iv_pause -> {\n                togglePause()\n                mController.show()\n            }\n            R.id.iv_previous -> {\n                mController.controllerListener?.onPreClick()\n                updatePreNextButton()\n            }\n            R.id.iv_next -> {\n                mController.controllerListener?.onNextClick()\n                updatePreNextButton()\n            }\n            R.id.iv_back -> {\n                mController.hide()\n                mController.controllerListener?.onBackClick()\n            }\n            R.id.iv_full_screen -> {\n                mController.switchControllerView(ControllerViewFactory.FULL_SCREEN_MODE)\n                mController.controllerListener?.onFullScreenClick()\n            }\n        }\n    }\n\n    override fun updateProgress(progress: Int, secondaryProgress: Int) {}\n\n\n    override fun updateTime(currentTime: String, endTime: String) {\n        mCurrentTime.text = currentTime\n        mEndTime.text = \"/$endTime\"\n    }\n\n    override fun updateTogglePauseUI(isPlaying: Boolean) {\n        if (isPlaying) {\n            mPauseButton.setImageResource(R.drawable.ic_player_pause)\n        } else {\n            mPauseButton.setImageResource(R.drawable.ic_player_play)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/router/EyesPathReplaceService.kt",
    "content": "package com.jennifer.andy.simpleeyes.router\n\nimport android.content.Context\nimport android.net.Uri\nimport androidx.core.net.toUri\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.facade.service.PathReplaceService\nimport java.util.regex.Pattern\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/20 14:08\n * Description:\n * 这里发现开眼的ActionUrl不是正常的scheme格式，因为当我们通过Uri跳转的时候会出现问题。所以这里要对其进行替换操作\n */\n@Route(path = \"/github/AndyJennifer\")\nclass EyesPathReplaceService : PathReplaceService {\n\n\n    companion object {\n        const val HOST = \"eyepetizer://github.com/\"\n        const val SCHEME = \"eyepetizer\"\n        const val ONE_LEVEL = \"AndyJennifer/\"\n\n    }\n\n    override fun forString(path: String): String {\n        return path\n    }\n\n    override fun forUri(uri: Uri): Uri {\n\n        if (uri.scheme == SCHEME) {\n            val uriStr = uri.toString()\n            val split = uriStr.split(\"eyepetizer://\")\n            when {\n                uriStr.contains(\"pgc/detail\") -> //处理作者详情\n                    return setPageDetailUrl(split)\n                uriStr.contains(\"webview\") -> //处理webview\n                    return setWebViewUrl(split)\n                uriStr.contains(\"detail\") -> //处理webview中点击跳转\n                    return setWebVideoUrl(split)\n                uriStr.contains(\"ranklist\") ->//处理排行榜\n                    return setRankListUrl(split)\n                uriStr.contains(\"campaign\") ->//处理专题\n                    return setTopicUrl(split)\n                uriStr.contains(\"tag\") ->//处理360全景\n                    return setTagUrl(split)\n                uriStr.contains(\"category\") ->//处理分类\n                    return setCategoryUrl(split)\n                uriStr.contains(\"common\") ->//处理公共RecyclerView界面\n                    return setCommonUrl(split)\n                else -> {\n                }\n            }\n\n\n        }\n        return uri\n    }\n\n\n    /**\n     * 处理作者详情url\n     * eyepetizer://pgc/detail/1065/?title=DELISH%20KITCHEN%20%E5%8F%AF%E5%8F%A3%E5%8E%A8%E6%88%BF&userType=PGC&tabIndex=1\n     * 替换为：\n     * eyepetizer://github.com/pgc/detail/?title=DELISH%20KITCHEN%20%E5%8F%AF%E5%8F%A3%E5%8E%A8%E6%88%BF&userType=PGC&tabIndex=1?id=1065\n     */\n    private fun setPageDetailUrl(split: List<String>): Uri {\n        //获取id\n        val pt = Pattern.compile(\"/\\\\d+/\")\n        val matcher = pt.matcher(split[1])\n        var id = 0\n        if (matcher.find()) {\n            id = matcher.group().replace(\"/\", \"\").toInt()\n        }\n        //替换/1065/为空字符串\n        val uriWithoutNumber = split[1].replace(Regex(\"/\\\\d+/\"), \"\")\n        return \"$HOST$uriWithoutNumber&id=$id\".toUri()\n    }\n\n    /**\n     * 处理webViewUrl\n     * eyepetizer://webview/?title=%E5%B9%BF%E5%9C%BA&url=http%3A%2F%2Fwww.kaiyanapp.com%2Fcampaign%2Ftag_square%2Ftag_square.html%3Fnid%3D1207%26tid%3D845%26cookie%3D%26udid%3Dd0f6190461864a3a978bdbcb3fe9b48709f1f390%26shareable%3Dtrue\n     * 替换为：eyepetizer://github.com/AndyJennifer/webview/?title=%E5%B9%BF%E5%9C%BA&url=http%3A%2F%2Fwww.kaiyanapp.com%2Fcampaign%2Ftag_square%2Ftag_square.html%3Fnid%3D1207%26tid%3D845%26cookie%3D%26udid%3Dd0f6190461864a3a978bdbcb3fe9b48709f1f390%26shareable%3Dtrue\n     */\n    private fun setWebViewUrl(split: List<String>): Uri {\n        return Uri.parse(\"$HOST$ONE_LEVEL${split[1]}\")\n    }\n\n    /**\n     * 处理webView点击跳转视频信息\n     * eyepetizer://detail/114760\n     * 替换为：eyepetizer://github.com/AndyJennifer/detail?id =114760\n     */\n    private fun setWebVideoUrl(split: List<String>): Uri {\n        //获取id\n        val pt = Pattern.compile(\"/\\\\d+\")\n        val matcher = pt.matcher(split[1])\n        var id = 0\n        if (matcher.find()) {\n            id = matcher.group().replace(\"/\", \"\").toInt()\n        }\n        //替换/1065/为空字符串\n        val uriWithoutNumber = split[1].replace(Regex(\"/\\\\d+\"), \"\")\n        return \"$HOST$ONE_LEVEL$uriWithoutNumber?id=$id\".toUri()\n    }\n\n    /**\n     * 处理排行榜\n     * eyepetizer://ranklist/\n     * 替换为：eyepetizer://github.com/AndyJennifer/ranklist\n     */\n    private fun setRankListUrl(split: List<String>): Uri {\n        val str = split[1].replace(\"/\", \"\")\n        return \"$HOST$ONE_LEVEL$str\".toUri()\n    }\n\n    /**\n     * 处理热门专题，\n     * eyepetizer://campaign/list/?title=%E4%B8%93%E9%A2%98\n     * 替换为：eyepetizer://github.com/campaign/list?title=%E4%B8%93%E9%A2%98\n     */\n    private fun setTopicUrl(split: List<String>): Uri {\n        val str = split[1].replace(\"/?\", \"?\")\n        return \"$HOST$str\".toUri()\n    }\n\n    /**\n     * 处理360全景\n     * eyepetizer://tag/658/?title=360%C2%B0%E5%85%A8%E6%99%AF\n     * 替换为：eyepetizer://github.com/AndyJennifer/tag?id=658&title=360%C2%B0%E5%85%A8%E6%99%AF\n     */\n    private fun setTagUrl(split: List<String>): Uri {\n        //获取id\n        val pt = Pattern.compile(\"/\\\\d+/\")\n        val matcher = pt.matcher(split[1])\n        var id = 0\n        if (matcher.find()) {\n            id = matcher.group().replace(\"/\", \"\").toInt()\n        }\n        //替换/658/为空字符串\n        val uriWithoutNumber = split[1].replace(Regex(\"/\\\\d+/\"), \"\")\n        return \"$HOST$ONE_LEVEL$uriWithoutNumber&id=$id\".toUri()\n    }\n\n    /**\n     * 处理360全景下面的所有的种类\n     * eyepetizer://category/14/?title=%E5%B9%BF%E5%91%8A\n     * 替换为：eyepetizer://github.com/AndyJennifer/category?id=14%title=%E5%B9%BF%E5%91%8A\n     */\n    private fun setCategoryUrl(split: List<String>): Uri {\n        val pt = Pattern.compile(\"/\\\\d+/\")\n        val matcher = pt.matcher(split[1])\n        var id = 0\n        if (matcher.find()) {\n            id = matcher.group().replace(\"/\", \"\").toInt()\n        }\n        val uriWithoutNumber = split[1].replace(Regex(\"/\\\\d+/\"), \"\")\n        return \"$HOST$ONE_LEVEL$uriWithoutNumber&id=$id\".toUri()\n    }\n\n    /**\n     * 处理公共界面中包含RecyclerVie的\n     * eyepetizer://common/?title=VLOG&url=http%3A%2F%2Fbaobab.kaiyanapp.com%2Fapi%2Fv4%2Fplaylists%2F3946%2Fvideos\n     * 替换为：eyepetizer://github.com/AndyJennifer/common?title=VLOG&url=http%3A%2F%2Fbaobab.kaiyanapp.com%2Fapi%2Fv4%2Fplaylists%2F3946%2Fvideos\n     */\n    private fun setCommonUrl(split: List<String>): Uri {\n        val str = split[1].replace(\"/?\", \"?\")\n        return \"$HOST$ONE_LEVEL$str\".toUri()\n    }\n\n    override fun init(context: Context?) {\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/router/RouteIntercept.kt",
    "content": "package com.jennifer.andy.simpleeyes.router\n\nimport android.content.Context\nimport com.alibaba.android.arouter.facade.Postcard\nimport com.alibaba.android.arouter.facade.annotation.Interceptor\nimport com.alibaba.android.arouter.facade.callback.InterceptorCallback\nimport com.alibaba.android.arouter.facade.template.IInterceptor\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.jennifer.andy.simpleeyes.UserPreferences\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019/1/14 19:04\n * Description: 1.登录拦截,判断用户是否登录\n */\n@Interceptor(priority = 8, name = \"登录拦截器\")\nclass RouteIntercept : IInterceptor {\n\n    companion object {\n        const val SHOULD_LOGIN = 1 ushr 30\n    }\n\n\n    override fun process(postcard: Postcard, callback: InterceptorCallback) {\n        when (postcard.extra) {\n            SHOULD_LOGIN -> judgeUserIsLogin(postcard, callback)\n            else -> {\n                callback.onContinue(postcard)\n            }\n        }\n    }\n\n    /**\n     * 判断用户是否登录，如果没有登录则显示登录界面\n     */\n    private fun judgeUserIsLogin(postcard: Postcard, callback: InterceptorCallback) {\n        val userIsLogin = UserPreferences.getUserIsLogin()\n        if (!userIsLogin) //如果用户没登录，直接跳转到登录界面\n            ARouter.getInstance().build(\"/github/Login\").navigation()\n        else\n            callback.onContinue(postcard)\n\n    }\n\n    override fun init(context: Context?) {\n\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/router/SchemeFilterActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.router\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport com.alibaba.android.arouter.facade.Postcard\nimport com.alibaba.android.arouter.facade.callback.NavCallback\nimport com.alibaba.android.arouter.launcher.ARouter\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/19 19:57\n * Description: 中间跳转页\n */\nclass SchemeFilterActivity : AppCompatActivity() {\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        val uri = intent.data\n        ARouter.getInstance().build(uri).navigation(this, object : NavCallback() {\n            override fun onArrival(postcard: Postcard?) {\n                finish()\n            }\n        })\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/rx/RxBus.kt",
    "content": "package com.jennifer.andy.simpleeyes.rx\n\nimport io.reactivex.Observable\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactivex.disposables.CompositeDisposable\nimport io.reactivex.disposables.Disposable\nimport io.reactivex.functions.Consumer\nimport io.reactivex.schedulers.Schedulers\nimport io.reactivex.subjects.PublishSubject\nimport java.util.concurrent.ConcurrentHashMap\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/2/12 14:06\n * Description: 参考地址：https://mcxiaoke.gitbooks.io/rxdocs/content/Subject.html\n */\n\nobject RxBus {\n\n    private val mBus = PublishSubject.create<Any>().toSerialized()\n    private val mSubjects = ConcurrentHashMap<String, CompositeDisposable>()\n\n    /**\n     * 发送一个事件\n     */\n    @JvmStatic\n    fun post(o: Any) {\n        mBus.onNext(o)\n    }\n\n    /**\n     * 根据传递的 eventType 类型返回特定类型的(eventType)的 被观察者\n     */\n    private fun <T> toObservable(eventType: Class<T>): Observable<T> {\n        return mBus.ofType(eventType)\n    }\n\n\n    /**\n     * 订阅\n     *\n     * @param eventType 接受的事件类型\n     * @param action    处理方法\n     * @param error  错误处理方法\n     * @param <T>    接受事件的泛型\n     * @return\n     */\n    private fun <T> doSubscribe(eventType: Class<T>, action: Consumer<T>, error: Consumer<Throwable>): Disposable {\n        return toObservable(eventType)\n                .subscribeOn(Schedulers.io())\n                .observeOn(AndroidSchedulers.mainThread())\n                .subscribe(action, error)\n    }\n\n    /**\n     * 保存订阅后的disposable\n     */\n    private fun addDisposable(any: Any, disposable: Disposable) {\n        val key = any.javaClass.name\n        if (mSubjects[key] != null) {\n            mSubjects[key]?.add(disposable)\n        } else {\n            val disposables = CompositeDisposable()\n            disposables.add(disposable)\n            mSubjects[key] = disposables\n        }\n    }\n\n    /**\n     * 处理注册事件\n     *\n     * @param event     事件\n     * @param action    处理方式\n     * @param subscribe 订阅者\n     * @param <T>       事件类型泛型\n     */\n    @JvmStatic\n    fun <T> register(subscribe: Any, event: Class<T>, action: Consumer<T>) {\n        val disposable = doSubscribe(event, action, Consumer { throwable -> throw RuntimeException(throwable.message) })\n        addDisposable(subscribe, disposable)\n    }\n\n\n    /**\n     * 注销监听事件\n     *\n     * @param subscribe 订阅者\n     */\n    @JvmStatic\n    fun unRegister(subscribe: Any) {\n        val key = subscribe.javaClass.name\n        if (mSubjects.containsKey(key) && mSubjects[key] != null) {\n            mSubjects[key]?.dispose()\n        }\n        mSubjects.remove(key)\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/rx/RxThreadHelper.kt",
    "content": "package com.jennifer.andy.simpleeyes.rx\n\nimport io.reactivex.FlowableTransformer\nimport io.reactivex.MaybeTransformer\nimport io.reactivex.ObservableTransformer\nimport io.reactivex.SingleTransformer\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactivex.schedulers.Schedulers\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/10/24 10:42\n * Description:网络请求帮助类处理。默认将观察者的方法切换到主线程中运行\n */\n\nobject RxThreadHelper {\n\n\n    /**\n     * 将Observable类型的观察者切换到主线程中运行\n     */\n    fun <T> switchObservableThread(): ObservableTransformer<T, T> {\n        return ObservableTransformer {\n            it.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())\n        }\n    }\n\n    /**\n     * 将Flowable类型的观察者切换到主线程中运行\n     */\n    fun <T> switchFlowableThread(): FlowableTransformer<T, T> {\n        return FlowableTransformer {\n            it.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())\n        }\n    }\n\n    /**\n     * 将Single类型的观察者切换到主线程中运行\n     */\n    fun <T> switchSingleThread(): SingleTransformer<T, T> {\n        return SingleTransformer {\n            it.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())\n        }\n    }\n\n\n    /**\n     * 将Maybe类型的观察者切换到主线程中运行\n     */\n    fun <T> switchMaybeThread(): MaybeTransformer<T, T> {\n        return MaybeTransformer {\n            it.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/rx/error/FlowableRetryDelay.kt",
    "content": "package com.jennifer.andy.simpleeyes.rx.error\n\nimport io.reactivex.Flowable\nimport io.reactivex.functions.Function\nimport org.reactivestreams.Publisher\nimport java.util.concurrent.TimeUnit\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-11-17 21:24\n * Description:该类逻辑与[ObservableRetryDelay]一样\n */\n\nclass FlowableRetryDelay(\n        val retryConfigProvider: (Throwable) -> RetryConfig\n) : Function<Flowable<Throwable>, Publisher<*>> {\n\n    private var retryCount: Int = 0\n\n    override fun apply(throwableFlowable: Flowable<Throwable>): Publisher<*> {\n        return throwableFlowable\n                .flatMap { error ->\n                    val (maxRetries, delay, retryTransform) = retryConfigProvider(error)\n\n                    if (++retryCount <= maxRetries) {\n                        retryTransform()\n                                .flatMapPublisher { retry ->\n                                    if (retry)\n                                        Flowable.timer(delay.toLong(), TimeUnit.MILLISECONDS)\n                                    else\n                                        Flowable.error<Any>(error)\n                                }\n                    } else Flowable.error<Any>(error)\n                }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/rx/error/GlobalErrorProcessor.kt",
    "content": "package com.jennifer.andy.simpleeyes.rx.error\n\nimport com.jennifer.andy.simpleeyes.utils.toast\nimport retrofit2.HttpException\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-11-17 22:04\n * Description:\n */\n\nfun <T> globalHandleError(): GlobalErrorTransformer<T> = GlobalErrorTransformer(\n        globalDoOnErrorConsumer = { error ->\n            when (error) {\n                is HttpException -> {\n                    when (error.code()) {\n                        401 -> toast { \"401 Unauthorized\" }\n                        404 -> toast { \"404 failure\" }\n                        500 -> toast { \"500 server failure\" }\n                        else -> toast { \"network failure\" }\n                    }\n                }\n                else -> {\n                    toast { error.message.toString() }\n                }\n            }\n        }\n)"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/rx/error/GlobalErrorTransformer.kt",
    "content": "package com.jennifer.andy.simpleeyes.rx.error\n\nimport io.reactivex.*\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-11-17 21:22\n * Description: RxJava全局错误处理，包括：\n * 1.全局的onNext拦截器[globalOnNextInterceptor]\n * 2.全局的错误重试处理器[globalOnErrorResume]，该属性用于当发生错误时，重新发送新的Observable\n * 3.全局重试配置参数[RetryConfig]，该属性用于配置重试次数以及重试的时间,是否需要重试，需要通过内部的condition进行判断\n * 4.全局的错误处理器[globalDoOnErrorConsumer]，该属性会在观察者的onError方法之前运行\n */\n\nclass GlobalErrorTransformer<T> constructor(\n        private val globalOnNextInterceptor: (T) -> Observable<T> = { Observable.just(it) },\n        private val globalOnErrorResume: (Throwable) -> Observable<T> = { Observable.error(it) },\n        private val retryConfigProvider: (Throwable) -> RetryConfig = { RetryConfig() },\n        private val globalDoOnErrorConsumer: (Throwable) -> Unit = { }\n) : ObservableTransformer<T, T>,\n        FlowableTransformer<T, T>,\n        SingleTransformer<T, T>,\n        MaybeTransformer<T, T>,\n        CompletableTransformer {\n\n    override fun apply(upstream: Observable<T>): Observable<T> =\n            upstream\n                    .flatMap {\n                        globalOnNextInterceptor(it)\n                    }\n                    .onErrorResumeNext { throwable: Throwable ->\n                        globalOnErrorResume(throwable)\n                    }\n                    .retryWhen(ObservableRetryDelay(retryConfigProvider))\n                    .doOnError(globalDoOnErrorConsumer)\n\n    override fun apply(upstream: Completable): Completable =\n            upstream\n                    .onErrorResumeNext {\n                        globalOnErrorResume(it).ignoreElements()\n                    }\n                    .retryWhen(FlowableRetryDelay(retryConfigProvider))\n                    .doOnError(globalDoOnErrorConsumer)\n\n    override fun apply(upstream: Flowable<T>): Flowable<T> =\n            upstream\n                    .flatMap {\n                        globalOnNextInterceptor(it)\n                                .toFlowable(BackpressureStrategy.BUFFER)\n                    }\n                    .onErrorResumeNext { throwable: Throwable ->\n                        globalOnErrorResume(throwable)\n                                .toFlowable(BackpressureStrategy.BUFFER)\n                    }\n                    .retryWhen(FlowableRetryDelay(retryConfigProvider))\n                    .doOnError(globalDoOnErrorConsumer)\n\n    override fun apply(upstream: Maybe<T>): Maybe<T> =\n            upstream\n                    .flatMap {\n                        globalOnNextInterceptor(it)\n                                .firstElement()\n                    }\n                    .onErrorResumeNext { throwable: Throwable ->\n                        globalOnErrorResume(throwable)\n                                .firstElement()\n                    }\n                    .retryWhen(FlowableRetryDelay(retryConfigProvider))\n                    .doOnError(globalDoOnErrorConsumer)\n\n    override fun apply(upstream: Single<T>): Single<T> =\n            upstream\n                    .flatMap {\n                        globalOnNextInterceptor(it)\n                                .firstOrError()\n                    }\n                    .onErrorResumeNext { throwable ->\n                        globalOnErrorResume(throwable)\n                                .firstOrError()\n                    }\n                    .retryWhen(FlowableRetryDelay(retryConfigProvider))\n                    .doOnError(globalDoOnErrorConsumer)\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/rx/error/ObservabeRetryDelay.kt",
    "content": "package com.jennifer.andy.simpleeyes.rx.error\n\nimport io.reactivex.Observable\nimport io.reactivex.ObservableSource\nimport io.reactivex.functions.Function\nimport java.util.concurrent.TimeUnit\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-11-17 21:27\n * Description:该类会获取配置的重试参数。判断当前重试的次数是否大于配置的重试次数，如果比配置的小，那么会判断重试\n * 的条件，如果为true,那么就会重试，反之直接发送Error信息\n */\n\nclass ObservableRetryDelay(\n        val retryConfigProvider: (Throwable) -> RetryConfig\n) : Function<Observable<Throwable>, ObservableSource<*>> {\n\n    private var retryCount: Int = 0\n\n    override fun apply(throwableObs: Observable<Throwable>): ObservableSource<*> {\n        return throwableObs\n                .flatMap { error ->\n                    val (maxRetries, delay, retryCondition) = retryConfigProvider(error)\n\n                    if (++retryCount <= maxRetries) {\n                        retryCondition().flatMapObservable { retry ->\n                            if (retry)\n                                Observable.timer(delay.toLong(), TimeUnit.MILLISECONDS)\n                            else\n                                Observable.error<Any>(error)\n                        }\n                    } else Observable.error<Any>(error)\n                }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/rx/error/RetryConfig.kt",
    "content": "package com.jennifer.andy.simpleeyes.rx.error\n\nimport io.reactivex.Single\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-11-17 21:12\n * Description: 重试配置，默认情况下不会进行重试，如果需要重试配置，需要重写condition\n */\n\nprivate const val DEFAULT_RETRY_TIMES = 1 //默认重试次数 1次\nprivate const val DEFAULT_DELAY_DURATION = 1000 //默认推迟时间为1秒\n\ndata class RetryConfig(val maxRetries: Int = DEFAULT_RETRY_TIMES,\n                       val delay: Int = DEFAULT_DELAY_DURATION,\n                       val condition: () -> Single<Boolean> = { Single.just(false) })\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/MainActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui\n\nimport android.os.Bundle\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.ui.base.BaseAppCompatActivity\nimport com.jennifer.andy.simpleeyes.ui.feed.FeedFragment\nimport com.jennifer.andy.simpleeyes.ui.follow.FollowFragment\nimport com.jennifer.andy.simpleeyes.ui.home.HomeFragment\nimport com.jennifer.andy.simpleeyes.ui.profile.ProfileFragment\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.BottomBar\nimport com.jennifer.andy.simpleeyes.widget.BottomItem\nimport me.yokeyword.fragmentation.SupportFragment\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/19 18:54\n * Description:主界面\n */\n\nclass MainActivity : BaseAppCompatActivity() {\n\n\n    private var mFragments = arrayOfNulls<SupportFragment>(4)\n    private val mBottomNavigation: BottomBar by bindView(R.id.bottom_navigation_bar)\n\n    companion object {\n        private const val FIRST = 0\n        private const val SECOND = 1\n        private const val THIRD = 2\n        private const val FOURTH = 3\n    }\n\n\n    override fun initView(savedInstanceState: Bundle?) {\n        mFragments[FIRST] = HomeFragment.newInstance()\n        mFragments[SECOND] = FeedFragment.newInstance()\n        mFragments[THIRD] = FollowFragment.newInstance()\n        mFragments[FOURTH] = ProfileFragment.newInstance()\n        loadMultipleRootFragment(R.id.fl_container, FIRST, *mFragments)\n        initBottomNavigation()\n    }\n\n    private fun initBottomNavigation() {\n\n        val home = BottomItem(R.drawable.ic_tab_strip_icon_feed_selected, getString(R.string.home))\n        home.setUnSelectedDrawable(R.drawable.ic_tab_strip_icon_feed)\n        val discover = BottomItem(R.drawable.ic_tab_strip_icon_category_selected, getString(R.string.discover))\n        discover.setUnSelectedDrawable(R.drawable.ic_tab_strip_icon_category)\n        val focus = BottomItem(R.drawable.ic_tab_strip_icon_follow_selected, getString(R.string.focus))\n        focus.setUnSelectedDrawable(R.drawable.ic_tab_strip_icon_follow)\n        val mine = BottomItem(R.drawable.ic_tab_strip_icon_profile_selected, getString(R.string.mine))\n        mine.setUnSelectedDrawable(R.drawable.ic_tab_strip_icon_profile)\n\n        with(mBottomNavigation) {\n            addItem(home)\n            addItem(discover)\n            addItem(focus)\n            addItem(mine)\n            initialise()\n            setOnTabSelectedListener(object : BottomBar.TabSelectedListener {\n                override fun onTabSelected(position: Int, prePosition: Int) {\n                    showHideFragment(mFragments[position])\n                }\n\n                override fun onTabUnselected(position: Int) {\n\n\n                }\n\n                override fun onTabReselected(position: Int) {\n                    if (position == FIRST) {\n                        val categoryFragment = mFragments[FIRST] as HomeFragment\n                        categoryFragment.scrollToTop()\n                    }\n                }\n            })\n\n        }\n\n    }\n\n    override fun getContentViewLayoutId() = R.layout.activity_main\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/author/AuthorTagDetailActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.author\n\nimport android.animation.ArgbEvaluator\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.widget.ImageView\nimport android.widget.RelativeLayout\nimport androidx.fragment.app.Fragment\nimport androidx.viewpager.widget.ViewPager\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.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.Tab\nimport com.jennifer.andy.simpleeyes.entity.TabInfo\nimport com.jennifer.andy.simpleeyes.net.Extras\nimport com.jennifer.andy.simpleeyes.ui.author.presenter.AuthorTagDetailPresenter\nimport com.jennifer.andy.simpleeyes.ui.author.ui.AuthorTagDetailView\nimport com.jennifer.andy.simpleeyes.ui.base.BaseActivity\nimport com.jennifer.andy.simpleeyes.ui.base.BaseFragmentItemAdapter\nimport com.jennifer.andy.simpleeyes.ui.feed.TagDetailInfoFragment\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.utils.showKeyboard\nimport com.jennifer.andy.simpleeyes.widget.StickyNavLayout\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\nimport com.jennifer.andy.simpleeyes.widget.font.FontType\nimport com.jennifer.andy.simpleeyes.widget.tab.ShortTabLayout\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-07-13 22:38\n * Description: 作者详细信息界面\n */\n\n@Route(path = \"/pgc/detail\")\nclass AuthorTagDetailActivity : BaseActivity<AuthorTagDetailView, AuthorTagDetailPresenter>(), AuthorTagDetailView {\n\n    private val mToolbar: RelativeLayout by bindView(R.id.tool_bar)\n    private val mTvTitle: CustomFontTextView by bindView(R.id.tv_title)\n    private val mStickyNavLayout: StickyNavLayout by bindView(R.id.stick_layout)\n    private val mViewPager: ViewPager by bindView(R.id.id_sticky_nav_layout_viewpager)\n    private val mTabLayout: ShortTabLayout by bindView(R.id.id_sticky_nav_layout_nav_view)\n\n    private val mTvName: CustomFontTextView by bindView(R.id.tv_name)\n    private val mTvDesc: CustomFontTextView by bindView(R.id.tv_desc)\n    private val mTvBrief: CustomFontTextView by bindView(R.id.tv_brief)\n    private val mIvHead: SimpleDraweeView by bindView(R.id.iv_head)\n\n    @Autowired\n    @JvmField\n    var tabIndex: String? = null\n\n    @Autowired\n    @JvmField\n    var title: String? = null\n\n    @Autowired\n    @JvmField\n    var id: String? = null\n\n\n    override fun getBundleExtras(extras: Bundle) {\n        with(extras) {\n            tabIndex = getString(Extras.TAB_INDEX)\n            title = getString(Extras.TITLE)\n            id = getString(Extras.ID)\n        }\n    }\n\n    override fun initView(savedInstanceState: Bundle?) {\n        ARouter.getInstance().inject(this)\n        initToolBar(title, 0f)\n        mPresenter.getAuthorTagDetail(id!!)\n\n    }\n\n\n    override fun loadInfoSuccess(tab: Tab) {\n\n        mViewPager.adapter = BaseFragmentItemAdapter(supportFragmentManager, initFragments(tab.tabInfo), initTitles(tab.tabInfo))\n        mTabLayout.setupWithViewPager(mViewPager)\n        mViewPager.currentItem = tabIndex?.toInt() ?: 0\n\n        mStickyNavLayout.setScrollChangeListener(object : StickyNavLayout.ScrollChangeListener {\n            override fun onScroll(moveRatio: Float) {\n                initToolBar(title, moveRatio)\n            }\n        })\n\n        //设置topView信息\n        with(tab.pgcInfo) {\n            mTvName.text = name\n            mTvDesc.text = description\n            mTvBrief.text = brief\n            mIvHead.setImageURI(icon)\n        }\n\n\n    }\n\n    private fun initFragments(tabInfo: TabInfo): MutableList<Fragment> {\n        val fragments = mutableListOf<Fragment>()\n        for (i in tabInfo.tabList.indices) {\n            fragments.add(TagDetailInfoFragment.newInstance(tabInfo.tabList[i].apiUrl))\n        }\n        return fragments\n    }\n\n    private fun initTitles(tabInfo: TabInfo): MutableList<String> {\n        val titles = mutableListOf<String>()\n        for (i in tabInfo.tabList.indices) {\n            titles.add(tabInfo.tabList[i].name)\n        }\n        return titles\n    }\n\n    private fun initToolBar(title: String? = null, titleAlpha: Float = 1f) {\n        findViewById<ImageView>(R.id.iv_back).setOnClickListener {\n            showKeyboard(false)\n            finish()\n        }\n        //设置背景渐变\n        val color = ArgbEvaluator().evaluate(titleAlpha, 0x00FFFFFF, Color.WHITE) as Int\n        mToolbar.setBackgroundColor(color)\n\n        mTvTitle.apply {\n            setFontType(fontType = FontType.BOLD)\n            text = title\n            alpha = titleAlpha\n        }\n\n    }\n\n    override fun getContentViewLayoutId() = R.layout.activity_author_tag_detail\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/author/model/AuthorModel.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.author.model\n\nimport com.jennifer.andy.simpleeyes.entity.Tab\nimport com.jennifer.andy.simpleeyes.net.Api\nimport com.jennifer.andy.simpleeyes.rx.RxThreadHelper\nimport com.jennifer.andy.simpleeyes.rx.error.globalHandleError\nimport com.jennifer.andy.simpleeyes.ui.base.model.BaseModel\nimport io.reactivex.Observable\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-07-13 22:34\n * Description:\n */\n\nclass AuthorModel : BaseModel {\n\n    /**\n     * 获取作者\n     */\n    fun getAuthorTagDetail(id: String): Observable<Tab> =\n            Api.getDefault()\n                    .getAuthorTagDetail(id)\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/author/presenter/AuthorTagDetailPresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.author.presenter\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.ui.author.model.AuthorModel\nimport com.jennifer.andy.simpleeyes.ui.author.ui.AuthorTagDetailView\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-07-13 22:40\n * Description:\n */\n\nclass AuthorTagDetailPresenter : BasePresenter<AuthorTagDetailView>() {\n\n    private var mAuthorModel: AuthorModel = AuthorModel()\n\n    fun getAuthorTagDetail(id: String) {\n        mAuthorModel.getAuthorTagDetail(id).autoDispose(mScopeProvider).subscribe({\n            mView?.showContent()\n            mView?.loadInfoSuccess(it)\n        }, {\n            mView?.showNetError(View.OnClickListener { getAuthorTagDetail(id) })\n        })\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/author/ui/AuthorTagDetailView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.author.ui\n\nimport com.jennifer.andy.simpleeyes.entity.Tab\nimport com.jennifer.andy.simpleeyes.ui.base.BaseView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-07-13 22:39\n * Description:\n */\n\ninterface AuthorTagDetailView : BaseView {\n    fun loadInfoSuccess(tab: Tab)\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/base/BaseActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.base\n\nimport android.os.Bundle\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport com.jennifer.andy.simpleeyes.utils.getGenericInstance\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/5 19:04\n * Description: 基础类activity\n */\n\n@Suppress(\"UNCHECKED_CAST\")\nabstract class BaseActivity<V, T : BasePresenter<V>> : BaseAppCompatActivity(), BaseView {\n\n\n    protected lateinit var mPresenter: T\n\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        mPresenter = getGenericInstance(this, 1)\n        lifecycle.addObserver(mPresenter)\n        mPresenter.attachView(this as V, this)\n        super.onCreate(savedInstanceState)\n    }\n\n    override fun showLoading() {\n        mMultipleStateView.showLoading()\n    }\n\n    override fun showNetError(onClickListener: View.OnClickListener) {\n        mMultipleStateView.showNetError(onClickListener)\n    }\n\n    override fun showEmpty(onClickListener: View.OnClickListener) {\n        mMultipleStateView.showEmpty(onClickListener)\n    }\n\n    override fun showContent() {\n        mMultipleStateView.showContent()\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/base/BaseAppCompatActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.base\n\nimport android.content.Context\nimport android.os.Bundle\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.ImageView\nimport androidx.annotation.StringRes\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.manager.ActivityManager\nimport com.jennifer.andy.simpleeyes.utils.showKeyboard\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\nimport com.jennifer.andy.simpleeyes.widget.font.FontType\nimport com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\nimport me.yokeyword.fragmentation.SupportActivity\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/8/10 22:37\n * Description:\n */\n\nabstract class BaseAppCompatActivity : SupportActivity() {\n\n    /**\n     * 上下文对象\n     */\n    protected lateinit var mContext: Context\n    protected lateinit var mMultipleStateView: MultipleStateView\n\n\n    /**\n     * 跳转到其他Activity启动或者退出的模式\n     */\n    enum class TransitionMode {\n        LEFT, RIGHT, TOP, BOTTOM, SCALE, FADE\n    }\n\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        initData()\n        initView(savedInstanceState)\n    }\n\n    override fun onStart() {\n        super.onStart()\n        ActivityManager.getInstance().addActivity(this)\n    }\n\n    private fun initData() {\n        overrideTransitionAnimation()\n        val extras = intent.extras\n        if (extras != null) {\n            getBundleExtras(extras)\n        }\n        mContext = this\n        //添加相应的布局\n        if (getContentViewLayoutId() != 0) {\n            mMultipleStateView = MultipleStateView(this)\n            val view = View.inflate(this, getContentViewLayoutId(), mMultipleStateView)\n            setContentView(view)\n        } else {\n            throw  IllegalArgumentException(\"You must return layout id\")\n        }\n\n    }\n\n    /**\n     * 设置activity进入动画\n     */\n    private fun overrideTransitionAnimation() {\n        if (toggleOverridePendingTransition()) {\n            when (getOverridePendingTransition()) {\n                TransitionMode.TOP -> overridePendingTransition(R.anim.top_in, R.anim.no_anim)\n                TransitionMode.BOTTOM -> overridePendingTransition(R.anim.bottom_in, R.anim.no_anim)\n                TransitionMode.LEFT -> overridePendingTransition(R.anim.left_in, R.anim.no_anim)\n                TransitionMode.RIGHT -> overridePendingTransition(R.anim.right_in, R.anim.no_anim)\n                TransitionMode.FADE -> overridePendingTransition(R.anim.fade_in, R.anim.no_anim)\n                TransitionMode.SCALE -> overridePendingTransition(R.anim.scale_in, R.anim.no_anim)\n            }\n        }\n    }\n\n    override fun finish() {\n        super.finish()\n        if (toggleOverridePendingTransition()) {\n            when (getOverridePendingTransition()) {\n                TransitionMode.TOP -> overridePendingTransition(0, R.anim.top_out)\n                TransitionMode.BOTTOM -> overridePendingTransition(0, R.anim.bottom_out)\n                TransitionMode.LEFT -> overridePendingTransition(0, R.anim.left_out)\n                TransitionMode.RIGHT -> overridePendingTransition(0, R.anim.right_out)\n                TransitionMode.FADE -> overridePendingTransition(0, R.anim.fade_out)\n                TransitionMode.SCALE -> overridePendingTransition(0, R.anim.scale_out)\n            }\n        }\n\n    }\n\n    override fun onStop() {\n        super.onStop()\n        ActivityManager.getInstance().removeActivity(this)\n    }\n\n\n    /**\n     * 初始化工具栏,默认情况下加粗\n     */\n    protected fun initToolBar(toolbar: ViewGroup, title: String? = null, fontType: FontType = FontType.BOLD) {\n        val ivBack = toolbar.findViewById<ImageView>(R.id.iv_back)\n        ivBack.setOnClickListener {\n            showKeyboard(false)\n            finish()\n        }\n\n        val tvTitle = toolbar.findViewById<CustomFontTextView>(R.id.tv_title)\n        tvTitle.setFontType(fontType)\n        tvTitle.text = title\n\n    }\n\n    /**\n     * 初始化工具栏，默认情况下加粗\n     */\n    protected fun initToolBar(toolbar: ViewGroup, @StringRes id: Int? = null, fontType: FontType = FontType.BOLD) {\n        val ivBack = toolbar.findViewById<ImageView>(R.id.iv_back)\n        ivBack.setOnClickListener {\n            showKeyboard(false)\n            finish()\n        }\n\n        val tvTitle = toolbar.findViewById<CustomFontTextView>(R.id.tv_title)\n        tvTitle.setFontType(fontType)\n        tvTitle.setText(id!!)\n\n    }\n\n\n    abstract fun initView(savedInstanceState: Bundle?)\n\n    /**\n     *  获取bundle 中的数据\n     */\n    open fun getBundleExtras(extras: Bundle) {}\n\n\n    /**\n     * 是否有切换动画\n     */\n    protected open fun toggleOverridePendingTransition() = false\n\n    /**\n     * 获得切换动画的模式\n     */\n    protected open fun getOverridePendingTransition(): TransitionMode? = null\n\n    /**\n     * 获取当前布局id\n     */\n    abstract fun getContentViewLayoutId(): Int\n\n\n}\n\n\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/base/BaseAppCompatFragment.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.base\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\nimport me.yokeyword.fragmentation.SupportFragment\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/5 19:06\n * Description:\n */\n\nabstract class BaseAppCompatFragment : SupportFragment() {\n\n    protected lateinit var LOG_TAG: String\n    protected lateinit var mMultipleStateView: MultipleStateView\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        LOG_TAG = this.javaClass.simpleName\n        if (arguments != null) {\n            getBundleExtras(arguments!!)\n        }\n    }\n\n\n    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {\n        return if (getContentViewLayoutId() != 0) {\n            mMultipleStateView = MultipleStateView(context!!)\n            return View.inflate(context, getContentViewLayoutId(), mMultipleStateView)\n        } else {\n            super.onCreateView(inflater, container, savedInstanceState)\n        }\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        initView(savedInstanceState)\n    }\n\n\n    /**\n     * 获取bundle中相应data\n     */\n    open fun getBundleExtras(extras: Bundle) {}\n\n    /**\n     * 获取资源id\n     */\n    abstract fun getContentViewLayoutId(): Int\n\n    /**\n     * 初始化view\n     */\n    open fun initView(savedInstanceState: Bundle?) {\n\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/base/BaseFragment.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.base\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport com.jennifer.andy.simpleeyes.utils.getGenericInstance\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/5 19:06\n * Description:\n */\n\n@Suppress(\"UNCHECKED_CAST\")\nabstract class BaseFragment<V, T : BasePresenter<V>> : BaseAppCompatFragment(), BaseView {\n\n    protected lateinit var mPresenter: T\n\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        mPresenter = getGenericInstance(this, 1)\n    }\n\n\n    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {\n        mPresenter.attachView(this as V, this)\n        return super.onCreateView(inflater, container, savedInstanceState)\n    }\n\n    override fun showLoading() {\n        mMultipleStateView.showLoading()\n    }\n\n    override fun showNetError(onClickListener: View.OnClickListener) {\n        mMultipleStateView.showNetError(onClickListener)\n    }\n\n    override fun showEmpty(onClickListener: View.OnClickListener) {\n        mMultipleStateView.showEmpty(onClickListener)\n    }\n\n    override fun showContent() {\n        mMultipleStateView.showContent()\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/base/BaseFragmentItemAdapter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.base\n\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentManager\nimport androidx.fragment.app.FragmentPagerAdapter\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/4 13:59\n * Description:基础FragmentPagerAdapter适配器\n */\n\nopen class BaseFragmentItemAdapter(fragmentManager: FragmentManager,\n                                   private val fragments: MutableList<Fragment>,\n                                   private val titles: MutableList<String>) : FragmentPagerAdapter(fragmentManager) {\n\n    override fun getItem(position: Int) = fragments[position]\n\n    override fun getCount() = fragments.size\n\n    override fun getPageTitle(position: Int) = titles[position]\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/base/BaseView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.base\n\nimport android.view.View\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/5 19:09\n * Description:基础view\n */\n\ninterface BaseView {\n    /**\n     * 显示正在加载\n     */\n    fun showLoading()\n\n    /**\n     * 显示内容\n     */\n    fun showContent()\n\n    /**\n     * 显示网络异常\n     */\n    fun showNetError(onClickListener: View.OnClickListener)\n\n    /**\n     * 显示空界面\n     */\n    fun showEmpty(onClickListener: View.OnClickListener)\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/base/LoadMoreView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.base\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/6/30 01:50\n * Description:\n */\ninterface LoadMoreView<T> : BaseView {\n\n    /**\n     * 加载更多信息成功\n     */\n    fun loadMoreSuccess(data: T)\n\n    /**\n     * 没有更多\n     */\n    fun showNoMore()\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/base/adapter/BaseDataAdapter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.base.adapter\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.text.TextUtils\nimport android.view.Gravity\nimport android.view.View\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.chad.library.adapter.base.BaseQuickAdapter\nimport com.chad.library.adapter.base.BaseQuickAdapter.OnItemClickListener\nimport com.chad.library.adapter.base.BaseViewHolder\nimport com.chad.library.adapter.base.util.MultiTypeDelegate\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.entity.ContentBean\nimport com.jennifer.andy.simpleeyes.ui.home.adapter.CollectionCardCoverAdapter\nimport com.jennifer.andy.simpleeyes.ui.home.adapter.SquareCollectionAdapter\nimport com.jennifer.andy.simpleeyes.ui.search.adapter.CollectionBriefAdapter\nimport com.jennifer.andy.simpleeyes.ui.video.VideoDetailActivity\nimport com.jennifer.andy.simpleeyes.utils.dip2px\nimport com.jennifer.andy.simpleeyes.utils.getElapseTimeForShow\nimport com.jennifer.andy.simpleeyes.widget.EliteImageView\nimport com.jennifer.andy.simpleeyes.widget.ItemHeaderView\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\nimport com.jennifer.andy.simpleeyes.widget.font.FontType\nimport com.jennifer.andy.simpleeyes.widget.image.imageloader.FrescoImageLoader\nimport com.jennifer.andy.simpleeyes.widget.viewpager.MarginWithIndicatorViewPager\nimport com.youth.banner.Banner\nimport com.youth.banner.BannerConfig\nimport java.util.*\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/10/27 16:35\n * Description: 基础信息适配器\n */\n\nopen class BaseDataAdapter(data: MutableList<Content>) : BaseQuickAdapter<Content, BaseViewHolder>(data) {\n\n\n    /**\n     * 卡片类型\n     */\n    companion object {\n        const val BANNER_TYPE = 0\n        const val FOLLOW_CARD_TYPE = 1\n        const val HORIZONTAL_SCROLL_CARD_TYPE = 2\n        const val VIDEO_COLLECTION_WITH_COVER_TYPE = 3\n        const val SQUARE_CARD_COLLECTION_TYPE = 4\n        const val VIDEO_COLLECTION_OF_HORIZONTAL_SCROLL_CARD_TYPE = 5\n        const val VIDEO_COLLECTION_WITH_BRIEF_TYPE = 6\n        const val TEXT_CARD_TYPE = 7\n        const val BRIEF_CARD_TYPE = 8\n        const val BLANK_CARD_TYPE = 9\n        const val SQUARE_CARD_TYPE = 10\n        const val RECTANGLE_CARD_TYPE = 11\n        const val VIDEO_TYPE = 12\n        const val VIDEO_BANNER_THREE_TYPE = 13\n        const val VIDEO_SMALL_CARD_TYPE = 14\n\n        const val BANNER = \"banner\"\n        const val FOLLOW_CARD = \"followCard\"\n        const val HORIZONTAL_CARD = \"horizontalScrollCard\"\n        const val VIDEO_COLLECTION_WITH_COVER = \"videoCollectionWithCover\"\n        const val SQUARE_CARD_COLLECTION = \"squareCardCollection\"\n        const val VIDEO_COLLECTION_OF_HORIZONTAL_SCROLL_CARD = \"videoCollectionOfHorizontalScrollCard\"\n        const val VIDEO_COLLECTION_WITH_BRIEF = \"videoCollectionWithBrief\"\n        const val TEXT_CARD = \"textCard\"\n        const val BRIEF_CARD = \"briefCard\"\n        const val BLANK_CARD = \"blankCard\"\n        const val SQUARE_CARD = \"squareCard\"\n        const val RECTANGLE_CARD = \"rectangleCard\"\n        const val VIDEO = \"video\"\n        const val VIDEO_BANNER_THREE = \"banner3\"\n        const val VIDEO_SMALL_CARD = \"videoSmallCard\"\n    }\n\n\n    init {\n        multiTypeDelegate = object : MultiTypeDelegate<Content>() {\n            override fun getItemType(andyInfoItem: Content?): Int {\n                when (andyInfoItem?.type) {\n                    BANNER -> return BANNER_TYPE\n                    FOLLOW_CARD -> return FOLLOW_CARD_TYPE\n                    HORIZONTAL_CARD -> return HORIZONTAL_SCROLL_CARD_TYPE\n                    VIDEO_COLLECTION_WITH_COVER -> return VIDEO_COLLECTION_WITH_COVER_TYPE\n                    SQUARE_CARD_COLLECTION -> return SQUARE_CARD_COLLECTION_TYPE\n                    VIDEO_COLLECTION_OF_HORIZONTAL_SCROLL_CARD -> return VIDEO_COLLECTION_OF_HORIZONTAL_SCROLL_CARD_TYPE\n                    VIDEO_COLLECTION_WITH_BRIEF -> return VIDEO_COLLECTION_WITH_BRIEF_TYPE\n                    TEXT_CARD -> return TEXT_CARD_TYPE\n                    BRIEF_CARD -> return BRIEF_CARD_TYPE\n                    BLANK_CARD -> return BLANK_CARD_TYPE\n                    SQUARE_CARD -> return SQUARE_CARD_TYPE\n                    RECTANGLE_CARD -> return RECTANGLE_CARD_TYPE\n                    VIDEO -> return VIDEO_TYPE\n                    VIDEO_BANNER_THREE -> return VIDEO_BANNER_THREE_TYPE\n                    VIDEO_SMALL_CARD -> return VIDEO_SMALL_CARD_TYPE\n\n                }\n                return FOLLOW_CARD_TYPE\n            }\n        }\n        with(multiTypeDelegate) {\n            registerItemType(BANNER_TYPE, R.layout.layout_card_banner)\n            registerItemType(FOLLOW_CARD_TYPE, R.layout.layout_follow_card)\n            registerItemType(HORIZONTAL_SCROLL_CARD_TYPE, R.layout.layout_horizontal_scroll_card)\n            registerItemType(VIDEO_COLLECTION_WITH_COVER_TYPE, R.layout.layout_collection_with_cover)\n            registerItemType(SQUARE_CARD_COLLECTION_TYPE, R.layout.layout_square_collection)\n            registerItemType(VIDEO_COLLECTION_OF_HORIZONTAL_SCROLL_CARD_TYPE, R.layout.item_collection_of_horizontal_scroll_card)\n            registerItemType(VIDEO_COLLECTION_WITH_BRIEF_TYPE, R.layout.layout_collection_with_brief)\n            registerItemType(TEXT_CARD_TYPE, R.layout.layout_single_text)\n            registerItemType(BRIEF_CARD_TYPE, R.layout.layout_brife_card)\n            registerItemType(BLANK_CARD_TYPE, R.layout.layout_blank_card)\n            registerItemType(SQUARE_CARD_TYPE, R.layout.item_square_card)\n            registerItemType(RECTANGLE_CARD_TYPE, R.layout.item_square_card)\n            registerItemType(VIDEO_TYPE, R.layout.layout_single_video)\n            registerItemType(VIDEO_BANNER_THREE_TYPE, R.layout.layout_follow_card)\n            registerItemType(VIDEO_SMALL_CARD_TYPE, R.layout.layout_video_small_card)\n        }\n        //设置倒数第5个开启预加载\n        setPreLoadNumber(5)\n    }\n\n    override fun convert(helper: BaseViewHolder?, item: Content) {\n        when (helper?.itemViewType) {\n            BANNER_TYPE -> setBannerInfo(helper, item)\n            FOLLOW_CARD_TYPE -> setFollowCardInfo(helper, item)\n            HORIZONTAL_SCROLL_CARD_TYPE -> setHorizontalScrollCardInfo(helper, item.data.itemList)\n            VIDEO_COLLECTION_WITH_COVER_TYPE -> setCollectionCardWithCoverInfo(helper, item.data)\n            SQUARE_CARD_COLLECTION_TYPE -> setSquareCollectionInfo(helper, item.data)\n            VIDEO_COLLECTION_OF_HORIZONTAL_SCROLL_CARD_TYPE -> setCollectionOfHorizontalScrollCardInfo(helper, item)\n            VIDEO_COLLECTION_WITH_BRIEF_TYPE -> setCollectionBriefInfo(helper, item)\n            TEXT_CARD_TYPE -> setSingleText(helper, item)\n            BRIEF_CARD_TYPE -> setBriefCardInfo(helper, item)\n            BLANK_CARD_TYPE -> setBlankCardInfo(helper, item)\n            SQUARE_CARD_TYPE -> setSquareCardInfo(helper, item.data)\n            RECTANGLE_CARD_TYPE -> setRectangleCardInfo(helper, item.data)\n            VIDEO_TYPE -> setSingleVideoInfo(helper, item)\n            VIDEO_BANNER_THREE_TYPE -> setBanner3Info(helper, item.data)\n            VIDEO_SMALL_CARD_TYPE -> setSmallCardInfo(helper, item.data)\n        }\n\n    }\n\n\n    /**\n     * 设置视频banner信息\n     */\n    private fun setBannerInfo(helper: BaseViewHolder, item: Content) {\n        with(helper) {\n            getView<SimpleDraweeView>(R.id.iv_image).setImageURI(item.data.image)\n            itemView.setOnClickListener {\n                if (!TextUtils.isEmpty(item.data.actionUrl)) {\n                    mContext.startActivity(Intent(Intent.ACTION_VIEW).apply {\n                        data = Uri.parse(item.data.actionUrl)\n                        addCategory(Intent.CATEGORY_DEFAULT)\n                        addCategory(Intent.CATEGORY_BROWSABLE)\n                    })\n                }\n            }\n        }\n    }\n\n\n    /**\n     * 设置单视频卡片信息\n     */\n    private fun setFollowCardInfo(helper: BaseViewHolder, item: Content) {\n\n        with(helper) {\n            val info = item.data\n            //设置头布局\n            info.header?.let { getView<ItemHeaderView>(R.id.item_header_view).setHeader(it, info.type) }\n\n            getView<EliteImageView>(R.id.elite_view).apply {\n                setImageUrl(info.content!!.data.cover.feed)\n                setDailyVisible(info.content!!.data.library == \"DAILY\")\n                setOnClickListener {\n                    //跳转到视频详细界面\n                    VideoDetailActivity.start(mContext!!, item.data.content?.data!!, mData as ArrayList, mData.indexOf(item))\n                }\n            }\n        }\n\n    }\n\n    /**\n     * 设置水平滚动卡片集合信息\n     */\n    private fun setCollectionOfHorizontalScrollCardInfo(helper: BaseViewHolder, content: Content) {\n\n        with(helper) {\n            //设置头布局\n            val data = content.data\n            data.header?.let { getView<ItemHeaderView>(R.id.item_header_view).setHeader(it, content.type) }\n            //跳转到视频详细界面\n            getView<MarginWithIndicatorViewPager>(R.id.view_pager).apply {\n                pageViewClickListener = {\n                    val content = data.itemList[it].data\n                    VideoDetailActivity.start(mContext!!, content, data.itemList as ArrayList, it)\n\n                }\n                setData(data.itemList)\n            }\n        }\n    }\n\n\n    /**\n     * 设置带关注人的视频集合信息\n     */\n    private fun setCollectionBriefInfo(helper: BaseViewHolder, content: Content) {\n\n        with(helper) {\n\n            getView<RecyclerView>(R.id.rv_recycler).apply {\n                isNestedScrollingEnabled = false\n                layoutManager = LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false)\n                adapter = CollectionBriefAdapter(content.data.itemList).apply {\n                    onItemClickListener = OnItemClickListener { _, _, position ->\n                        //跳转到播放视频详情\n                        val item = getItem(position)\n                        VideoDetailActivity.start(mContext, item!!.data, data as ArrayList<Content>, position)\n\n                    }\n                }\n            }\n\n            //设置头布局\n            content.data.header?.let { getView<ItemHeaderView>(R.id.item_header_view).setHeader(it, content.data.type) }\n\n        }\n    }\n\n\n    /**\n     * 设置文字信息\n     */\n    private fun setSingleText(helper: BaseViewHolder, item: Content) {\n        helper.getView<CustomFontTextView>(R.id.tv_text).apply {\n            text = item.data.text\n            when (item.data.type) {\n                \"header1\" -> {\n                    setFontType(item.data.header?.font, FontType.LOBSTER)\n                    gravity = Gravity.CENTER\n                }\n                \"header2\" -> {\n                    setFontType(item.data.header?.font, FontType.BOLD)\n                    gravity = Gravity.CENTER\n                }\n                else -> {\n                    gravity = Gravity.START\n                    setFontType(item.data.header?.font, FontType.NORMAL)\n                }\n            }\n        }\n    }\n\n    /**\n     * 设置合作作者信息\n     */\n    private fun setBriefCardInfo(helper: BaseViewHolder, content: Content) {\n        helper.getView<ItemHeaderView>(R.id.item_header_view).setAuthorHeader(content.data)\n\n    }\n\n    /**\n     * 设置空白信息高度\n     */\n    private fun setBlankCardInfo(helper: BaseViewHolder, content: Content) {\n        helper.getView<View>(R.id.view).apply {\n            val lp = layoutParams\n            lp.height = mContext.dip2px(content.data.height.toFloat())\n            layoutParams = lp\n        }\n    }\n\n\n    /**\n     * 设置banner3（广告信息）信息\n     */\n    private fun setBanner3Info(helper: BaseViewHolder, item: ContentBean) {\n        with(helper) {\n            //设置头布局\n            item.header?.let { getView<ItemHeaderView>(R.id.item_header_view).setHeader(it, item.type) }\n\n            getView<EliteImageView>(R.id.elite_view).apply {\n                setImageUrl(item.image)\n                setDailyVisible(false)\n                setTranslateText(mContext.getString(R.string.advert))\n            }\n\n        }\n    }\n\n\n    /**\n     * 设置水平滚动卡片信息\n     */\n    private fun setHorizontalScrollCardInfo(helper: BaseViewHolder, itemList: MutableList<Content>) {\n        helper.getView<Banner>(R.id.banner).apply {\n            setImageLoader(FrescoImageLoader())\n            setImages(getHorizonTalCardUrl(itemList))\n            setBannerStyle(BannerConfig.CIRCLE_INDICATOR)\n            setIndicatorGravity(BannerConfig.CENTER)\n            isAutoPlay(true)\n            start()\n            setDelayTime(5000)\n            setOnBannerListener {\n                //跳转到过滤界面\n                mContext.startActivity(Intent(Intent.ACTION_VIEW).apply {\n                    data = Uri.parse(itemList[it].data.actionUrl)\n                    addCategory(Intent.CATEGORY_DEFAULT)\n                    addCategory(Intent.CATEGORY_BROWSABLE)\n                })\n            }\n        }\n    }\n\n    /**\n     * 获取水平卡片图片地址\n     */\n    private fun getHorizonTalCardUrl(itemList: MutableList<Content>) = itemList.map { it.data.image }\n\n\n    /**\n     * 设置集合卡片信息\n     */\n    private fun setCollectionCardWithCoverInfo(helper: BaseViewHolder, item: ContentBean) {\n        with(helper) {\n\n            getView<EliteImageView>(R.id.iv_image).apply {\n                item.header?.cover?.let { setImageUrl(it) }\n                setArrowVisible(true)\n            }\n\n            getView<RecyclerView>(R.id.rv_collection_cover_recycler).apply {\n                isNestedScrollingEnabled = false\n                layoutManager = LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false)\n                adapter = CollectionCardCoverAdapter(item.itemList)\n\n            }\n\n        }\n\n    }\n\n    /**\n     * 设置矩形卡片信息\n     */\n    private fun setSquareCollectionInfo(helper: BaseViewHolder, item: ContentBean) {\n        val itemList: MutableList<Content> = item.itemList\n        with(helper) {\n\n            getView<RecyclerView>(R.id.rv_square_recycler).apply {\n                isNestedScrollingEnabled = false\n                layoutManager = LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false)\n                adapter = SquareCollectionAdapter(itemList).apply {\n                    onItemClickListener = OnItemClickListener { _, _, position ->\n                        ARouter.getInstance()\n                                .build(Uri.parse(itemList[position].data.actionUrl))\n                                .navigation()\n                    }\n                }\n\n            }\n\n            item.header?.let { getView<ItemHeaderView>(R.id.item_header_view).setHeader(it, item.type) }\n        }\n    }\n\n    /**\n     * 设置长方形卡片信息\n     */\n    private fun setSquareCardInfo(helper: BaseViewHolder, item: ContentBean) {\n        helper.apply {\n            getView<SimpleDraweeView>(R.id.iv_simple_image).setImageURI(item.image)\n            setText(R.id.tv_title, item.title)\n        }\n    }\n\n    /**\n     * 设置正方形卡片信息\n     */\n    private fun setRectangleCardInfo(helper: BaseViewHolder, item: ContentBean) {\n        helper.apply {\n            getView<SimpleDraweeView>(R.id.iv_simple_image).setImageURI(item.image)\n            setText(R.id.tv_title, item.title)\n        }\n\n    }\n\n    /**\n     * 设置单视频信息\n     */\n    private fun setSingleVideoInfo(helper: BaseViewHolder, item: Content) {\n        with(helper) {\n            getView<SimpleDraweeView>(R.id.iv_image).setImageURI(item.data.cover.feed)\n            setText(R.id.tv_single_title, item.data.title)\n            val description = \"#${item.data.category}   /   ${getElapseTimeForShow(item.data.duration)}\"\n            setText(R.id.tv_single_desc, description)\n\n\n            //设置label\n            if (item.data.label != null) {\n                setGone(R.id.tv_label, true)\n                setText(R.id.tv_label, item.data.label?.text)\n            } else {\n                setGone(R.id.tv_label, false)\n            }\n            //点击跳转到视频界面\n            itemView.setOnClickListener { VideoDetailActivity.start(mContext, item.data, data as ArrayList<Content>) }\n\n        }\n    }\n\n    /**\n     * 设置小视频卡片信息\n     */\n    private fun setSmallCardInfo(helper: BaseViewHolder, data: ContentBean) {\n        with(helper) {\n            getView<SimpleDraweeView>(R.id.iv_image).setImageURI(data.cover.feed)\n            setText(R.id.tv_title, data.title)\n            val description = \"#${data.category}   /   ${getElapseTimeForShow(data.duration)}\"\n            setText(R.id.tv_desc, description)\n            itemView.setOnClickListener { VideoDetailActivity.start(mContext, data, arrayListOf()) }\n        }\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/base/model/BaseModel.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.base.model\n\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.entity.JenniferInfo\nimport com.jennifer.andy.simpleeyes.net.Api\nimport com.jennifer.andy.simpleeyes.rx.RxThreadHelper\nimport com.jennifer.andy.simpleeyes.rx.error.globalHandleError\nimport io.reactivex.Observable\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/5 19:08\n * Description:\n */\n\ninterface BaseModel {\n\n    /**\n     * 加载更多信息，针对于AndyInfo数据类型\n     */\n    fun loadMoreAndyInfo(nextPageUrl: String?): Observable<AndyInfo> = Api.getDefault()\n            .getMoreAndyInfo(nextPageUrl)\n            .compose(globalHandleError())\n            .compose(RxThreadHelper.switchObservableThread())\n\n    /**\n     * 加载更多信息，针对于JenniferInfo数据类型\n     */\n    fun loadMoreJenniferInfo(nextPageUrl: String?): Observable<JenniferInfo> = Api.getDefault()\n            .getMoreJenniferInfo(nextPageUrl)\n            .compose(globalHandleError())\n            .compose(RxThreadHelper.switchObservableThread())\n\n    /**\n     * 根据url,获取数据\n     */\n    fun getDataInfoFromUrl(url: String?): Observable<AndyInfo> = Api.getDefault()\n            .getDataInfoFromUrl(url)\n            .compose(globalHandleError())\n            .compose(RxThreadHelper.switchObservableThread())\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/base/presenter/BasePresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.base.presenter\n\nimport androidx.lifecycle.LifecycleObserver\nimport androidx.lifecycle.LifecycleOwner\nimport com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/5 19:10\n * Description:\n */\n\nopen class BasePresenter<V> : LifecycleObserver {\n\n    protected var mView: V? = null\n    protected lateinit var mScopeProvider: AndroidLifecycleScopeProvider\n\n    /**\n     * 与view进行关联\n     */\n    fun attachView(view: V, lifecycleOwner: LifecycleOwner) {\n        this.mView = view\n        this.mScopeProvider = AndroidLifecycleScopeProvider.from(lifecycleOwner)\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/base/presenter/LoadMorePresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.base.presenter\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.ui.base.LoadMoreView\nimport com.jennifer.andy.simpleeyes.ui.base.model.BaseModel\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-07-10 22:24\n * Description: 加载更多Presenter。\n * 第一个泛型参数T:为加载更多对应的数据类型\n * 第二个泛型参数M：为对应model\n * 第三个泛型参数V：为实现了加载更多的View\n */\n\nopen class LoadMorePresenter<T, M : BaseModel, V : LoadMoreView<T>> : BasePresenter<V>() {\n\n    protected var mNextPageUrl: String? = null\n    protected open lateinit var mBaseModel: M\n\n    fun loadMoreInfo() {\n        if (mNextPageUrl != null) {\n            mBaseModel.loadMoreAndyInfo(mNextPageUrl).autoDispose(mScopeProvider).subscribe({\n                mView?.showContent()\n                if (mNextPageUrl == null) {\n                    mView?.showNoMore()\n                } else {\n                    mNextPageUrl = it.nextPageUrl\n                    mView?.loadMoreSuccess(it as T)\n                }\n            }, {\n                mView?.showNetError(View.OnClickListener {\n                    loadMoreInfo()\n                })\n            })\n        } else {\n            mView?.showNoMore()\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/common/CommonModel.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.common\n\nimport com.jennifer.andy.simpleeyes.ui.base.model.BaseModel\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-08-26 20:37\n * Description:\n */\n\nclass CommonModel:BaseModel"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/common/CommonPresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.common\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.LoadMorePresenter\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-08-26 19:51\n * Description:\n */\n\nclass CommonPresenter : LoadMorePresenter<AndyInfo, CommonModel, CommonView>() {\n\n\n    override var mBaseModel = CommonModel()\n    /**\n     * 获取专题信息\n     */\n    fun loadDataInfoFromUrl(url: String?) {\n        mBaseModel.getDataInfoFromUrl(url).autoDispose(mScopeProvider).subscribe({\n            mView?.showLoadSuccess(it.itemList)\n            mNextPageUrl = it.nextPageUrl\n        }, {\n            mView?.showNetError(View.OnClickListener { loadDataInfoFromUrl(url) })\n        })\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/common/CommonRecyclerActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.common\n\nimport android.os.Bundle\nimport android.widget.RelativeLayout\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\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.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.ui.base.BaseActivity\nimport com.jennifer.andy.simpleeyes.ui.base.adapter.BaseDataAdapter\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.CustomLoadMoreView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-08-26 19:47\n * Description:公共RecyclerView界面，根据url加载数据\n */\n\n@Route(path = \"/AndyJennifer/common\")\nclass CommonRecyclerActivity : BaseActivity<CommonView, CommonPresenter>(), CommonView {\n\n    @Autowired\n    @JvmField\n    var title: String? = null\n\n    @Autowired\n    @JvmField\n    var url: String? = null\n\n    private val mToolbar: RelativeLayout by bindView(R.id.tool_bar)\n    private val mRecycler by bindView<RecyclerView>(R.id.rv_recycler)\n    private var mCommonAdapter: BaseDataAdapter? = null\n\n    override fun initView(savedInstanceState: Bundle?) {\n        ARouter.getInstance().inject(this)\n        initToolBar(mToolbar, title)\n        mPresenter.loadDataInfoFromUrl(url)\n    }\n\n    override fun showLoadSuccess(itemList: MutableList<Content>) {\n        with(mRecycler) {\n            mCommonAdapter = BaseDataAdapter(itemList).apply {\n                setOnLoadMoreListener({ mPresenter.loadMoreInfo() }, mRecycler)\n                setLoadMoreView(CustomLoadMoreView())\n            }\n            layoutManager = LinearLayoutManager(mContext)\n            adapter = mCommonAdapter\n        }\n    }\n\n    override fun loadMoreSuccess(data: AndyInfo) {\n        mCommonAdapter?.addData(data.itemList)\n        mCommonAdapter?.loadMoreComplete()\n    }\n\n    override fun showNoMore() {\n        mCommonAdapter?.loadMoreEnd()\n    }\n\n    override fun getContentViewLayoutId() = R.layout.activity_common_recyclerview\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/common/CommonView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.common\n\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.ui.base.LoadMoreView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-08-26 19:48\n * Description:\n */\n\ninterface CommonView: LoadMoreView<AndyInfo>{\n\n    fun showLoadSuccess(itemList: MutableList<Content>)\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/AllCategoryActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed\n\nimport android.net.Uri\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.RelativeLayout\nimport androidx.recyclerview.widget.GridLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.chad.library.adapter.base.BaseQuickAdapter\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.BaseActivity\nimport com.jennifer.andy.simpleeyes.ui.base.adapter.BaseDataAdapter\nimport com.jennifer.andy.simpleeyes.ui.base.adapter.BaseDataAdapter.Companion.RECTANGLE_CARD_TYPE\nimport com.jennifer.andy.simpleeyes.ui.feed.presenter.AllCategoryPresenter\nimport com.jennifer.andy.simpleeyes.ui.feed.view.AllCategoryView\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.GridItemDecoration\nimport com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/16 14:17\n * Description: 全部分类\n */\n\nclass AllCategoryActivity : BaseActivity<AllCategoryView, AllCategoryPresenter>(), AllCategoryView {\n\n    private val mToolBar: RelativeLayout by bindView(R.id.tool_bar)\n    private val mRecyclerView: RecyclerView by bindView(R.id.rv_recycler)\n    private val mStateView: MultipleStateView by bindView(R.id.multiple_state_view)\n\n\n    override fun initView(savedInstanceState: Bundle?) {\n        initToolBar(mToolBar, R.string.all_category)\n        mPresenter.loadAllCategoriesInfo()\n    }\n\n    override fun loadAllCategoriesSuccess(andyInfo: AndyInfo) {\n        val adapter = BaseDataAdapter(andyInfo.itemList)\n        adapter.setSpanSizeLookup { _, position ->\n            if (adapter.getItemViewType(position) == RECTANGLE_CARD_TYPE) 2 else 1\n        }\n        //根据actionUrl跳转到相应界面\n        adapter.onItemClickListener = BaseQuickAdapter.OnItemClickListener { _, _, position ->\n            ARouter.getInstance()\n                    .build(Uri.parse(adapter.data[position].data.actionUrl))\n                    .navigation()\n        }\n        mRecyclerView.layoutManager = GridLayoutManager(mContext, 2)\n        mRecyclerView.addItemDecoration(GridItemDecoration(2, 4, true))\n        mRecyclerView.adapter = adapter\n    }\n\n\n    override fun showNetError(onClickListener: View.OnClickListener) {\n        mStateView.showNetError(onClickListener)\n    }\n\n    override fun showContent() {\n        mStateView.showContent()\n    }\n\n    override fun getContentViewLayoutId() = R.layout.activity_all_category\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/CategoryTabActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed\n\nimport android.animation.ArgbEvaluator\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.widget.ImageView\nimport androidx.annotation.DrawableRes\nimport androidx.core.graphics.drawable.DrawableCompat\nimport androidx.fragment.app.Fragment\nimport androidx.viewpager.widget.ViewPager\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.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.Category\nimport com.jennifer.andy.simpleeyes.entity.TabInfo\nimport com.jennifer.andy.simpleeyes.ui.base.BaseActivity\nimport com.jennifer.andy.simpleeyes.ui.base.BaseFragmentItemAdapter\nimport com.jennifer.andy.simpleeyes.ui.feed.presenter.CategoryTabPresenter\nimport com.jennifer.andy.simpleeyes.ui.feed.view.CategoryTabView\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.utils.showKeyboard\nimport com.jennifer.andy.simpleeyes.widget.StickyNavLayout\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\nimport com.jennifer.andy.simpleeyes.widget.font.FontType\nimport com.jennifer.andy.simpleeyes.widget.tab.ShortTabLayout\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/8/6 10:46\n * Description:种类界面，包括广告、剧情等同类型的分类。\n */\n\n@Route(path = \"/AndyJennifer/category\")\nclass CategoryTabActivity : BaseActivity<CategoryTabView, CategoryTabPresenter>(), CategoryTabView {\n\n    private val mStickyNavLayout: StickyNavLayout by bindView(R.id.stick_layout)\n    private val mViewPager: ViewPager by bindView(R.id.id_sticky_nav_layout_viewpager)\n    private val mTabLayout: ShortTabLayout by bindView(R.id.id_sticky_nav_layout_nav_view)\n\n    private val mImageView: SimpleDraweeView by bindView(R.id.iv_image)\n    private val mTvSubTitle: CustomFontTextView by bindView(R.id.tv_sub_title)\n    private val mTvDesc: CustomFontTextView by bindView(R.id.tv_desc)\n    private val mTvFollow: CustomFontTextView by bindView(R.id.tv_follow)\n\n    @Autowired\n    @JvmField\n    var title: String? = null\n\n    @Autowired\n    @JvmField\n    var id: String? = null\n\n    @Autowired\n    @JvmField\n    var tabIndex: String? = null\n\n\n    override fun initView(savedInstanceState: Bundle?) {\n        ARouter.getInstance().inject(this)\n        mPresenter.getTabInfo(id!!)\n        initToolBar(R.drawable.ic_action_back_white, title, 0f)\n    }\n\n\n    override fun showLoadTabSuccess(category: Category) {\n        mImageView.setImageURI(category.categoryInfo.headerImage)\n        mTvSubTitle.text = category.categoryInfo.name\n        mTvDesc.text = category.categoryInfo.description\n\n        mViewPager.adapter = BaseFragmentItemAdapter(supportFragmentManager, initFragments(category.tabInfo), initTitles(category.tabInfo))\n        mTabLayout.setupWithViewPager(mViewPager)\n        mViewPager.currentItem = tabIndex?.toInt() ?: 0\n\n        mStickyNavLayout.setScrollChangeListener(object : StickyNavLayout.ScrollChangeListener {\n            override fun onScroll(moveRatio: Float) {\n                if (moveRatio < 1) initToolBar(R.drawable.ic_action_back_white, title, moveRatio)\n                else initToolBar(R.drawable.ic_action_back_black, title, moveRatio)\n            }\n        })\n    }\n\n    private fun initFragments(tabInfo: TabInfo): MutableList<Fragment> {\n        val fragments = mutableListOf<Fragment>()\n        for (i in tabInfo.tabList.indices) {\n            fragments.add(TagDetailInfoFragment.newInstance(tabInfo.tabList[i].apiUrl))\n        }\n        return fragments\n    }\n\n    private fun initTitles(tabInfo: TabInfo): MutableList<String> {\n        val titles = mutableListOf<String>()\n        for (i in tabInfo.tabList.indices) {\n            titles.add(tabInfo.tabList[i].name)\n        }\n        return titles\n    }\n\n\n    private fun initToolBar(@DrawableRes backResId: Int, title: String? = null, titleAlpha: Float = 1f) {\n        val ivBack = findViewById<ImageView>(R.id.iv_back)\n        ivBack.setOnClickListener {\n            showKeyboard(false)\n            finish()\n        }\n        //设置渐变\n        val color = ArgbEvaluator().evaluate(titleAlpha, Color.WHITE, Color.BLACK) as Int\n        val wrapDrawable = DrawableCompat.wrap(getDrawable(backResId))\n        wrapDrawable.setTint(color)\n        ivBack.setImageDrawable(wrapDrawable)\n\n        val tvTitle = findViewById<CustomFontTextView>(R.id.tv_title)\n        tvTitle.setFontType(fontType = FontType.BOLD)\n        tvTitle.text = title\n        tvTitle.alpha = titleAlpha\n\n    }\n\n\n    override fun getContentViewLayoutId() = R.layout.activity_category_tab\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/FeedFragment.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed\n\nimport android.os.Bundle\n\nimport android.view.View\nimport android.widget.ImageView\nimport androidx.fragment.app.Fragment\nimport androidx.viewpager.widget.ViewPager\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.TabInfo\nimport com.jennifer.andy.simpleeyes.ui.base.BaseFragment\nimport com.jennifer.andy.simpleeyes.ui.base.BaseFragmentItemAdapter\nimport com.jennifer.andy.simpleeyes.ui.feed.presenter.FeedPresenter\nimport com.jennifer.andy.simpleeyes.ui.feed.view.FeedView\nimport com.jennifer.andy.simpleeyes.ui.search.SearchHotActivity\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.utils.readyGo\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\nimport com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\nimport com.jennifer.andy.simpleeyes.widget.tab.ShortTabLayout\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/22 13:51\n * Description:发现\n */\n\nclass FeedFragment : BaseFragment<FeedView, FeedPresenter>(), FeedView {\n\n    private val mViewPager: ViewPager by bindView(R.id.view_pager)\n    private val mTvAllCategory: CustomFontTextView by bindView(R.id.tv_all_category)\n    private val mIvSearch: ImageView by bindView(R.id.iv_search)\n    private val mTabLayout: ShortTabLayout by bindView(R.id.tab_layout)\n    private val mStateView: MultipleStateView by bindView(R.id.multiple_state_view)\n\n    companion object {\n        @JvmStatic\n        fun newInstance(): FeedFragment = FeedFragment()\n    }\n\n\n    override fun initView(savedInstanceState: Bundle?) {\n        mPresenter.getDiscoveryTab()\n\n        //跳转到搜索界面\n        mIvSearch.setOnClickListener {\n            readyGo<SearchHotActivity>()\n        }\n        //跳转到全部分类界面\n        mTvAllCategory.setOnClickListener {\n            readyGo<AllCategoryActivity>()\n        }\n    }\n\n    override fun loadTabSuccess(tabInfo: TabInfo) {\n        mViewPager.adapter = BaseFragmentItemAdapter(childFragmentManager, initFragments(tabInfo), initTitles(tabInfo))\n        mViewPager.offscreenPageLimit = tabInfo.tabList.size\n        mTabLayout.setupWithViewPager(mViewPager)\n    }\n\n    private fun initFragments(tabInfo: TabInfo): MutableList<Fragment> {\n        val fragments = mutableListOf<Fragment>()\n        for (i in tabInfo.tabList.indices) {\n            fragments.add(TagDetailInfoFragment.newInstance(tabInfo.tabList[i].apiUrl))\n        }\n        return fragments\n    }\n\n    private fun initTitles(tabInfo: TabInfo): MutableList<String> {\n        val titles = mutableListOf<String>()\n        for (i in tabInfo.tabList.indices) {\n            titles.add(tabInfo.tabList[i].name)\n        }\n        return titles\n    }\n\n    override fun showNetError(onClickListener: View.OnClickListener) {\n        mStateView.showNetError(onClickListener)\n    }\n\n    override fun showContent() {\n        mStateView.showContent()\n    }\n\n    override fun getContentViewLayoutId() = R.layout.fragment_feed\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/RankListActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed\n\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.RelativeLayout\nimport androidx.fragment.app.Fragment\nimport androidx.viewpager.widget.ViewPager\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.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.TabInfo\nimport com.jennifer.andy.simpleeyes.ui.base.BaseActivity\nimport com.jennifer.andy.simpleeyes.ui.base.BaseFragmentItemAdapter\nimport com.jennifer.andy.simpleeyes.ui.feed.presenter.RankListPresenter\nimport com.jennifer.andy.simpleeyes.ui.feed.view.RankListView\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.font.FontType\nimport com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\nimport com.jennifer.andy.simpleeyes.widget.tab.ShortTabLayout\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/26 18:05\n * Description:排行榜\n */\n\n@Route(path = \"/AndyJennifer/ranklist\")\nclass RankListActivity : BaseActivity<RankListView, RankListPresenter>(), RankListView {\n\n    private val mToolbar: RelativeLayout by bindView(R.id.tool_bar)\n    private val mViewPager: ViewPager by bindView(R.id.view_pager)\n    private val mTabLayout: ShortTabLayout by bindView(R.id.tab_layout)\n    private val mStateView: MultipleStateView by bindView(R.id.multiple_state_view)\n\n    @Autowired\n    @JvmField\n    var tabIndex: String? = null//哇，这里ARouter居然不能支持Int类型\n\n    override fun initView(savedInstanceState: Bundle?) {\n        ARouter.getInstance().inject(this)\n        initToolBar(mToolbar, R.string.open_eyes_english, FontType.LOBSTER)\n        mPresenter.getRankListTab()\n    }\n\n    override fun loadTabSuccess(tabInfo: TabInfo) {\n        mTabLayout.visibility = View.VISIBLE\n        mViewPager.adapter = BaseFragmentItemAdapter(supportFragmentManager, initFragments(tabInfo), initTitles(tabInfo))\n        mViewPager.offscreenPageLimit = tabInfo.tabList.size\n        tabIndex?.let { mViewPager.currentItem = it.toInt() }\n        mTabLayout.setupWithViewPager(mViewPager)\n    }\n\n\n    private fun initFragments(tabInfo: TabInfo): MutableList<Fragment> {\n        val fragments = mutableListOf<Fragment>()\n        for (i in tabInfo.tabList.indices) {\n            fragments.add(TagDetailInfoFragment.newInstance(tabInfo.tabList[i].apiUrl))\n        }\n        return fragments\n    }\n\n    private fun initTitles(tabInfo: TabInfo): MutableList<String> {\n        val titles = mutableListOf<String>()\n        for (i in tabInfo.tabList.indices) {\n            titles.add(tabInfo.tabList[i].name)\n        }\n        return titles\n    }\n\n    override fun showNetError(onClickListener: View.OnClickListener) {\n        mStateView.showNetError(onClickListener)\n    }\n\n    override fun showContent() {\n        mStateView.showContent()\n    }\n\n    override fun getContentViewLayoutId() = R.layout.activity_rank\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/TagActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed\n\nimport android.os.Bundle\nimport android.widget.RelativeLayout\nimport androidx.fragment.app.Fragment\nimport androidx.viewpager.widget.ViewPager\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.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.TabDetailInfo\nimport com.jennifer.andy.simpleeyes.entity.TabInfo\nimport com.jennifer.andy.simpleeyes.net.Api\nimport com.jennifer.andy.simpleeyes.ui.base.BaseAppCompatActivity\nimport com.jennifer.andy.simpleeyes.ui.base.BaseFragmentItemAdapter\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.tab.ShortTabLayout\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/8/3 14:00\n * Description: 360 全景\n */\n\n@Route(path = \"/AndyJennifer/tag\")\nclass TagActivity : BaseAppCompatActivity() {\n\n    private val mToolbar: RelativeLayout by bindView(R.id.tool_bar)\n    private val mTabLayout: ShortTabLayout by bindView(R.id.tab_layout)\n    private val mViewPager: ViewPager by bindView(R.id.view_pager)\n\n\n    @Autowired\n    @JvmField\n    var title: String? = null\n\n    @Autowired\n    @JvmField\n    var id: String? = null\n\n    override fun initView(savedInstanceState: Bundle?) {\n        ARouter.getInstance().inject(this)\n        initToolBar(mToolbar, title)\n        initTabInfo()\n    }\n\n    private fun initTabInfo() {\n        /**\n         * 这里拼数据，是因为抓包的时候，没有发现获取tab信息的接口，所以自己拼的数据\n         */\n        val tabInfoDetailList = mutableListOf<TabDetailInfo>()\n        tabInfoDetailList.add(TabDetailInfo(0, \"按时间排序\", \"${Api.BASE_URL}api/v3/tag/videos?tagId=$id&strategy=date\"))\n        tabInfoDetailList.add(TabDetailInfo(1, \"按分享排序\", \"${Api.BASE_URL}api/v3/tag/videos?tagId=$id&strategy=shareCount\"))\n        val tabInfo = TabInfo(tabInfoDetailList, 0)\n\n        mViewPager.adapter = BaseFragmentItemAdapter(supportFragmentManager, initFragments(tabInfo), initTitles(tabInfo))\n        mViewPager.offscreenPageLimit = tabInfo.tabList.size\n        mTabLayout.setupWithViewPager(mViewPager)\n    }\n\n    private fun initFragments(tabInfo: TabInfo): MutableList<Fragment> {\n        val fragments = mutableListOf<Fragment>()\n        for (i in tabInfo.tabList.indices) {\n            fragments.add(TagDetailInfoFragment.newInstance(tabInfo.tabList[i].apiUrl))\n        }\n        return fragments\n    }\n\n    private fun initTitles(tabInfo: TabInfo): MutableList<String> {\n        val titles = mutableListOf<String>()\n        for (i in tabInfo.tabList.indices) {\n            titles.add(tabInfo.tabList[i].name)\n        }\n        return titles\n    }\n\n    override fun getContentViewLayoutId() = R.layout.activity_tag\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/TagDetailInfoFragment.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed\n\nimport android.os.Bundle\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.net.Extras\nimport com.jennifer.andy.simpleeyes.ui.base.BaseFragment\nimport com.jennifer.andy.simpleeyes.ui.base.adapter.BaseDataAdapter\nimport com.jennifer.andy.simpleeyes.ui.feed.presenter.TagDetailInfoPresenter\nimport com.jennifer.andy.simpleeyes.ui.feed.view.TagDetailInfoView\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.CustomLoadMoreView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/3 11:29\n * Description:详细的Tab界面\n */\n\nclass TagDetailInfoFragment : BaseFragment<TagDetailInfoView, TagDetailInfoPresenter>(), TagDetailInfoView {\n\n\n    private val mRecyclerView: RecyclerView by bindView(R.id.rv_recycler)\n    private var mAdapter: BaseDataAdapter? = null\n\n    private lateinit var mApiUrl: String\n\n    companion object {\n\n        @JvmStatic\n        fun newInstance(apiUrl: String): TagDetailInfoFragment {\n            val categoryFragment = TagDetailInfoFragment()\n            val bundle = Bundle()\n            bundle.putString(Extras.API_URL, apiUrl)\n            categoryFragment.arguments = bundle\n            return categoryFragment\n        }\n    }\n\n\n    override fun getBundleExtras(extras: Bundle) {\n        mApiUrl = extras.getString(Extras.API_URL)\n    }\n\n    override fun onLazyInitView(savedInstanceState: Bundle?) {\n        mPresenter.getDetailInfo(mApiUrl)\n    }\n\n    override fun showGetTabInfoSuccess(andyInfo: AndyInfo) {\n        if (mAdapter == null) {\n            mAdapter = BaseDataAdapter(andyInfo.itemList)\n            mAdapter?.setLoadMoreView(CustomLoadMoreView())\n            mAdapter?.setOnLoadMoreListener({ mPresenter.loadMoreInfo() }, mRecyclerView)\n            mRecyclerView.adapter = mAdapter\n            mRecyclerView.layoutManager = LinearLayoutManager(context)\n        } else {\n            mAdapter?.setNewData(andyInfo.itemList)\n        }\n    }\n\n    override fun loadMoreSuccess(data: AndyInfo) {\n        mAdapter?.addData(data.itemList)\n        mAdapter?.loadMoreComplete()\n    }\n\n    override fun showNoMore() {\n        mAdapter?.loadMoreEnd()\n    }\n\n    override fun getContentViewLayoutId() = R.layout.fragment_tag_detail_info\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/TopicActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed\n\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.RelativeLayout\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\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.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.ui.base.BaseActivity\nimport com.jennifer.andy.simpleeyes.ui.base.adapter.BaseDataAdapter\nimport com.jennifer.andy.simpleeyes.ui.feed.presenter.TopicPresenter\nimport com.jennifer.andy.simpleeyes.ui.feed.view.TopicView\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.CustomLoadMoreView\nimport com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/31 15:37\n * Description:专题\n */\n@Route(path = \"/campaign/list\")\nclass TopicActivity : BaseActivity<TopicView, TopicPresenter>(), TopicView {\n\n    private val mToolbar: RelativeLayout by bindView(R.id.tool_bar)\n    private val mRecyclerView: RecyclerView by bindView(R.id.rv_recycler)\n    private val mStateView: MultipleStateView by bindView(R.id.multiple_state_view)\n    private var mAdapter: BaseDataAdapter? = null\n\n    @Autowired\n    @JvmField\n    var title: String? = null\n\n    override fun initView(savedInstanceState: Bundle?) {\n        ARouter.getInstance().inject(this)\n        initToolBar(mToolbar, title)\n        mPresenter.getTopicInfo()\n    }\n\n    override fun showGetTopicInfoSuccess(itemList: MutableList<Content>) {\n        if (mAdapter == null) {\n            mAdapter = BaseDataAdapter(itemList)\n            mAdapter?.setOnLoadMoreListener({ mPresenter.loadMoreInfo() }, mRecyclerView)\n            mAdapter?.setLoadMoreView(CustomLoadMoreView())\n            mRecyclerView.adapter = mAdapter\n            mRecyclerView.layoutManager = LinearLayoutManager(mContext)\n        } else {\n            mAdapter?.setNewData(itemList)\n        }\n    }\n\n    override fun loadMoreSuccess(data: AndyInfo) {\n        mAdapter?.addData(data.itemList)\n        mAdapter?.loadMoreComplete()\n    }\n\n\n    override fun showNoMore() {\n        mAdapter?.loadMoreEnd()\n    }\n\n    override fun showNetError(onClickListener: View.OnClickListener) {\n        mStateView.showNetError(onClickListener)\n    }\n\n    override fun getContentViewLayoutId() = R.layout.activity_topic\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/WebViewActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed\n\nimport android.os.Build\nimport android.os.Bundle\nimport android.webkit.WebResourceRequest\nimport android.webkit.WebSettings\nimport android.webkit.WebView\nimport android.webkit.WebViewClient\nimport android.widget.ImageView\nimport android.widget.RelativeLayout\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.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.ui.base.BaseAppCompatActivity\nimport com.jennifer.andy.simpleeyes.utils.bindView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/20 16:24\n * Description:网页展示(设置了标题，设置了能否分享）\n * jvmField注解注释的不会生成get与set方法。且为public\n */\n@Route(path = \"/AndyJennifer/webview/\")\nclass WebViewActivity : BaseAppCompatActivity() {\n\n    private val mToolbar: RelativeLayout by bindView(R.id.tool_bar)\n    private val mIvShare: ImageView by bindView(R.id.iv_share)\n    private val mWebView: WebView by bindView(R.id.web_view)\n\n    @Autowired\n    @JvmField\n    var title: String? = null\n\n    @Autowired\n    @JvmField\n    var url: String? = null\n\n    @Autowired\n    @JvmField\n    var nid: String? = null\n\n    @Autowired\n    @JvmField\n    var tid: String? = null\n\n    @Autowired\n    @JvmField\n    var cookie: String? = null\n\n    @Autowired\n    @JvmField\n    var udid: String? = null\n\n    @Autowired\n    @JvmField\n    var shareable: Boolean? = false\n\n\n    override fun initView(savedInstanceState: Bundle?) {\n        ARouter.getInstance().inject(this)\n        initToolBar(mToolbar, title)\n        initWebView()\n    }\n\n    private fun initWebView() {\n\n        mWebView.apply {\n\n            settings.apply {\n                javaScriptEnabled = true\n                loadsImagesAutomatically = true\n                defaultTextEncodingName = \"utf-8\"\n                //5.0以上需要支持混合模式\n                if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {\n                    mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW\n                }\n            }\n\n            webViewClient = object : WebViewClient() {\n                override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?) = false\n            }\n\n        }.loadUrl(url)\n    }\n\n    override fun getContentViewLayoutId() = R.layout.activity_webview\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/model/FeedModel.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed.model\n\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.entity.Category\nimport com.jennifer.andy.simpleeyes.entity.Tab\nimport com.jennifer.andy.simpleeyes.net.Api\nimport com.jennifer.andy.simpleeyes.rx.RxThreadHelper\nimport com.jennifer.andy.simpleeyes.rx.error.globalHandleError\nimport com.jennifer.andy.simpleeyes.ui.base.model.BaseModel\nimport io.reactivex.Observable\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/22 13:52\n * Description:\n */\n\nclass FeedModel : BaseModel {\n\n    /**\n     * 获取发现tab栏\n     */\n    fun getDiscoveryTab(): Observable<Tab> =\n            Api.getDefault()\n                    .getDiscoveryTab()\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n\n    /**\n     * 获取全部分类信息\n     */\n    fun loadAllCategoriesInfo(): Observable<AndyInfo> =\n            Api.getDefault()\n                    .getAllCategoriesInfo()\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n\n    /**\n     * 获取排行榜tab栏\n     */\n    fun getRankListTab(): Observable<Tab> =\n            Api.getDefault()\n                    .getRankListTab()\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n\n    /**\n     * 获取专题信息\n     */\n    fun getTopicInfo(): Observable<AndyInfo> =\n            Api.getDefault()\n                    .getTopicInfo()\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n\n\n    /**\n     * 获取种类下tab信息\n     */\n    fun getCategoryTabIno(id: String): Observable<Category> =\n            Api.getDefault()\n                    .getCategoryTabInfo(id)\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/presenter/AllCategoryPresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed.presenter\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport com.jennifer.andy.simpleeyes.ui.feed.model.FeedModel\nimport com.jennifer.andy.simpleeyes.ui.feed.view.AllCategoryView\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/16 14:18\n * Description:\n */\n\nclass AllCategoryPresenter : BasePresenter<AllCategoryView>() {\n\n    private var mFeedModel: FeedModel = FeedModel()\n\n    fun loadAllCategoriesInfo() {\n        mFeedModel.loadAllCategoriesInfo().autoDispose(mScopeProvider).subscribe({\n            mView?.showContent()\n            mView?.loadAllCategoriesSuccess(it)\n        }, {\n            mView?.showNetError(View.OnClickListener { loadAllCategoriesInfo() })\n        })\n\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/presenter/CategoryTabPresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed.presenter\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport com.jennifer.andy.simpleeyes.ui.feed.model.FeedModel\nimport com.jennifer.andy.simpleeyes.ui.feed.view.CategoryTabView\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/8/6 10:48\n * Description:\n */\n\nclass CategoryTabPresenter : BasePresenter<CategoryTabView>() {\n\n    private var mFeedModel: FeedModel = FeedModel()\n\n    fun getTabInfo(id: String) {\n        mFeedModel.getCategoryTabIno(id).autoDispose(mScopeProvider).subscribe({\n            mView?.showContent()\n            mView?.showLoadTabSuccess(it)\n        }, {\n            mView?.showNetError(View.OnClickListener { getTabInfo(id) })\n        })\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/presenter/FeedPresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed.presenter\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport com.jennifer.andy.simpleeyes.ui.feed.model.FeedModel\nimport com.jennifer.andy.simpleeyes.ui.feed.view.FeedView\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/22 13:52\n * Description:\n */\n\nclass FeedPresenter : BasePresenter<FeedView>() {\n\n    private var mFeedModel: FeedModel = FeedModel()\n\n    /**\n     * 获取导航栏信息\n     */\n    fun getDiscoveryTab() {\n        mFeedModel.getDiscoveryTab().autoDispose(mScopeProvider).subscribe({\n            mView?.showContent()\n            mView?.loadTabSuccess(it.tabInfo)\n        }, {\n            mView?.showNetError(View.OnClickListener { getDiscoveryTab() })\n        })\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/presenter/RankListPresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed.presenter\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport com.jennifer.andy.simpleeyes.ui.feed.model.FeedModel\nimport com.jennifer.andy.simpleeyes.ui.feed.view.RankListView\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/26 18:44\n * Description:\n */\n\nclass RankListPresenter : BasePresenter<RankListView>() {\n\n    private var mFeedModel: FeedModel = FeedModel()\n\n    fun getRankListTab() {\n        mFeedModel.getRankListTab().autoDispose(mScopeProvider).subscribe({\n            mView?.loadTabSuccess(it.tabInfo)\n        }, {\n            mView?.showNetError(View.OnClickListener { getRankListTab() })\n        })\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/presenter/TagDetailInfoPresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed.presenter\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.LoadMorePresenter\nimport com.jennifer.andy.simpleeyes.ui.feed.model.FeedModel\nimport com.jennifer.andy.simpleeyes.ui.feed.view.TagDetailInfoView\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/3 11:30\n * Description:\n */\n\nclass TagDetailInfoPresenter : LoadMorePresenter<AndyInfo, FeedModel, TagDetailInfoView>() {\n\n    override var mBaseModel: FeedModel = FeedModel()\n\n    /**\n     * 获取tab栏下信息\n     */\n    fun getDetailInfo(url: String) {\n        mBaseModel.getDataInfoFromUrl(url).autoDispose(mScopeProvider).subscribe({\n            mView?.showContent()\n            mView?.showGetTabInfoSuccess(it)\n            mNextPageUrl = it.nextPageUrl\n            if (mNextPageUrl == null) mView?.showNoMore()\n        }, {\n            mView?.showNetError(View.OnClickListener { getDetailInfo(url) })\n        })\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/presenter/TopicPresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed.presenter\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.LoadMorePresenter\nimport com.jennifer.andy.simpleeyes.ui.feed.model.FeedModel\nimport com.jennifer.andy.simpleeyes.ui.feed.view.TopicView\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/31 15:40\n * Description:\n */\n\nclass TopicPresenter : LoadMorePresenter<AndyInfo, FeedModel, TopicView>() {\n\n    override var mBaseModel: FeedModel = FeedModel()\n\n    /**\n     * 获取专题信息\n     */\n    fun getTopicInfo() {\n        mBaseModel.getTopicInfo().autoDispose(mScopeProvider).subscribe({\n            mView?.showGetTopicInfoSuccess(it.itemList)\n            mNextPageUrl = it.nextPageUrl\n        }, {\n            mView?.showNetError(View.OnClickListener { getTopicInfo() })\n        })\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/view/AllCategoryView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed.view\n\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.BaseView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/16 14:18\n * Description:\n */\n\ninterface AllCategoryView : BaseView {\n    fun loadAllCategoriesSuccess(andyInfo: AndyInfo)\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/view/CategoryTabView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed.view\n\nimport com.jennifer.andy.simpleeyes.entity.Category\nimport com.jennifer.andy.simpleeyes.ui.base.BaseView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/8/6 10:47\n * Description:\n */\n\ninterface CategoryTabView : BaseView {\n    /**\n     * 加载分类标签成功\n     */\n    fun showLoadTabSuccess(category: Category)\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/view/FeedView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed.view\n\nimport com.jennifer.andy.simpleeyes.entity.TabInfo\nimport com.jennifer.andy.simpleeyes.ui.base.BaseView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/22 13:53\n * Description:\n */\n\ninterface FeedView : BaseView {\n\n    fun loadTabSuccess(tabInfo: TabInfo)\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/view/RankListView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed.view\n\nimport com.jennifer.andy.simpleeyes.entity.TabInfo\nimport com.jennifer.andy.simpleeyes.ui.base.BaseView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/26 18:06\n * Description:\n */\n\ninterface RankListView : BaseView {\n\n    fun loadTabSuccess(tabInfo: TabInfo)\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/view/TagDetailInfoView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed.view\n\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.LoadMoreView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/3 11:30\n * Description:\n */\n\ninterface TagDetailInfoView : LoadMoreView<AndyInfo> {\n\n    fun showGetTabInfoSuccess(andyInfo: AndyInfo)\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/feed/view/TopicView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.feed.view\n\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.ui.base.LoadMoreView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/31 15:38\n * Description:\n */\n\ninterface TopicView : LoadMoreView<AndyInfo> {\n\n    fun showGetTopicInfoSuccess(itemList: MutableList<Content>)\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/follow/AllAuthorActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.follow\n\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.RelativeLayout\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.BaseActivity\nimport com.jennifer.andy.simpleeyes.ui.base.adapter.BaseDataAdapter\nimport com.jennifer.andy.simpleeyes.ui.follow.presenter.AllAuthorPresenter\nimport com.jennifer.andy.simpleeyes.ui.follow.view.AllAuthorView\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.CustomLoadMoreView\nimport com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-07-09 18:22\n * Description: 全部作者\n */\n\nclass AllAuthorActivity : BaseActivity<AllAuthorView, AllAuthorPresenter>(), AllAuthorView {\n\n    private val mToolBar: RelativeLayout by bindView(R.id.tool_bar)\n    private val mStateView: MultipleStateView by bindView(R.id.multiple_state_view)\n    private val mRecyclerView: RecyclerView by bindView(R.id.rv_recycler)\n    private var mAdapter: BaseDataAdapter? = null\n\n    override fun initView(savedInstanceState: Bundle?) {\n        initToolBar(mToolBar, R.string.all_author)\n        mPresenter.getAllAuthorInfo()\n    }\n\n\n    override fun loadAllAuthorSuccess(it: AndyInfo) {\n        mAdapter = BaseDataAdapter(it.itemList)\n        mAdapter?.setLoadMoreView(CustomLoadMoreView())\n        mAdapter?.setOnLoadMoreListener({ mPresenter.loadMoreInfo() }, mRecyclerView)\n        mRecyclerView.adapter = mAdapter\n        mRecyclerView.layoutManager = LinearLayoutManager(this)\n\n    }\n\n    override fun showNetError(onClickListener: View.OnClickListener) {\n        mStateView.showNetError(onClickListener)\n    }\n\n    override fun showContent() {\n        mStateView.showContent()\n    }\n\n    override fun loadMoreSuccess(data: AndyInfo) {\n        mAdapter?.addData(data.itemList)\n        mAdapter?.loadMoreComplete()\n    }\n\n\n    override fun showNoMore() {\n        mAdapter?.loadMoreEnd()\n    }\n\n\n    override fun getContentViewLayoutId() = R.layout.activity_all_author\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/follow/FollowFragment.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.follow\n\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.ImageView\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.BaseFragment\nimport com.jennifer.andy.simpleeyes.ui.base.adapter.BaseDataAdapter\nimport com.jennifer.andy.simpleeyes.ui.follow.presenter.FollowPresenter\nimport com.jennifer.andy.simpleeyes.ui.follow.view.FollowView\nimport com.jennifer.andy.simpleeyes.ui.search.SearchHotActivity\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.utils.readyGo\nimport com.jennifer.andy.simpleeyes.widget.CustomLoadMoreView\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\nimport com.jennifer.andy.simpleeyes.widget.pull.refresh.PullToRefreshRecyclerView\nimport com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/22 13:51\n * Description: 关注\n */\n\nclass FollowFragment : BaseFragment<FollowView, FollowPresenter>(), FollowView {\n\n\n    private val mTvAllAuthor: CustomFontTextView by bindView(R.id.tv_all_author)\n    private val mIvSearch: ImageView by bindView(R.id.iv_search)\n    private val mStateView: MultipleStateView by bindView(R.id.multiple_state_view)\n    private val mRecyclerView: PullToRefreshRecyclerView by bindView(R.id.rv_follow_recycler)\n    private var mAdapter: BaseDataAdapter? = null\n\n    companion object {\n        @JvmStatic\n        fun newInstance(): FollowFragment = FollowFragment()\n    }\n\n\n    override fun initView(savedInstanceState: Bundle?) {\n        //获取主界面信息\n\n        mPresenter.getFollowInfo()\n\n        //跳转到搜索界面\n        mIvSearch.setOnClickListener {\n            readyGo<SearchHotActivity>()\n        }\n        //跳转到全部作者界面\n        mTvAllAuthor.setOnClickListener {\n            readyGo<AllAuthorActivity>()\n        }\n\n        mRecyclerView.refreshListener = { mPresenter.refresh() }\n    }\n\n    override fun loadFollowInfoSuccess(andyInfo: AndyInfo) {\n        if (mAdapter == null) {\n            mAdapter = BaseDataAdapter(andyInfo.itemList)\n            mAdapter?.setLoadMoreView(CustomLoadMoreView())\n            mAdapter?.setOnLoadMoreListener({ mPresenter.loadMoreInfo() }, mRecyclerView.rootView)\n\n            mRecyclerView.setAdapterAndLayoutManager(mAdapter!!, LinearLayoutManager(context))\n        } else {\n            mAdapter?.setNewData(andyInfo.itemList)\n        }\n    }\n\n    override fun showNetError(onClickListener: View.OnClickListener) {\n        mStateView.showNetError(onClickListener)\n    }\n\n    override fun showContent() {\n        mStateView.showContent()\n    }\n\n    override fun loadMoreSuccess(data: AndyInfo) {\n        mAdapter?.addData(data.itemList)\n        mAdapter?.loadMoreComplete()\n    }\n\n\n    override fun showNoMore() {\n        mAdapter?.loadMoreEnd()\n    }\n\n    override fun refreshSuccess(andyInfo: AndyInfo) {\n        loadFollowInfoSuccess(andyInfo)\n        mRecyclerView.refreshComplete()\n    }\n\n    override fun getContentViewLayoutId() = R.layout.fragment_follow\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/follow/model/FollowModel.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.follow.model\n\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.net.Api\nimport com.jennifer.andy.simpleeyes.rx.RxThreadHelper\nimport com.jennifer.andy.simpleeyes.rx.error.globalHandleError\nimport com.jennifer.andy.simpleeyes.ui.base.model.BaseModel\nimport io.reactivex.Observable\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/22 13:54\n * Description:\n */\n\nclass FollowModel : BaseModel {\n\n\n    /**\n     * 获取关注首页信息\n     */\n    fun getFollowInfo(): Observable<AndyInfo> =\n            Api.getDefault()\n                    .getFollowInfo()\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n\n\n    /**\n     * 获取全部作者\n     */\n    fun getAllAuthor(): Observable<AndyInfo> =\n            Api.getDefault()\n                    .getAllAuthor()\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/follow/presenter/AllAuthorPresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.follow.presenter\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.LoadMorePresenter\nimport com.jennifer.andy.simpleeyes.ui.follow.model.FollowModel\nimport com.jennifer.andy.simpleeyes.ui.follow.view.AllAuthorView\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-07-09 18:23\n * Description:\n */\n\nclass AllAuthorPresenter : LoadMorePresenter<AndyInfo, FollowModel, AllAuthorView>() {\n\n    override var mBaseModel: FollowModel = FollowModel()\n\n    fun getAllAuthorInfo() {\n        mBaseModel.getAllAuthor().autoDispose(mScopeProvider).subscribe({\n            mView?.showContent()\n            mNextPageUrl = it.nextPageUrl\n            mView?.loadAllAuthorSuccess(it)\n        }, {\n            mView?.showNetError(View.OnClickListener { getAllAuthorInfo() })\n        })\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/follow/presenter/FollowPresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.follow.presenter\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.LoadMorePresenter\nimport com.jennifer.andy.simpleeyes.ui.follow.model.FollowModel\nimport com.jennifer.andy.simpleeyes.ui.follow.view.FollowView\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/22 13:54\n * Description:\n */\n\nclass FollowPresenter : LoadMorePresenter<AndyInfo, FollowModel, FollowView>() {\n\n\n    override var mBaseModel: FollowModel = FollowModel()\n\n    fun getFollowInfo() {\n        mBaseModel.getFollowInfo().autoDispose(mScopeProvider).subscribe({\n            mView?.showContent()\n            mNextPageUrl = it.nextPageUrl\n            mView?.loadFollowInfoSuccess(it)\n        }, {\n            mView?.showNetError(View.OnClickListener { getFollowInfo() })\n        })\n    }\n\n\n    fun refresh() {\n        mBaseModel.getFollowInfo().autoDispose(mScopeProvider).subscribe({\n            mView?.showContent()\n            mNextPageUrl = it.nextPageUrl\n            mView?.refreshSuccess(it)\n        }, {\n            mView?.showNetError(View.OnClickListener {\n                refresh()\n            })\n        })\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/follow/view/AllAuthorView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.follow.view\n\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.LoadMoreView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-07-09 18:22\n * Description:\n */\n\ninterface AllAuthorView : LoadMoreView<AndyInfo> {\n\n    fun loadAllAuthorSuccess(it: AndyInfo)\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/follow/view/FollowView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.follow.view\n\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.LoadMoreView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/22 13:54\n * Description:\n */\n\ninterface FollowView : LoadMoreView<AndyInfo> {\n\n    fun loadFollowInfoSuccess(andyInfo: AndyInfo)\n\n    fun refreshSuccess(andyInfo: AndyInfo)\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/home/DailyEliteActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.home\n\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.ImageView\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.ui.base.BaseActivity\nimport com.jennifer.andy.simpleeyes.ui.base.adapter.BaseDataAdapter\nimport com.jennifer.andy.simpleeyes.ui.home.adapter.DailyEliteAdapter\nimport com.jennifer.andy.simpleeyes.ui.home.presenter.DailyElitePresenter\nimport com.jennifer.andy.simpleeyes.ui.home.view.DailyEliteView\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\nimport com.jennifer.andy.simpleeyes.widget.pull.refresh.LinearLayoutManagerWithSmoothScroller\nimport com.jennifer.andy.simpleeyes.widget.pull.refresh.PullToRefreshRecyclerView\nimport com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/20 16:09\n * Description:每日编辑精选\n */\n\nclass DailyEliteActivity : BaseActivity<DailyEliteView, DailyElitePresenter>(), DailyEliteView {\n\n    private val mBackImageView: ImageView by bindView(R.id.iv_back)\n    private val mRecycler: PullToRefreshRecyclerView by bindView(R.id.rv_recycler)\n    private val mTvDate: CustomFontTextView by bindView(R.id.tv_date)\n    private val mStateView: MultipleStateView by bindView(R.id.multiple_state_view)\n\n    private var mDailyEliteAdapter: DailyEliteAdapter? = null\n    private lateinit var mLinearLayoutManager: LinearLayoutManagerWithSmoothScroller\n\n    override fun initView(savedInstanceState: Bundle?) {\n\n        mPresenter.getDailyElite()\n\n        mRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {\n            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {\n                //设置当前选择日期\n                val position = mLinearLayoutManager.findFirstVisibleItemPosition()\n                if (mDailyEliteAdapter?.getItemViewType(position) == BaseDataAdapter.TEXT_CARD_TYPE) {\n                    mTvDate.text = mDailyEliteAdapter?.getItem(position)?.data?.text\n                }\n            }\n\n            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {\n                val layoutManager = mRecycler.rootView.layoutManager as LinearLayoutManager\n                //滚动到日期及完毕\n                if (newState == RecyclerView.SCROLL_STATE_IDLE && layoutManager.findFirstVisibleItemPosition() == mDailyEliteAdapter!!.getCurrentDayPosition()) {\n                    mRecycler.refreshComplete()\n                }\n            }\n        })\n        mRecycler.refreshListener = { mPresenter.refresh() }\n        mBackImageView.setOnClickListener { finish() }\n    }\n\n\n    override fun showGetDailySuccess(content: MutableList<Content>) {\n        if (mDailyEliteAdapter == null) {\n            mDailyEliteAdapter = DailyEliteAdapter(content)\n            mDailyEliteAdapter?.setOnLoadMoreListener({ mPresenter.loadMoreResult() }, mRecycler.rootView)\n            mLinearLayoutManager = LinearLayoutManagerWithSmoothScroller(mContext)\n            mRecycler.setAdapterAndLayoutManager(mDailyEliteAdapter!!, mLinearLayoutManager)\n        } else {\n            mDailyEliteAdapter?.setNewData(content)\n        }\n        //这里有可能加载的每日精选，没有text类型，就会导致刷新完毕后，刷新界面没有消失\n        if (mDailyEliteAdapter!!.getCurrentDayPosition() != 0) {\n            //这里发送延时消息，是因为数据还有可能没有装载完毕\n            mRecycler.handler.postDelayed({ mRecycler.smoothScrollToPosition(mDailyEliteAdapter!!.getCurrentDayPosition()) }, 200)\n        } else {\n            mRecycler.refreshComplete()\n        }\n\n    }\n\n    override fun showRefreshSuccess(content: MutableList<Content>) {\n        showGetDailySuccess(content)\n    }\n\n    override fun loadMoreSuccess(data: MutableList<Content>) {\n        mDailyEliteAdapter?.addData(data)\n        mDailyEliteAdapter?.loadMoreComplete()\n    }\n\n    override fun showNoMore() {\n        mDailyEliteAdapter?.loadMoreEnd()\n    }\n\n    override fun showNetError(onClickListener: View.OnClickListener) {\n        mStateView.showNetError(onClickListener)\n    }\n\n    override fun getContentViewLayoutId() = R.layout.activity_daily_elite\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/home/HomeFragment.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.home\n\nimport android.os.Bundle\nimport android.view.ViewGroup\nimport android.widget.LinearLayout\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.BaseFragment\nimport com.jennifer.andy.simpleeyes.ui.base.adapter.BaseDataAdapter\nimport com.jennifer.andy.simpleeyes.ui.home.presenter.HomePresenter\nimport com.jennifer.andy.simpleeyes.ui.home.view.HomeView\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.utils.getScreenHeight\nimport com.jennifer.andy.simpleeyes.utils.getScreenWidth\nimport com.jennifer.andy.simpleeyes.widget.CustomLoadMoreView\nimport com.jennifer.andy.simpleeyes.widget.pull.head.HomePageHeaderView\nimport com.jennifer.andy.simpleeyes.widget.pull.zoom.PullToZoomBase\nimport com.jennifer.andy.simpleeyes.widget.pull.zoom.PullToZoomRecyclerView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/19 18:45\n * Description:首页\n */\n\nclass HomeFragment : BaseFragment<HomeView, HomePresenter>(), HomeView {\n\n    private val mPullToZoomRecycler: PullToZoomRecyclerView by bindView(R.id.rv_home_recycler)\n    private lateinit var mHomePageHeaderView: HomePageHeaderView\n    private var mCateGoryAdapter: BaseDataAdapter? = null\n\n    companion object {\n        @JvmStatic\n        fun newInstance(): HomeFragment = HomeFragment()\n    }\n\n    override fun initView(savedInstanceState: Bundle?) {\n        mPullToZoomRecycler.setOnPullZoomListener(object : PullToZoomBase.OnPullZoomListener {\n            override fun onPullZooming(scrollValue: Int) {\n                mHomePageHeaderView.showRefreshCover(scrollValue)\n            }\n\n            override fun onPullZoomEnd() {\n                if (mHomePageHeaderView.judgeCanRefresh()) {//只有达到刷新的阀值，上升才刷新，其他情况不刷新\n                    mPresenter.refreshCategoryData()\n                } else {\n                    mHomePageHeaderView.hideRefreshCover()\n                }\n            }\n        })\n        mPresenter.loadCategoryData()\n    }\n\n    override fun loadDataSuccess(andyInfo: AndyInfo) {\n        if (mCateGoryAdapter == null) {\n            setHeaderInfo(andyInfo)\n            setAdapterAndListener(andyInfo)\n        } else {\n            mCateGoryAdapter?.setNewData(andyInfo.itemList)\n        }\n    }\n\n    private fun setAdapterAndListener(andyInfo: AndyInfo) {\n        val recyclerView = mPullToZoomRecycler.getPullRootView()\n        recyclerView.setItemViewCacheSize(10)\n        mCateGoryAdapter = BaseDataAdapter(andyInfo.itemList)\n        mCateGoryAdapter?.setOnLoadMoreListener({ mPresenter.loadMoreCategoryData() }, recyclerView)\n        mCateGoryAdapter?.setLoadMoreView(CustomLoadMoreView())\n        mPullToZoomRecycler.setAdapterAndLayoutManager(mCateGoryAdapter!!, LinearLayoutManager(_mActivity))\n    }\n\n    private fun setHeaderInfo(andyInfo: AndyInfo) {\n        mHomePageHeaderView = HomePageHeaderView(context!!)\n        val lp = ViewGroup.LayoutParams(context!!.getScreenWidth(), context!!.getScreenHeight() / 2)\n        mPullToZoomRecycler.setHeaderViewLayoutParams(LinearLayout.LayoutParams(lp))\n        mHomePageHeaderView.setHeaderInfo(andyInfo.topIssue, andyInfo.topIssue.data.itemList, this)\n        mPullToZoomRecycler.setHeaderView(mHomePageHeaderView)\n    }\n\n\n    override fun refreshDataSuccess(andyInfo: AndyInfo) {\n        mCateGoryAdapter?.removeAllFooterView()\n        mCateGoryAdapter?.setNewData(andyInfo.itemList)\n        mHomePageHeaderView.hideRefreshCover()\n    }\n\n\n    override fun loadMoreSuccess(andyInfo: AndyInfo) {\n        mCateGoryAdapter?.addData(andyInfo.itemList)\n        mCateGoryAdapter?.loadMoreComplete()\n    }\n\n    override fun showNoMore() {\n        mCateGoryAdapter?.loadMoreEnd()\n    }\n\n    fun scrollToTop() {\n        mPullToZoomRecycler.scrollToTop()\n    }\n\n    override fun getContentViewLayoutId() = R.layout.fragment_home\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/home/adapter/CollectionCardCoverAdapter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.home.adapter\n\nimport android.widget.TextView\nimport com.chad.library.adapter.base.BaseQuickAdapter\nimport com.chad.library.adapter.base.BaseViewHolder\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.utils.getElapseTimeForShow\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/11/6 16:14\n * Description:集合卡片适配器\n */\n\nclass CollectionCardCoverAdapter(data: MutableList<Content>) : BaseQuickAdapter<Content, BaseViewHolder>(R.layout.item_collection_card_cover, data) {\n\n    override fun convert(helper: BaseViewHolder, item: Content) {\n        val imageCover = helper.getView<SimpleDraweeView>(R.id.iv_image)\n        val title = helper.getView<TextView>(R.id.tv_title)\n        val desc = helper.getView<TextView>(R.id.tv_desc)\n        helper.setGone(R.id.iv_daily, item.data.library == \"DAILY\")\n        if (item.type != \"actionCard\") {//集合\n            imageCover.setImageURI(item.data.cover.feed)\n            title.text = item.data.title\n            val description = \"#${item.data.category}   /   ${getElapseTimeForShow(item.data.duration)}\"\n            val elite = if (item.data.library == \"DAILY\")\n                \"   /   ${mContext.getString(R.string.elite)}\" else \"\"\n            desc.text = description + elite\n        } else {//显示全部\n            helper.setGone(R.id.tv_show_all, true)\n            helper.setGone(R.id.ll_container, false)\n            helper.addOnClickListener(R.id.tv_show_all)\n        }\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/home/adapter/DailyEliteAdapter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.home.adapter\n\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.ui.base.adapter.BaseDataAdapter\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/20 17:04\n * Description:每日编辑精选\n */\n\nclass DailyEliteAdapter(data: MutableList<Content>) : BaseDataAdapter(data) {\n\n\n    /**\n     * 获取第一个日期位置\n     */\n    fun getCurrentDayPosition(): Int {\n        for (i in 0 until mData.size) {\n            if (getItemViewType(i) == TEXT_CARD_TYPE) {\n                return i\n            }\n        }\n        return 0\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/home/adapter/SquareCollectionAdapter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.home.adapter\n\nimport android.widget.FrameLayout\nimport com.chad.library.adapter.base.BaseQuickAdapter\nimport com.chad.library.adapter.base.BaseViewHolder\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.widget.font.FontType\nimport com.jennifer.andy.simpleeyes.widget.font.TypefaceManager\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/11/6 10:49\n * Description:矩形卡片适配器\n */\n\nclass SquareCollectionAdapter(data: MutableList<Content>) : BaseQuickAdapter<Content, BaseViewHolder>(R.layout.item_square_collection, data) {\n\n    override fun convert(helper: BaseViewHolder, item: Content) {\n        val imageView = helper.getView<SimpleDraweeView>(R.id.iv_simple_image)\n        val coverView = helper.getView<FrameLayout>(R.id.fl_cover)\n        if (item.type != \"actionCard\") {//分类\n            imageView.setImageURI(item.data.image)\n            helper.setText(R.id.tv_title, item.data.title)\n            coverView.foreground = mContext?.resources?.getDrawable(R.drawable.selector_item_square_foreground,null)\n        } else {//显示全部\n            imageView.setImageDrawable(mContext.resources.getDrawable(R.drawable.shape_show_all_border,null))\n            helper.setText(R.id.tv_title, item.data.text)\n            helper.setTextColor(R.id.tv_title, mContext.resources.getColor(R.color.black))\n            helper.setTypeface(R.id.tv_title, TypefaceManager.getTypeFace(FontType.NORMAL))\n            coverView.foreground = null\n\n        }\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/home/model/HomeModel.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.home.model\n\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.entity.JenniferInfo\nimport com.jennifer.andy.simpleeyes.net.Api\nimport com.jennifer.andy.simpleeyes.rx.RxThreadHelper\nimport com.jennifer.andy.simpleeyes.rx.error.globalHandleError\nimport com.jennifer.andy.simpleeyes.ui.base.model.BaseModel\nimport io.reactivex.Observable\nimport java.util.concurrent.TimeUnit\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/19 18:00\n * Description:\n */\n\nclass HomeModel : BaseModel {\n\n    /**\n     * 加载首页信息\n     */\n    fun loadCategoryInfo(): Observable<AndyInfo> =\n            Api.getDefault()\n                    .getHomeInfo()\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n\n    /**\n     * 刷新主页信息，延迟1秒执行\n     */\n    fun refreshCategoryInfo(): Observable<AndyInfo> =\n            Api.getDefault()\n                    .getHomeInfo()\n                    .delay(1000, TimeUnit.MILLISECONDS)\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n\n\n    /**\n     * 热门关键词获取\n     */\n    fun getHotWord(): Observable<MutableList<String>> =\n            Api.getDefault()\n                    .getHotWord()\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n\n    /**\n     * 根据关键字搜索视频\n     */\n    fun searchVideoByWord(word: String): Observable<AndyInfo> =\n            Api.getDefault()\n                    .searchVideoByWord(word)\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n\n    /**\n     * 获取每日编辑精选\n     */\n    fun getDailyElite(): Observable<JenniferInfo> =\n            Api.getDefault()\n                    .getDailyElite()\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/home/presenter/DailyElitePresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.home.presenter\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.entity.JenniferInfo\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport com.jennifer.andy.simpleeyes.ui.home.model.HomeModel\nimport com.jennifer.andy.simpleeyes.ui.home.view.DailyEliteView\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/20 16:12\n * Description:\n */\n\nclass DailyElitePresenter : BasePresenter<DailyEliteView>() {\n\n    private var mHomeModel: HomeModel = HomeModel()\n    private var mNextPageUrl: String? = null\n\n    fun getDailyElite() {\n        mHomeModel.getDailyElite().autoDispose(mScopeProvider).subscribe({\n            mView?.showContent()\n            mNextPageUrl = it.nextPageUrl\n            mView?.showGetDailySuccess(combineContentInfo(it))\n        }, {\n            mView?.showNetError(View.OnClickListener {\n                getDailyElite()\n            })\n        })\n    }\n\n    /**\n     * 刷新\n     */\n    fun refresh() {\n        mHomeModel.getDailyElite().autoDispose(mScopeProvider).subscribe({\n            mView?.showContent()\n            mNextPageUrl = it.nextPageUrl\n            mView?.showRefreshSuccess(combineContentInfo(it))\n        }, {\n            mView?.showNetError(View.OnClickListener {\n                refresh()\n            })\n        })\n    }\n\n    /**\n     * 加載更多\n     */\n    fun loadMoreResult() {\n        mHomeModel.loadMoreJenniferInfo(mNextPageUrl).autoDispose(mScopeProvider).subscribe({\n            mNextPageUrl = it.nextPageUrl\n            mView?.loadMoreSuccess(combineContentInfo(it))\n        }, {\n            mView?.showNetError(View.OnClickListener {\n                getDailyElite()\n            })\n        })\n    }\n\n    private fun combineContentInfo(jenniferInfo: JenniferInfo): MutableList<Content> {\n        val list = mutableListOf<Content>()\n        val issueList = jenniferInfo.issueList\n        for (contentBean in issueList) {\n            val itemList = contentBean.itemList\n            for (content in itemList) {\n                list.add(content)\n            }\n        }\n        return list\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/home/presenter/HomePresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.home.presenter\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport com.jennifer.andy.simpleeyes.ui.home.model.HomeModel\nimport com.jennifer.andy.simpleeyes.ui.home.view.HomeView\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/19 17:58\n * Description:\n */\n\nclass HomePresenter : BasePresenter<HomeView>() {\n\n    private var mHomeModel: HomeModel = HomeModel()\n    private var mNextPageUrl: String? = null\n\n    /**\n     * 加载首页信息\n     */\n    fun loadCategoryData() {\n        mView?.showLoading()\n        mHomeModel.loadCategoryInfo().autoDispose(mScopeProvider).subscribe({\n            mView?.showContent()\n            mNextPageUrl = it.nextPageUrl\n            mView?.loadDataSuccess(it)\n        }, {\n            mView?.showNetError(View.OnClickListener {\n                loadCategoryData()\n            })\n        })\n    }\n\n    /**\n     * 刷新首页信息延迟1秒执行\n     */\n    fun refreshCategoryData() {\n        mHomeModel.refreshCategoryInfo().autoDispose(mScopeProvider).subscribe({\n            mView?.showContent()\n            mNextPageUrl = it.nextPageUrl\n            mView?.refreshDataSuccess(it)\n        }, {\n            mView?.showNetError(View.OnClickListener {\n                refreshCategoryData()\n            })\n        })\n    }\n\n    /**\n     * 加载更多首页信息\n     */\n    fun loadMoreCategoryData() {\n        if (mNextPageUrl != null) {\n            mHomeModel.loadMoreAndyInfo(mNextPageUrl).autoDispose(mScopeProvider).subscribe({\n                mView?.showContent()\n                if (it.nextPageUrl == null) {\n                    mView?.showNoMore()\n                } else {\n                    mNextPageUrl = it.nextPageUrl\n                    mView?.loadMoreSuccess(it)\n                }\n            }, {\n                mView?.showNetError(View.OnClickListener {\n                    loadMoreCategoryData()\n                })\n            })\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/home/view/DailyEliteView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.home.view\n\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.ui.base.LoadMoreView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/20 16:13\n * Description:\n */\n\ninterface DailyEliteView : LoadMoreView<MutableList<Content>> {\n\n    fun showGetDailySuccess(content: MutableList<Content>)\n\n    fun showRefreshSuccess(content: MutableList<Content>)\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/home/view/HomeView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.home.view\n\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.BaseView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/19 17:59\n * Description:\n */\n\ninterface HomeView : BaseView {\n\n    /**\n     * 加载信息成功\n     */\n    fun loadDataSuccess(andyInfo: AndyInfo)\n\n    /**\n     * 加载信息成功\n     */\n    fun refreshDataSuccess(andyInfo: AndyInfo)\n\n    /**\n     * 加载更多成功\n     */\n    fun loadMoreSuccess(andyInfo: AndyInfo)\n\n    /**\n     * 没有更多\n     */\n    fun showNoMore()\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/login/LoginActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.login\n\nimport android.os.Bundle\nimport android.widget.ImageView\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.ui.base.BaseAppCompatActivity\nimport com.jennifer.andy.simpleeyes.utils.bindView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019/1/14 19:22\n * Description:\n */\n@Route(path = \"/github/Login\")\nclass LoginActivity : BaseAppCompatActivity() {\n\n    private val mIvClose: ImageView by bindView(R.id.iv_close)\n\n\n    override fun initView(savedInstanceState: Bundle?) {\n        mIvClose.setOnClickListener { finish() }\n    }\n\n    override fun getContentViewLayoutId() = R.layout.activity_login\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/profile/CacheFragment.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.profile\n\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.ImageView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.ui.base.BaseFragment\nimport com.jennifer.andy.simpleeyes.ui.profile.presenter.CachePresenter\nimport com.jennifer.andy.simpleeyes.ui.profile.view.CacheView\nimport com.jennifer.andy.simpleeyes.utils.bindView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-09-12 15:51\n * Description:我的缓存\n */\n\nclass CacheFragment : BaseFragment<CacheView, CachePresenter>(), CacheView, View.OnClickListener {\n\n    private val mIvBack: ImageView by bindView(R.id.iv_back)\n\n\n    companion object {\n        @JvmStatic\n        fun newInstance(): CacheFragment = CacheFragment()\n    }\n\n    override fun initView(savedInstanceState: Bundle?) {\n        super.initView(savedInstanceState)\n        mIvBack.setOnClickListener(this)\n    }\n\n    override fun onClick(view: View?) {\n        when (view?.id) {\n            R.id.iv_back -> {\n                pop()\n            }\n            else -> {\n            }\n        }\n\n        //todo 这里打算用WorkManager来做\n\n    }\n\n    override fun getContentViewLayoutId() = R.layout.fragment_cache\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/profile/ProfileFragment.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.profile\n\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.FrameLayout\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.ui.base.BaseFragment\nimport com.jennifer.andy.simpleeyes.ui.profile.adapter.ProfileSettingAdapter\nimport com.jennifer.andy.simpleeyes.ui.profile.presenter.ProfilePresenter\nimport com.jennifer.andy.simpleeyes.ui.profile.view.ProfileView\nimport com.jennifer.andy.simpleeyes.utils.bindView\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/22 13:51\n * Description:我的\n */\n\nclass ProfileFragment : BaseFragment<ProfileView, ProfilePresenter>(), ProfileView, View.OnClickListener {\n\n    private val mRecycler: RecyclerView by bindView(R.id.rv_profile_recycler)\n    private val mIvAvatar: SimpleDraweeView by bindView(R.id.iv_avatar)\n    private val mFlComment: FrameLayout by bindView(R.id.fl_comment_container)\n\n\n    companion object {\n        @JvmStatic\n        fun newInstance(): ProfileFragment = ProfileFragment()\n\n        private const val MESSAGE = 0  //我的消息\n        private const val FOCUS = 1 //我的关注\n        private const val CACHE_POSITION = 2//我的缓存\n        private const val WATH_HISTORY = 3//观看记录\n    }\n\n\n    override fun initView(savedInstanceState: Bundle?) {\n\n        mIvAvatar.setOnClickListener(this)\n        mFlComment.setOnClickListener(this)\n        mRecycler.apply {\n            val stringArray = resources.getStringArray(R.array.profile_setting)\n            adapter = ProfileSettingAdapter(arrayListOf(*stringArray))\n                    .apply {\n                        setOnItemClickListener { _, _, position ->\n                            when (position) {\n                                CACHE_POSITION -> {//我的缓存\n                                    start(CacheFragment.newInstance())\n                                }\n                            }\n                        }\n                    }\n            layoutManager = LinearLayoutManager(context)\n        }\n    }\n\n\n    override fun onClick(view: View) {\n        when (view.id) {\n            R.id.iv_avatar,\n            R.id.fl_comment_container -> {//跳转到登录界面\n                ARouter.getInstance().build(\"/github/Login\").navigation()\n            }\n        }\n    }\n\n    override fun getContentViewLayoutId() = R.layout.fragment_profile\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/profile/adapter/ProfileSettingAdapter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.profile.adapter\n\nimport com.chad.library.adapter.base.BaseQuickAdapter\nimport com.chad.library.adapter.base.BaseViewHolder\nimport com.jennifer.andy.simpleeyes.R\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019/1/15 18:59\n * Description:\n */\n\nclass ProfileSettingAdapter(data: MutableList<String>?) : BaseQuickAdapter<String, BaseViewHolder>(R.layout.item_profile_setting, data) {\n\n    override fun convert(helper: BaseViewHolder, item: String) {\n        helper.setText(R.id.tv_text, item)\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/profile/model/ProfileModel.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.profile.model\n\nimport com.jennifer.andy.simpleeyes.ui.base.model.BaseModel\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/22 13:55\n * Description:\n */\n\nclass ProfileModel : BaseModel"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/profile/presenter/CachePresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.profile.presenter\n\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport com.jennifer.andy.simpleeyes.ui.profile.view.CacheView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-09-13 16:09\n * Description:\n */\nclass CachePresenter : BasePresenter<CacheView>() {\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/profile/presenter/ProfilePresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.profile.presenter\n\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport com.jennifer.andy.simpleeyes.ui.profile.view.ProfileView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/22 13:55\n * Description:\n */\n\nclass ProfilePresenter : BasePresenter<ProfileView>() {\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/profile/view/CacheView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.profile.view\n\nimport com.jennifer.andy.simpleeyes.ui.base.BaseView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-09-13 16:08\n * Description:\n */\n\ninterface CacheView : BaseView {\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/profile/view/ProfileView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.profile.view\n\nimport com.jennifer.andy.simpleeyes.ui.base.BaseView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/22 13:56\n * Description:\n */\ninterface ProfileView : BaseView {\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/search/SearchHotActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.search\n\n\nimport android.animation.ValueAnimator\nimport android.graphics.drawable.Drawable\nimport android.os.Bundle\nimport android.text.Spannable\nimport android.text.SpannableStringBuilder\nimport android.view.Gravity\nimport android.view.View\nimport android.view.animation.AccelerateInterpolator\nimport android.widget.RelativeLayout\nimport android.widget.TextView\nimport androidx.appcompat.widget.SearchView\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.chad.library.adapter.base.BaseQuickAdapter\nimport com.google.android.flexbox.FlexDirection\nimport com.google.android.flexbox.FlexboxLayoutManager\nimport com.google.android.flexbox.JustifyContent\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.BaseActivity\nimport com.jennifer.andy.simpleeyes.ui.search.adapter.SearchHotAdapter\nimport com.jennifer.andy.simpleeyes.ui.search.adapter.SearchVideoAdapter\nimport com.jennifer.andy.simpleeyes.ui.search.presenter.SearchPresenter\nimport com.jennifer.andy.simpleeyes.ui.search.view.SearchHotView\nimport com.jennifer.andy.simpleeyes.ui.video.VideoDetailActivity\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.utils.dip2px\nimport com.jennifer.andy.simpleeyes.utils.showKeyboard\nimport com.jennifer.andy.simpleeyes.widget.CustomLoadMoreView\nimport com.jennifer.andy.simpleeyes.widget.SearchHotRemindView\nimport com.jennifer.andy.simpleeyes.widget.image.CenterAlignImageSpan\nimport com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/3 10:54\n * Description:搜索界面\n */\n\nclass SearchHotActivity : BaseActivity<SearchHotView, SearchPresenter>(), SearchHotView {\n\n    private val mSearchRemind: SearchHotRemindView by bindView(R.id.rl_search_remind)\n    private val mSearchView: SearchView by bindView(R.id.searchView)\n    private val mTvCancel: TextView by bindView(R.id.tv_cancel)\n    private val multipleStateView: MultipleStateView by bindView(R.id.multiple_state_view)\n    private val mRecycler: RecyclerView by bindView(R.id.rv_search_recycler)\n\n    private lateinit var mHotSearchAdapter: SearchHotAdapter\n    private lateinit var mSearchVideoAdapter: SearchVideoAdapter\n\n    override fun initView(savedInstanceState: Bundle?) {\n        initSearchView()\n        mTvCancel.setOnClickListener { finish() }\n        mPresenter.searchHot()\n    }\n\n    private fun initSearchView() {\n        mSearchView.isIconified = false\n        mSearchView.setIconifiedByDefault(false)\n\n        //设置输入框提示文字样式\n        val searchComplete = mSearchView.findViewById<SearchView.SearchAutoComplete>(R.id.search_src_text)\n        searchComplete.gravity = Gravity.CENTER\n        searchComplete.setHintTextColor(resources.getColor(R.color.gray_66A2A2A2))\n        searchComplete.textSize = 13f\n        searchComplete.hint = getDecoratedHint(searchComplete.hint, getDrawable(R.drawable.ic_action_search_no_padding), 50)\n        //添加搜索监听\n        mSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {\n            override fun onQueryTextSubmit(query: String): Boolean {\n                showKeyboard(false)\n                mPresenter.searchVideoByWord(query)\n                return true\n            }\n\n            override fun onQueryTextChange(newText: String?): Boolean {\n                return false\n            }\n\n        })\n    }\n\n    /**\n     * 设置输入框提示文字\n     */\n    private fun getDecoratedHint(hintText: CharSequence, searchHintIcon: Drawable, drawableSize: Int): CharSequence {\n        searchHintIcon.setBounds(0, 0, drawableSize, drawableSize)\n        val ssb = SpannableStringBuilder(\"   \")\n        ssb.setSpan(CenterAlignImageSpan(searchHintIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)\n        ssb.append(hintText)\n        return ssb\n    }\n\n\n    override fun getHotWordSuccess(hotList: MutableList<String>) {\n        setRecyclerMargin()\n        startContentAnimation()\n        mHotSearchAdapter = SearchHotAdapter(hotList)\n        mHotSearchAdapter.setOnItemClickListener { _, _, position ->\n            showKeyboard(false)\n            mPresenter.searchVideoByWord(mHotSearchAdapter.getItem(position)!!)\n        }\n        val flexBoxLayoutManager = FlexboxLayoutManager(mContext, FlexDirection.ROW)\n        flexBoxLayoutManager.justifyContent = JustifyContent.CENTER\n        mRecycler.layoutManager = flexBoxLayoutManager\n        mRecycler.adapter = mHotSearchAdapter\n\n    }\n\n    /**\n     * 进入内容动画\n     */\n    private fun startContentAnimation() {\n        val valueAnimator = ValueAnimator.ofInt(multipleStateView.measuredHeight, 0)\n        valueAnimator.duration = 500\n        valueAnimator.interpolator = AccelerateInterpolator()\n        valueAnimator.addUpdateListener {\n            val value = it.animatedValue as Int\n            multipleStateView.scrollTo(0, value)\n        }\n        valueAnimator.start()\n    }\n\n\n    override fun showSearchSuccess(queryWord: String, andyInfo: AndyInfo) {\n\n        //设置搜索结果\n        mSearchRemind.setSearchResult(queryWord, andyInfo.total)\n\n        //添加了过滤规则,防止搜索的时候崩溃\n        mSearchVideoAdapter = SearchVideoAdapter(andyInfo.itemList.filter { it.type == SearchVideoAdapter.VIDEO_COLLECTION_WITH_BRIEF || it.type == SearchVideoAdapter.VIDEO })\n\n\n        //跳转到播放界面\n        mSearchVideoAdapter.onItemClickListener = BaseQuickAdapter.OnItemClickListener { _, _, position ->\n            val item = mSearchVideoAdapter.getItem(position)\n            if (item?.type == SearchVideoAdapter.VIDEO) {\n                VideoDetailActivity.start(mContext, item.data, arrayListOf(), position)\n            }\n        }\n\n\n        if (andyInfo.itemList.size > 0) {\n            resetRecyclerMargin()\n        } else {\n            mSearchVideoAdapter.setEmptyView(R.layout.empty_search_word, mRecycler)\n        }\n\n        mSearchVideoAdapter.setOnLoadMoreListener({ mPresenter.loadMoreInfo() }, mRecycler)\n        mSearchVideoAdapter.setLoadMoreView(CustomLoadMoreView())\n        mRecycler.layoutManager = LinearLayoutManager(mContext)\n        mRecycler.adapter = mSearchVideoAdapter\n\n        //处理没有更多的情况\n        if (andyInfo.nextPageUrl == null) {\n            mSearchVideoAdapter.loadMoreEnd()\n        }\n\n    }\n\n    override fun loadMoreSuccess(andyInfo: AndyInfo) {\n        mSearchVideoAdapter.addData(andyInfo.itemList)\n        mSearchVideoAdapter.loadMoreComplete()\n    }\n\n    override fun showNoMore() {\n        mSearchVideoAdapter.loadMoreEnd()\n    }\n\n    override fun showLoading() {\n        multipleStateView.showLoading()\n    }\n\n    override fun showContent() {\n        multipleStateView.showContent()\n    }\n\n    override fun showNetError(onClickListener: View.OnClickListener) {\n        multipleStateView.showNetError(onClickListener)\n    }\n\n\n    /**\n     * 重置RecyclerMargin\n     */\n    private fun resetRecyclerMargin() {\n        val lp = mRecycler.layoutParams as RelativeLayout.LayoutParams\n        lp.marginEnd = 0\n        lp.marginStart = 0\n        mRecycler.layoutParams = lp\n    }\n\n    /**\n     * 设置RecyclerMargin\n     */\n    private fun setRecyclerMargin() {\n        val lp = mRecycler.layoutParams as RelativeLayout.LayoutParams\n        lp.marginEnd = dip2px(30f)\n        lp.marginStart = dip2px(30f)\n        mRecycler.layoutParams = lp\n    }\n\n\n    override fun toggleOverridePendingTransition() = true\n\n    override fun getOverridePendingTransition() = TransitionMode.TOP\n\n    override fun getContentViewLayoutId() = R.layout.fragment_search_hot\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/search/adapter/CollectionBriefAdapter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.search.adapter\n\nimport com.chad.library.adapter.base.BaseQuickAdapter\nimport com.chad.library.adapter.base.BaseViewHolder\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.utils.getElapseTimeForShow\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/9 11:01\n * Description:\n */\n\nclass CollectionBriefAdapter(data: MutableList<Content>) : BaseQuickAdapter<Content, BaseViewHolder>(R.layout.item_collection_brief, data) {\n\n    override fun convert(helper: BaseViewHolder, item: Content) {\n\n        with(helper) {\n            getView<SimpleDraweeView>(R.id.iv_image).setImageURI(item.data.cover.feed)\n            setText(R.id.tv_title, item.data.title)\n            val description = \"#${item.data.category}   /   ${getElapseTimeForShow(item.data.duration)}\"\n            setText(R.id.tv_desc, description)\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/search/adapter/SearchHotAdapter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.search.adapter\n\nimport com.chad.library.adapter.base.BaseQuickAdapter\nimport com.chad.library.adapter.base.BaseViewHolder\nimport com.jennifer.andy.simpleeyes.R\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/8 10:16\n * Description:\n */\n\nclass SearchHotAdapter(data: MutableList<String>) : BaseQuickAdapter<String, BaseViewHolder>(R.layout.item_hot_search, data) {\n\n    override fun convert(helper: BaseViewHolder, item: String) {\n        helper.setText(R.id.tv_text, item)\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/search/adapter/SearchVideoAdapter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.search.adapter\n\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.chad.library.adapter.base.BaseQuickAdapter\nimport com.chad.library.adapter.base.BaseQuickAdapter.OnItemClickListener\nimport com.chad.library.adapter.base.BaseViewHolder\nimport com.chad.library.adapter.base.util.MultiTypeDelegate\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.ui.video.VideoDetailActivity\nimport com.jennifer.andy.simpleeyes.utils.getElapseTimeForShow\nimport com.jennifer.andy.simpleeyes.widget.ItemHeaderView\nimport java.util.*\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/9 10:19\n * Description:\n */\n\nclass SearchVideoAdapter(data: List<Content>?) : BaseQuickAdapter<Content, BaseViewHolder>(data) {\n\n    /**\n     * 卡片类型\n     */\n    companion object {\n\n        const val VIDEO_COLLECTION_WITH_BRIEF_TYPE = 0\n        const val VIDEO_TYPE = 1\n\n        const val VIDEO_COLLECTION_WITH_BRIEF = \"videoCollectionWithBrief\"\n        const val VIDEO = \"video\"\n    }\n\n\n    init {\n        multiTypeDelegate = object : MultiTypeDelegate<Content>() {\n            override fun getItemType(content: Content?): Int {\n                when (content?.type) {\n                    VIDEO_COLLECTION_WITH_BRIEF -> return VIDEO_COLLECTION_WITH_BRIEF_TYPE\n                    VIDEO -> return VIDEO_TYPE\n                }\n                return VIDEO_TYPE\n            }\n        }\n        with(multiTypeDelegate) {\n            registerItemType(VIDEO_COLLECTION_WITH_BRIEF_TYPE, R.layout.layout_collection_with_brief)\n            registerItemType(VIDEO_TYPE, R.layout.layout_single_video)\n        }\n\n    }\n\n    override fun convert(helper: BaseViewHolder?, content: Content) {\n        when (helper?.itemViewType) {\n            VIDEO_COLLECTION_WITH_BRIEF_TYPE -> setCollectionBriefInfo(helper, content)\n            VIDEO_TYPE -> setSingleVideoInfo(helper, content)\n        }\n    }\n\n    /**\n     * 设置带关注人的视频集合信息\n     */\n    private fun setCollectionBriefInfo(helper: BaseViewHolder, content: Content) {\n        //设置视频集合信息\n        with(helper) {\n            getView<RecyclerView>(R.id.rv_recycler).apply {\n                isNestedScrollingEnabled = false\n                layoutManager = LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false)\n                adapter = CollectionBriefAdapter(content.data.itemList).apply {\n                    onItemClickListener = OnItemClickListener { _, _, position ->\n                        //跳转到播放视频详情\n                        val item = getItem(position)\n                        VideoDetailActivity.start(mContext, item!!.data, data as ArrayList<Content>, position)\n\n                    }\n                }\n\n            }\n            //设置头布局\n            content.data.header?.let { getView<ItemHeaderView>(R.id.item_header_view).setHeader(it, content.data.dataType) }\n\n        }\n\n    }\n\n    /**\n     * 设置单视频信息\n     */\n    private fun setSingleVideoInfo(helper: BaseViewHolder, item: Content) {\n        val imageView = helper.getView<SimpleDraweeView>(R.id.iv_image)\n        imageView.setImageURI(item.data.cover.feed)\n        helper.setText(R.id.tv_single_title, item.data.title)\n        val description = \"#${item.data.category}   /   ${getElapseTimeForShow(item.data.duration)}\"\n        helper.setText(R.id.tv_single_desc, description)\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/search/presenter/SearchPresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.search.presenter\n\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.LoadMorePresenter\nimport com.jennifer.andy.simpleeyes.ui.home.model.HomeModel\nimport com.jennifer.andy.simpleeyes.ui.search.view.SearchHotView\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/3 11:05\n * Description:\n */\n\nclass SearchPresenter : LoadMorePresenter<AndyInfo, HomeModel, SearchHotView>() {\n\n    override var mBaseModel: HomeModel = HomeModel()\n\n    /**\n     * 获取热门搜索\n     */\n    fun searchHot() {\n        mBaseModel.getHotWord().autoDispose(mScopeProvider).subscribe {\n            mView?.getHotWordSuccess(it)\n        }\n    }\n\n    /**\n     * 根据关键字搜索\n     */\n    fun searchVideoByWord(word: String) {\n        mView?.showLoading()\n        mBaseModel.searchVideoByWord(word).autoDispose(mScopeProvider).subscribe({\n            mView?.showContent()\n            mView?.showSearchSuccess(word, it)\n            mNextPageUrl = it.nextPageUrl\n        }, {\n            mView?.showNetError(View.OnClickListener { searchVideoByWord(word) })\n        })\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/search/view/SearchView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.search.view\n\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.ui.base.LoadMoreView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/3 11:06\n * Description:\n */\n\ninterface SearchHotView : LoadMoreView<AndyInfo> {\n\n    /**\n     * 获取热门关键词成功\n     */\n    fun getHotWordSuccess(hotList: MutableList<String>)\n\n    /**\n     * 搜索成功\n     */\n    fun showSearchSuccess(queryWord: String, andyInfo: AndyInfo)\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/splash/LandingActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.splash\n\nimport android.os.Bundle\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.UserPreferences\nimport com.jennifer.andy.simpleeyes.ui.base.BaseAppCompatActivity\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/8/3 09:45\n * Description:加载页，用于区分显示广告，还是视频，还是本地的加载页\n */\n\nclass LandingActivity : BaseAppCompatActivity() {\n\n    override fun initView(savedInstanceState: Bundle?) {\n        if (UserPreferences.getUserIsFirstLogin()) {//如果是第一次进入就加载视频界面\n            loadRootFragment(R.id.fl_container, VideoLandingFragment.newInstance())\n        } else {//如果不是加载常规加载界面\n            loadRootFragment(R.id.fl_container, LocalCommonLandingFragment.newInstance())\n        }\n    }\n\n    override fun getContentViewLayoutId(): Int = R.layout.activity_landing\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/splash/LocalCommonLandingFragment.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.splash\n\nimport android.animation.AnimatorSet\nimport android.animation.ObjectAnimator\nimport android.animation.ValueAnimator\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.ImageView\nimport android.widget.RelativeLayout\nimport android.widget.TextView\nimport androidx.core.animation.doOnEnd\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.UserPreferences\nimport com.jennifer.andy.simpleeyes.ui.MainActivity\nimport com.jennifer.andy.simpleeyes.ui.base.BaseAppCompatFragment\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.utils.dip2px\nimport com.jennifer.andy.simpleeyes.utils.getDateString\nimport com.jennifer.andy.simpleeyes.utils.readyGoThenKillSelf\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\nimport java.util.*\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/8/3 09:56\n * Description:本地的加载页(上升动画）\n */\n\nclass LocalCommonLandingFragment : BaseAppCompatFragment() {\n\n    private val mIvBackground by bindView<SimpleDraweeView>(R.id.iv_background)\n    private val mLoadingContainer by bindView<RelativeLayout>(R.id.rl_loading_container)\n    private val mMoveContainer by bindView<RelativeLayout>(R.id.ll_move_container)\n    private val mHeadOuter by bindView<ImageView>(R.id.iv_head_outer)\n    private val mHeadInner by bindView<ImageView>(R.id.iv_head_inner)\n    private val mName by bindView<CustomFontTextView>(R.id.tv_name)\n\n    private val mForToday by bindView<CustomFontTextView>(R.id.tv_today)\n    private val mDate by bindView<CustomFontTextView>(R.id.tv_date)\n    private val mTodayChose by bindView<CustomFontTextView>(R.id.tv_today_chose)\n\n\n    companion object {\n        @JvmStatic\n        fun newInstance(): LocalCommonLandingFragment = LocalCommonLandingFragment()\n    }\n\n    override fun initView(savedInstanceState: Bundle?) {\n        //如果用户没有执行上升动画，则执行，反之则执行缩放动画\n        if (!UserPreferences.getShowUserAnim()) {\n            doUpAnimator()\n            doBackgroundAnimator()\n        } else {\n            doScaleAnimator()\n        }\n    }\n\n\n    /**\n     * 执行上升动画\n     */\n    private fun doUpAnimator() {\n        val moveY = _mActivity.dip2px(100f)\n        ObjectAnimator.ofFloat(mMoveContainer, \"translationY\", 0f, -moveY.toFloat()).apply {\n            addUpdateListener {\n                if (it.currentPlayTime in 600..1500) {\n                    mHeadOuter.setImageResource(R.drawable.ic_eye_white_outer)\n                    mHeadInner.setImageResource(R.drawable.ic_eye_white_inner)\n                    mName.setTextColor(resources.getColor(R.color.gray_B7B9B8))\n\n                } else if (it.currentPlayTime in 1500..2000) {\n                    mHeadOuter.setImageResource(R.drawable.ic_eye_black_outer)\n                    mHeadInner.setImageResource(R.drawable.ic_eye_black_inner)\n\n                    mName.setTextColor(resources.getColor(R.color.black_444444))\n                }\n            }\n            duration = 2000\n        }.start()\n    }\n\n    /**\n     * 执行背景动画\n     */\n    private fun doBackgroundAnimator() {\n        ValueAnimator.ofArgb(0, 0xffffffff.toInt()).apply {\n            addUpdateListener { mLoadingContainer.setBackgroundColor(it.animatedValue as Int) }\n            doOnEnd { doTextAnimator() }\n            duration = 2000\n        }.start()\n    }\n\n    /**\n     * 显示 today 文字与精选\n     */\n    private fun doTextAnimator() {\n        ValueAnimator.ofArgb(0, 0xff444444.toInt()).apply {\n            addUpdateListener {\n                val color = it.animatedValue as Int\n                setTextColor(mForToday, color)\n                setTextColor(mDate, color)\n                setTextColor(mTodayChose, color)\n                mDate.text = getDateString(Date(), \"- yyyy/MM/dd -\")\n            }\n\n            doOnEnd { doInnerEyeAnimator() }\n        }.start()\n    }\n\n    private fun setTextColor(textView: TextView, color: Int) {\n        textView.visibility = View.VISIBLE\n        textView.setTextColor(color)\n    }\n\n    /**\n     * 执行内部眼睛动画\n     */\n    private fun doInnerEyeAnimator() {\n        ObjectAnimator.ofFloat(mHeadInner, \"rotation\", 0f, 360f).apply {\n            doOnEnd {\n                readyGoThenKillSelf<MainActivity>()\n                UserPreferences.saveShowUserAnim(true)\n            }\n            duration = 1000\n        }.start()\n    }\n\n    /**\n     * 执行背景缩放动画\n     */\n    private fun doScaleAnimator() {\n\n        AnimatorSet().apply {\n            val scaleX = ObjectAnimator.ofFloat(mIvBackground, \"scaleX\", 1f, 1.08f)\n            val scaleY = ObjectAnimator.ofFloat(mIvBackground, \"scaleY\", 1f, 1.08f)\n            playTogether(scaleX, scaleY)\n            doOnEnd {\n                readyGoThenKillSelf<MainActivity>()\n                UserPreferences.saveShowUserAnim(true)\n            }\n            duration = 2000\n        }.start()\n    }\n\n    override fun getContentViewLayoutId() = R.layout.fragment_local_coomon_landing\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/splash/SloganFragment.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.splash\n\nimport android.os.Bundle\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.ui.base.BaseAppCompatFragment\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/5/15 10:51\n * Description: 口号fragment 用于切换视频口号\n */\n\nclass SloganFragment : BaseAppCompatFragment() {\n\n\n    companion object {\n        @JvmStatic\n        fun newInstance() = SloganFragment()\n    }\n\n\n    override fun initView(savedInstanceState: Bundle?) {\n\n    }\n\n\n    override fun getContentViewLayoutId() = R.layout.fragment_slogan\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/splash/VideoLandingFragment.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.splash\n\nimport android.os.Bundle\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.LifecycleObserver\nimport androidx.lifecycle.OnLifecycleEvent\nimport androidx.viewpager.widget.ViewPager\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.UserPreferences\nimport com.jennifer.andy.simpleeyes.player.render.IRenderView.Companion.AR_ASPECT_FIT_PARENT\nimport com.jennifer.andy.simpleeyes.ui.MainActivity\nimport com.jennifer.andy.simpleeyes.ui.base.BaseAppCompatFragment\nimport com.jennifer.andy.simpleeyes.ui.splash.adapter.SplashVideoFragmentAdapter\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.utils.readyGoThenKillSelf\nimport com.jennifer.andy.simpleeyes.widget.FullScreenVideoView\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTypeWriterTextView\nimport com.jennifer.andy.simpleeyes.widget.viewpager.InterceptVerticalViewPager\nimport com.rd.PageIndicatorView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/8/3 10:08\n * Description:视频闪屏页 第一次用户安装应用的时候会走\n */\n\nclass VideoLandingFragment : BaseAppCompatFragment() {\n\n    private val mVideoView: FullScreenVideoView by bindView(R.id.video_view)\n    private val mViewPager: InterceptVerticalViewPager by bindView(R.id.view_pager)\n    private val mTvSloganChinese: CustomFontTypeWriterTextView by bindView(R.id.tv_slogan_zh)\n    private val mTvSloganEnglish: CustomFontTypeWriterTextView by bindView(R.id.tv_slogan_en)\n    private val mIndicator: PageIndicatorView by bindView(R.id.pageIndicatorView)\n\n    private var mVideoPosition = 0\n    private var isHasPaused = false\n    private lateinit var mFragmentList: MutableList<SloganFragment>\n\n\n    companion object {\n        @JvmStatic\n        fun newInstance(): VideoLandingFragment = VideoLandingFragment()\n    }\n\n    override fun initView(savedInstanceState: Bundle?) {\n        initSloganText()\n        setVideoObserver()\n        playVideo()\n    }\n\n\n    /**\n     * 这里我采用的比较暴力的方法，主要是不想写事件拦截了，想写的小伙伴，可以自己去写\n     */\n    private fun initSloganText() {\n        //设置初始标语\n        mTvSloganEnglish.printText(resources.getStringArray(R.array.slogan_array_en)[0])\n        mTvSloganChinese.printText(resources.getStringArray(R.array.slogan_array_zh)[0])\n\n        mIndicator.count = 4\n\n        mFragmentList = List(4) { SloganFragment.newInstance() } as MutableList<SloganFragment>\n\n        with(mViewPager) {\n            verticalListener = { goMainActivity() }\n            horizontalListener = { goMainActivity() }\n            mDisMissIndex = mFragmentList.size - 1\n            adapter = SplashVideoFragmentAdapter(mFragmentList, childFragmentManager)\n\n            addOnPageChangeListener(object : ViewPager.OnPageChangeListener {\n                override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {\n                    mIndicator.setSelected(position)\n                }\n\n                override fun onPageScrollStateChanged(state: Int) {\n                }\n\n                override fun onPageSelected(position: Int) {\n                    if (position in 0..3) {\n                        mTvSloganEnglish.printText(resources.getStringArray(R.array.slogan_array_en)[position])\n                        mTvSloganChinese.printText(resources.getStringArray(R.array.slogan_array_zh)[position])\n                    }\n                }\n            })\n        }\n    }\n\n    /**\n     * 设置用户不是第一次登录,并跳转到主界面\n     */\n    private fun goMainActivity() {\n        UserPreferences.saveUserIsFirstLogin(false)\n        readyGoThenKillSelf<MainActivity>()\n    }\n\n    private fun playVideo() {\n        val path = R.raw.landing\n        with(mVideoView) {\n            setAspectRatio(AR_ASPECT_FIT_PARENT)\n            setVideoPath(\"android.resource://${activity?.packageName}/$path\")\n            setOnPreparedListener {\n                requestFocus()\n                seekTo(0)\n                start()\n            }\n            setOnCompletionListener {\n                it.isLooping = true\n                start()\n            }\n        }\n    }\n\n    private fun setVideoObserver() {\n        lifecycle.addObserver(object : LifecycleObserver {\n\n            @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)\n            fun onVideoResume() {\n                if (isHasPaused) {\n                    mVideoView.seekTo(mVideoPosition)\n                    mVideoView.resume()\n                    isHasPaused = false\n                }\n            }\n\n            @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)\n            fun onVideoPause() {\n                mVideoPosition = mVideoView.currentPosition\n                mVideoView.pause()\n                isHasPaused = true\n            }\n\n            @OnLifecycleEvent(Lifecycle.Event.ON_STOP)\n            fun onVideoStop() {\n                mVideoView.stopPlayback()\n            }\n        })\n    }\n\n    override fun getContentViewLayoutId() = R.layout.fragment_video_landing\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/splash/adapter/SplashVideoFragmentAdapter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.splash.adapter\n\nimport androidx.fragment.app.FragmentManager\nimport androidx.fragment.app.FragmentPagerAdapter\nimport com.jennifer.andy.simpleeyes.ui.splash.SloganFragment\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/5/15 10:59\n * Description: 闪屏视频适配器\n */\n\nclass SplashVideoFragmentAdapter(var mFragmentList: MutableList<SloganFragment>,\n                                 fragmentManager: FragmentManager) : FragmentPagerAdapter(fragmentManager) {\n\n\n    override fun getItem(position: Int) = mFragmentList[position]\n\n\n    override fun getCount() = mFragmentList.size\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/video/VideoDetailActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.video\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Color\nimport android.graphics.drawable.ColorDrawable\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.widget.SeekBar\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.chad.library.adapter.base.BaseQuickAdapter\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.entity.ContentBean\nimport com.jennifer.andy.simpleeyes.net.Extras\nimport com.jennifer.andy.simpleeyes.player.IjkMediaController\nimport com.jennifer.andy.simpleeyes.player.IjkVideoViewWrapper\nimport com.jennifer.andy.simpleeyes.player.event.VideoProgressEvent\nimport com.jennifer.andy.simpleeyes.player.render.IRenderView\nimport com.jennifer.andy.simpleeyes.rx.RxBus\nimport com.jennifer.andy.simpleeyes.ui.base.BaseActivity\nimport com.jennifer.andy.simpleeyes.ui.video.adapter.VideoDetailAdapter\nimport com.jennifer.andy.simpleeyes.ui.video.presenter.VideoDetailPresenter\nimport com.jennifer.andy.simpleeyes.ui.video.view.VideoDetailView\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.VideoDetailAuthorView\nimport com.jennifer.andy.simpleeyes.widget.pull.head.VideoDetailHeadView\nimport io.reactivex.functions.Consumer\nimport tv.danmaku.ijk.media.player.IMediaPlayer\nimport java.util.*\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/12/18 16:51\n * Description: 视频详细信息界面\n */\nclass VideoDetailActivity : BaseActivity<VideoDetailView, VideoDetailPresenter>(), VideoDetailView {\n\n    private val mBlurredImage by bindView<SimpleDraweeView>(R.id.iv_blurred)\n    private val ijkVideoViewWrapper by bindView<IjkVideoViewWrapper>(R.id.video_view_wrapper)\n    private val mSeekBar by bindView<SeekBar>(R.id.seek_bar)\n    private val mRecycler by bindView<RecyclerView>(R.id.rv_video_recycler)\n\n    private lateinit var mCurrentVideoInfo: ContentBean\n    private var mCurrentIndex = 0\n    private lateinit var mVideoListInfo: MutableList<Content>\n    private var mBackPressed = false\n\n    private lateinit var ijkMediaController: IjkMediaController\n\n    private var mChangeProgress: Int = 0\n\n\n    companion object {\n\n        /**\n         * 跳转到视频详细界面\n         */\n        @JvmStatic\n        fun start(context: Context, content: ContentBean, videoListInfo: ArrayList<Content>, defaultIndex: Int = 0) {\n            val bundle = Bundle()\n            bundle.putSerializable(Extras.VIDEO_INFO, content)\n            bundle.putSerializable(Extras.VIDEO_LIST_INFO, videoListInfo)\n            bundle.putInt(Extras.VIDEO_INFO_INDEX, defaultIndex)\n            val starter = Intent(context, VideoDetailActivity::class.java)\n            starter.putExtras(bundle)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun getBundleExtras(extras: Bundle) {\n        mCurrentVideoInfo = extras.getSerializable(Extras.VIDEO_INFO) as ContentBean\n        mVideoListInfo = extras.getSerializable(Extras.VIDEO_LIST_INFO) as ArrayList<Content>\n        mCurrentIndex = extras.getInt(Extras.VIDEO_INFO_INDEX, 0)\n    }\n\n    override fun initView(savedInstanceState: Bundle?) {\n        initPlaceHolder()\n        initMediaController()\n        initSeekBar()\n        registerProgressEvent()\n        playVideo()\n    }\n\n\n    private fun initPlaceHolder() {\n        ijkVideoViewWrapper.setPlaceImageUrl(mCurrentVideoInfo.cover.detail)\n        mBlurredImage.setImageURI(mCurrentVideoInfo.cover.blurred)\n    }\n\n    private fun initMediaController() {\n        ijkMediaController = IjkMediaController(this)\n        ijkMediaController.initData(mCurrentIndex, mVideoListInfo.size, mCurrentVideoInfo)\n        ijkMediaController.controllerListener = object : IjkMediaController.ControllerListener {\n            override fun onBackClick() {\n                finish()\n            }\n\n            override fun onNextClick() {\n                mCurrentIndex = ++mCurrentIndex\n                ijkMediaController.currentIndex = mCurrentIndex\n                refreshVideo(getFutureVideo())\n            }\n\n            override fun onFullScreenClick() {\n                ijkVideoViewWrapper.enterFullScreen()\n            }\n\n            override fun onTinyScreenClick() {\n                ijkVideoViewWrapper.exitFullScreen()\n            }\n\n            override fun onPreClick() {\n                mCurrentIndex = --mCurrentIndex\n                ijkMediaController.currentIndex = mCurrentIndex\n                refreshVideo(getFutureVideo())\n            }\n\n            override fun onErrorViewClick() {\n                ijkMediaController.hide()\n                refreshVideo(getFutureVideo())\n            }\n\n            override fun onShowController(isShowController: Boolean) {\n                mSeekBar.thumb = if (isShowController)\n                    getDrawable(R.drawable.ic_player_progress_handle)\n                else ColorDrawable(Color.TRANSPARENT)\n            }\n\n        }\n    }\n\n\n    private fun initSeekBar() {\n        mSeekBar.thumb = ColorDrawable(Color.TRANSPARENT)\n        mSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {\n            override fun onStartTrackingTouch(bar: SeekBar) {\n                //控制的时候停止更新进度条，同时禁止隐藏\n                ijkVideoViewWrapper.setDragging(true)\n                ijkVideoViewWrapper.showControllerAllTheTime()\n\n            }\n\n            override fun onProgressChanged(bar: SeekBar, progress: Int, fromUser: Boolean) {\n                if (fromUser) {\n                    //更新当前播放时间\n                    mChangeProgress = progress\n                }\n            }\n\n            override fun onStopTrackingTouch(bar: SeekBar) {\n                //定位都拖动位置\n                val newPosition = ijkVideoViewWrapper.getDuration() * mChangeProgress / 1000L\n                ijkVideoViewWrapper.seekTo(newPosition.toInt())\n                ijkVideoViewWrapper.showController()\n                ijkVideoViewWrapper.setDragging(false)\n            }\n        })\n    }\n\n\n    /**\n     * 重置视频信息\n     */\n    private fun refreshVideo(videoInfo: Content?) {\n        videoInfo?.let {\n            mCurrentVideoInfo = videoInfo.data\n\n            mRecycler.visibility = View.INVISIBLE\n            ijkVideoViewWrapper.togglePlaceImage(true)\n\n            mSeekBar.secondaryProgress = 0\n            mSeekBar.progress = 0\n\n            initPlaceHolder()\n\n            //重置视频播放信息\n            ijkVideoViewWrapper.setVideoPath(mCurrentVideoInfo.playUrl)\n            ijkVideoViewWrapper.start()\n            //获取关联视频信息\n            mPresenter.getRelatedVideoInfo(mCurrentVideoInfo.id)\n        }\n\n    }\n\n    /**\n     * 获取即将展示的视频\n     */\n    private fun getFutureVideo(): Content {\n        return if (mVideoListInfo[mCurrentIndex].data.content != null) {\n            mVideoListInfo[mCurrentIndex].data.content!!\n        } else {\n            mVideoListInfo[mCurrentIndex]\n        }\n\n    }\n\n    private fun registerProgressEvent() {\n        //注册进度条监听\n        RxBus.register(this, VideoProgressEvent::class.java, Consumer {\n            //如果正在拖动的话，不更新进度条\n            if (!ijkVideoViewWrapper.isDragging()) {\n                mSeekBar.progress = it.progress\n            }\n            mSeekBar.secondaryProgress = it.secondaryProgress\n        })\n\n    }\n\n\n    /**\n     * 播放视频\n     */\n    private fun playVideo() {\n        with(ijkVideoViewWrapper) {\n            //设置视频准备完成监听\n            setOnPreparedListener(IMediaPlayer.OnPreparedListener {\n                resetType()\n                hidePlaceImage()\n            })\n\n            //设置视频播放失败监听\n            setOnErrorListener(IMediaPlayer.OnErrorListener { _, _, _ ->\n                showErrorView()\n                true\n            })\n\n            //设置视频播放完成监听\n            setOnCompletionListener(IMediaPlayer.OnCompletionListener {\n               mSeekBar.thumb = null\n            })\n            //设置视频地址，并开始播放\n            setVideoPath(mCurrentVideoInfo.playUrl)\n            toggleAspectRatio(IRenderView.AR_MATCH_PARENT)\n            setMediaController(ijkMediaController)\n            start()\n        }\n        //获取相关视频信息\n        mPresenter.getRelatedVideoInfo(mCurrentVideoInfo.id)\n    }\n\n\n    /**\n     * 添加标题\n     */\n    private fun getVideoDetailView(): View {\n        return VideoDetailHeadView(this).apply {\n            setTitle(mCurrentVideoInfo.title)\n            setCategoryAndTime(mCurrentVideoInfo.category, mCurrentVideoInfo.duration)\n            setFavoriteCount(mCurrentVideoInfo.consumption.collectionCount)\n            setShareCount(mCurrentVideoInfo.consumption.replyCount)\n            setReplayCount(mCurrentVideoInfo.consumption.replyCount)\n            setDescription(mCurrentVideoInfo.description)\n            startScrollAnimation()\n        }\n\n    }\n\n    /**\n     * 设置相关视频信息\n     */\n    private fun getRelationVideoInfo(): View {\n        return VideoDetailAuthorView(this).apply { setVideoAuthorInfo(mCurrentVideoInfo.author) }\n    }\n\n    override fun getRelatedVideoInfoSuccess(itemList: MutableList<Content>) {\n        with(mRecycler) {\n            visibility = View.VISIBLE\n\n            layoutManager = LinearLayoutManager(this@VideoDetailActivity)\n\n            adapter = VideoDetailAdapter(itemList).apply {\n                addHeaderView(getVideoDetailView())\n                addHeaderView(getRelationVideoInfo())\n                openLoadAnimation(BaseQuickAdapter.ALPHAIN)\n                setFooterView(LayoutInflater.from(this@VideoDetailActivity).inflate(R.layout.item_the_end, null))\n\n                setOnItemClickListener { _, _, position ->\n                    if (getItemViewType(position) != BaseQuickAdapter.HEADER_VIEW) {\n                        refreshVideo(getItem(position))\n                    }\n                }\n            }\n        }\n    }\n\n    override fun getRelatedVideoFail() {\n    }\n\n\n    override fun onBackPressedSupport() {\n        super.onBackPressedSupport()\n        mBackPressed = true\n    }\n\n\n    override fun onResume() {\n        super.onResume()\n        //当前界面可见的时候重新注册进度条监听\n        registerProgressEvent()\n    }\n\n    override fun onPause() {\n        super.onPause()\n        if (ijkVideoViewWrapper.isPlaying()) {\n            ijkVideoViewWrapper.pause()\n        }\n    }\n\n    override fun onStop() {\n        super.onStop()\n        if (mBackPressed) {\n            ijkVideoViewWrapper.stopPlayback()\n            ijkVideoViewWrapper.release(true)\n        }\n        RxBus.unRegister(this)\n    }\n\n    override fun toggleOverridePendingTransition() = true\n\n    override fun getOverridePendingTransition(): TransitionMode = TransitionMode.BOTTOM\n\n    override fun getContentViewLayoutId() = R.layout.activity_video_detail\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/video/VideoInfoByIdActivity.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.video\n\nimport android.os.Bundle\nimport com.alibaba.android.arouter.facade.Postcard\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.facade.callback.NavCallback\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.ContentBean\nimport com.jennifer.andy.simpleeyes.net.Extras\nimport com.jennifer.andy.simpleeyes.ui.base.BaseActivity\nimport com.jennifer.andy.simpleeyes.ui.video.presenter.VideoInfoByIdPresenter\nimport com.jennifer.andy.simpleeyes.ui.video.view.VideoInfoByIdView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/23 14:25\n * Description: 根据视频id获取视频信息\n */\n\n@Route(path = \"/AndyJennifer/detail\")\nclass VideoInfoByIdActivity : BaseActivity<VideoInfoByIdView, VideoInfoByIdPresenter>(), VideoInfoByIdView {\n\n    @Autowired\n    @JvmField\n    var id: String? = null\n\n    override fun initView(savedInstanceState: Bundle?) {\n        ARouter.getInstance().inject(this)\n        mPresenter.getVideoInfoById(id!!)\n    }\n\n    override fun getVideoInfoSuccess(contentBean: ContentBean) {\n\n        val bundle = Bundle().apply {\n            putSerializable(Extras.VIDEO_INFO, contentBean)\n            putSerializable(Extras.VIDEO_LIST_INFO, arrayListOf<Any>())\n        }\n        ARouter.getInstance()\n                .build(\"/pgc/detail\")\n                .with(bundle)\n                .navigation(this, object : NavCallback() {\n                    override fun onArrival(postcard: Postcard?) {\n                        finish()\n                    }\n                })\n    }\n\n\n    override fun getContentViewLayoutId() = R.layout.activity_videoinfo_by_id\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/video/adapter/VideoDetailAdapter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.video.adapter\n\nimport com.chad.library.adapter.base.BaseQuickAdapter\nimport com.chad.library.adapter.base.BaseViewHolder\nimport com.chad.library.adapter.base.util.MultiTypeDelegate\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.utils.getElapseTimeForShow\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/2/11 14:28\n * Description:\n */\n\nclass VideoDetailAdapter(data: MutableList<Content>) : BaseQuickAdapter<Content, BaseViewHolder>(data) {\n\n    companion object {\n        const val TEXT_CARD = \"textCard\"\n        const val VIDEO_SMALL_CARD = \"videoSmallCard\"\n        const val TEXT_CARD_TYPE = 0\n        const val VIDEO_SMALL_CARD_TYPE = 1\n    }\n\n\n    init {\n        multiTypeDelegate = object : MultiTypeDelegate<Content>() {\n            override fun getItemType(andyInfoItem: Content): Int {\n                when (andyInfoItem.type) {\n                    TEXT_CARD -> return TEXT_CARD_TYPE\n                    VIDEO_SMALL_CARD -> return VIDEO_SMALL_CARD_TYPE\n                }\n                return -1\n            }\n        }\n        with(multiTypeDelegate) {\n            registerItemType(TEXT_CARD_TYPE, R.layout.item_video_text_card)\n            registerItemType(VIDEO_SMALL_CARD_TYPE, R.layout.item_video_samll_card)\n        }\n\n    }\n\n    override fun convert(helper: BaseViewHolder?, item: Content) {\n        when (helper?.itemViewType) {\n            TEXT_CARD_TYPE -> setTextCardInfo(helper, item)\n            VIDEO_SMALL_CARD_TYPE -> setVideoSmallCardInfo(helper, item)\n        }\n    }\n\n    /**\n     * 设置卡片信息\n     */\n    private fun setTextCardInfo(helper: BaseViewHolder, item: Content) {\n        helper.setText(R.id.tv_text, item.data.text)\n\n    }\n\n    /**\n     * 设置视频信息\n     */\n    private fun setVideoSmallCardInfo(helper: BaseViewHolder, item: Content) {\n        val imageView = helper.getView<SimpleDraweeView>(R.id.iv_image)\n        imageView.setImageURI(item.data.cover.feed)\n        helper.setText(R.id.tv_title, item.data.title)\n        val description = \"#${item.data.category}   /   ${getElapseTimeForShow(item.data.duration)}\"\n        helper.setText(R.id.tv_time, description)\n\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/video/model/VideoDetailModel.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.video.model\n\nimport com.jennifer.andy.simpleeyes.entity.AndyInfo\nimport com.jennifer.andy.simpleeyes.entity.ContentBean\nimport com.jennifer.andy.simpleeyes.net.Api\nimport com.jennifer.andy.simpleeyes.rx.RxThreadHelper\nimport com.jennifer.andy.simpleeyes.rx.error.globalHandleError\nimport com.jennifer.andy.simpleeyes.ui.base.model.BaseModel\nimport io.reactivex.Observable\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/1/5 15:29\n * Description:\n */\n\nclass VideoDetailModel : BaseModel {\n\n\n    /**\n     * 获取相关视频信息\n     */\n    fun getRelatedVideoInfo(id: String): Observable<AndyInfo> =\n            Api.getDefault()\n                    .getRelatedVideo(id)\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n\n    /**\n     * 根据视频id获取视频信息\n     */\n    fun getVideoInfoById(id: String): Observable<ContentBean> =\n            Api.getDefault()\n                    .getVideoInfoById(id)\n                    .compose(globalHandleError())\n                    .compose(RxThreadHelper.switchObservableThread())\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/video/presenter/VideoDetailPresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.video.presenter\n\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport com.jennifer.andy.simpleeyes.ui.video.model.VideoDetailModel\nimport com.jennifer.andy.simpleeyes.ui.video.view.VideoDetailView\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/12/18 16:55\n * Description:\n */\nclass VideoDetailPresenter : BasePresenter<VideoDetailView>() {\n\n    private var mVideoModel: VideoDetailModel = VideoDetailModel()\n\n    /**\n     * 获取相关视频信息\n     */\n    fun getRelatedVideoInfo(id: String) {\n        mVideoModel.getRelatedVideoInfo(id).autoDispose(mScopeProvider).subscribe({\n            mView?.getRelatedVideoInfoSuccess(it.itemList)\n        }, {\n            mView?.getRelatedVideoFail()\n        })\n    }\n\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/video/presenter/VideoInfoByIdPresenter.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.video.presenter\n\nimport android.view.View\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport com.jennifer.andy.simpleeyes.ui.video.model.VideoDetailModel\nimport com.jennifer.andy.simpleeyes.ui.video.view.VideoInfoByIdView\nimport com.uber.autodispose.autoDispose\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/23 14:32\n * Description:\n */\n\nclass VideoInfoByIdPresenter : BasePresenter<VideoInfoByIdView>() {\n\n    private val mVideoModel = VideoDetailModel()\n\n    fun getVideoInfoById(id: String) {\n        mVideoModel.getVideoInfoById(id).autoDispose(mScopeProvider).subscribe({\n            mView?.getVideoInfoSuccess(it)\n        }, {\n            mView?.showNetError(View.OnClickListener {\n                getVideoInfoById(id)\n            })\n        })\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/video/view/VideoDetailView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.video.view\n\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.ui.base.BaseView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/12/18 16:56\n * Description:\n */\n\ninterface VideoDetailView : BaseView {\n\n    /**\n     * 获取相关视频成功\n     */\n    fun getRelatedVideoInfoSuccess(itemList: MutableList<Content>)\n\n    /**\n     * 获取相关视屏失败\n     */\n    fun getRelatedVideoFail()\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/ui/video/view/VideoInfoByIdView.kt",
    "content": "package com.jennifer.andy.simpleeyes.ui.video.view\n\nimport com.jennifer.andy.simpleeyes.entity.ContentBean\nimport com.jennifer.andy.simpleeyes.ui.base.BaseView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/23 14:31\n * Description:\n */\n\ninterface VideoInfoByIdView : BaseView {\n\n    fun getVideoInfoSuccess(contentBean: ContentBean)\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/AppUtils.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils\n\nimport android.content.Context\n\n\n/**\n * 获取App版本号\n *\n * @return App版本号\n */\nfun Context.getAppVersionName(): String {\n    val pm = packageManager\n    val packageInfo = pm.getPackageInfo(packageName, 0)\n    return packageInfo.versionName\n}\n\n/**\n * 获取App版本码\n *\n * @return App版本码\n */\nfun Context.getAppVersionCode(): Int {\n    val pm = packageManager\n    val packageInfo = pm.getPackageInfo(packageName, 0)\n    return packageInfo.versionCode\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/ButterKnife.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils\n\nimport android.app.Activity\nimport android.app.Dialog\nimport android.app.DialogFragment\nimport android.app.Fragment\nimport android.view.View\nimport androidx.recyclerview.widget.RecyclerView.ViewHolder\nimport kotlin.properties.ReadOnlyProperty\nimport kotlin.reflect.KProperty\nimport androidx.fragment.app.DialogFragment as SupportDialogFragment\nimport androidx.fragment.app.Fragment as SupportFragment\n\npublic fun <V : View> View.bindView(id: Int)\n    : ReadOnlyProperty<View, V> = required(id, viewFinder)\npublic fun <V : View> Activity.bindView(id: Int)\n    : ReadOnlyProperty<Activity, V> = required(id, viewFinder)\npublic fun <V : View> Dialog.bindView(id: Int)\n    : ReadOnlyProperty<Dialog, V> = required(id, viewFinder)\npublic fun <V : View> DialogFragment.bindView(id: Int)\n    : ReadOnlyProperty<DialogFragment, V> = required(id, viewFinder)\npublic fun <V : View> SupportDialogFragment.bindView(id: Int)\n    : ReadOnlyProperty<SupportDialogFragment, V> = required(id, viewFinder)\npublic fun <V : View> Fragment.bindView(id: Int)\n    : ReadOnlyProperty<Fragment, V> = required(id, viewFinder)\npublic fun <V : View> SupportFragment.bindView(id: Int)\n    : ReadOnlyProperty<SupportFragment, V> = required(id, viewFinder)\npublic fun <V : View> ViewHolder.bindView(id: Int)\n    : ReadOnlyProperty<ViewHolder, V> = required(id, viewFinder)\n\npublic fun <V : View> View.bindOptionalView(id: Int)\n    : ReadOnlyProperty<View, V?> = optional(id, viewFinder)\npublic fun <V : View> Activity.bindOptionalView(id: Int)\n    : ReadOnlyProperty<Activity, V?> = optional(id, viewFinder)\npublic fun <V : View> Dialog.bindOptionalView(id: Int)\n    : ReadOnlyProperty<Dialog, V?> = optional(id, viewFinder)\npublic fun <V : View> DialogFragment.bindOptionalView(id: Int)\n    : ReadOnlyProperty<DialogFragment, V?> = optional(id, viewFinder)\npublic fun <V : View> SupportDialogFragment.bindOptionalView(id: Int)\n    : ReadOnlyProperty<SupportDialogFragment, V?> = optional(id, viewFinder)\npublic fun <V : View> Fragment.bindOptionalView(id: Int)\n    : ReadOnlyProperty<Fragment, V?> = optional(id, viewFinder)\npublic fun <V : View> SupportFragment.bindOptionalView(id: Int)\n    : ReadOnlyProperty<SupportFragment, V?> = optional(id, viewFinder)\npublic fun <V : View> ViewHolder.bindOptionalView(id: Int)\n    : ReadOnlyProperty<ViewHolder, V?> = optional(id, viewFinder)\n\npublic fun <V : View> View.bindViews(vararg ids: Int)\n    : ReadOnlyProperty<View, List<V>> = required(ids, viewFinder)\npublic fun <V : View> Activity.bindViews(vararg ids: Int)\n    : ReadOnlyProperty<Activity, List<V>> = required(ids, viewFinder)\npublic fun <V : View> Dialog.bindViews(vararg ids: Int)\n    : ReadOnlyProperty<Dialog, List<V>> = required(ids, viewFinder)\npublic fun <V : View> DialogFragment.bindViews(vararg ids: Int)\n    : ReadOnlyProperty<DialogFragment, List<V>> = required(ids, viewFinder)\npublic fun <V : View> SupportDialogFragment.bindViews(vararg ids: Int)\n    : ReadOnlyProperty<SupportDialogFragment, List<V>> = required(ids, viewFinder)\npublic fun <V : View> Fragment.bindViews(vararg ids: Int)\n    : ReadOnlyProperty<Fragment, List<V>> = required(ids, viewFinder)\npublic fun <V : View> SupportFragment.bindViews(vararg ids: Int)\n    : ReadOnlyProperty<SupportFragment, List<V>> = required(ids, viewFinder)\npublic fun <V : View> ViewHolder.bindViews(vararg ids: Int)\n    : ReadOnlyProperty<ViewHolder, List<V>> = required(ids, viewFinder)\n\npublic fun <V : View> View.bindOptionalViews(vararg ids: Int)\n    : ReadOnlyProperty<View, List<V>> = optional(ids, viewFinder)\npublic fun <V : View> Activity.bindOptionalViews(vararg ids: Int)\n    : ReadOnlyProperty<Activity, List<V>> = optional(ids, viewFinder)\npublic fun <V : View> Dialog.bindOptionalViews(vararg ids: Int)\n    : ReadOnlyProperty<Dialog, List<V>> = optional(ids, viewFinder)\npublic fun <V : View> DialogFragment.bindOptionalViews(vararg ids: Int)\n    : ReadOnlyProperty<DialogFragment, List<V>> = optional(ids, viewFinder)\npublic fun <V : View> SupportDialogFragment.bindOptionalViews(vararg ids: Int)\n    : ReadOnlyProperty<SupportDialogFragment, List<V>> = optional(ids, viewFinder)\npublic fun <V : View> Fragment.bindOptionalViews(vararg ids: Int)\n    : ReadOnlyProperty<Fragment, List<V>> = optional(ids, viewFinder)\npublic fun <V : View> SupportFragment.bindOptionalViews(vararg ids: Int)\n    : ReadOnlyProperty<SupportFragment, List<V>> = optional(ids, viewFinder)\npublic fun <V : View> ViewHolder.bindOptionalViews(vararg ids: Int)\n    : ReadOnlyProperty<ViewHolder, List<V>> = optional(ids, viewFinder)\n\nprivate val View.viewFinder: View.(Int) -> View?\n    get() = { findViewById(it) }\nprivate val Activity.viewFinder: Activity.(Int) -> View?\n    get() = { findViewById(it) }\nprivate val Dialog.viewFinder: Dialog.(Int) -> View?\n    get() = { findViewById(it) }\nprivate val DialogFragment.viewFinder: DialogFragment.(Int) -> View?\n    get() = { dialog?.findViewById(it) ?: view?.findViewById(it) }\nprivate val SupportDialogFragment.viewFinder: SupportDialogFragment.(Int) -> View?\n    get() = { dialog?.findViewById(it) ?: view?.findViewById(it) }\nprivate val Fragment.viewFinder: Fragment.(Int) -> View?\n    get() = { view.findViewById(it) }\nprivate val SupportFragment.viewFinder: SupportFragment.(Int) -> View?\n    get() = { view!!.findViewById(it) }\nprivate val ViewHolder.viewFinder: ViewHolder.(Int) -> View?\n    get() = { itemView.findViewById(it) }\n\nprivate fun viewNotFound(id:Int, desc: KProperty<*>): Nothing =\n    throw IllegalStateException(\"View ID $id for '${desc.name}' not found.\")\n\n@Suppress(\"UNCHECKED_CAST\")\nprivate fun <T, V : View> required(id: Int, finder: T.(Int) -> View?)\n    = Lazy { t: T, desc ->\n    t.finder(id) as V? ?: viewNotFound(id, desc)\n}\n\n@Suppress(\"UNCHECKED_CAST\")\nprivate fun <T, V : View> optional(id: Int, finder: T.(Int) -> View?)\n    = Lazy { t: T, desc -> t.finder(id) as V? }\n\n@Suppress(\"UNCHECKED_CAST\")\nprivate fun <T, V : View> required(ids: IntArray, finder: T.(Int) -> View?)\n    = Lazy { t: T, desc ->\n    ids.map {\n        t.finder(it) as V? ?: viewNotFound(it, desc)\n    }\n}\n\n@Suppress(\"UNCHECKED_CAST\")\nprivate fun <T, V : View> optional(ids: IntArray, finder: T.(Int) -> View?)\n    = Lazy { t: T, desc -> ids.map { t.finder(it) as V? }.filterNotNull() }\n\n// Like Kotlin's lazy delegate but the initializer gets the target and metadata passed to it\nprivate class Lazy<T, V>(private val initializer: (T, KProperty<*>) -> V) : ReadOnlyProperty<T, V> {\n  private object EMPTY\n  private var value: Any? = EMPTY\n\n  override fun getValue(thisRef: T, property: KProperty<*>): V {\n    if (value == EMPTY) {\n      value = initializer(thisRef, property)\n    }\n    @Suppress(\"UNCHECKED_CAST\")\n    return value as V\n  }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/DensityUtils.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils\n\nimport android.content.Context\n\n\n/**\n * 获取设备尺寸密度\n */\nfun Context.getDensity() = resources.displayMetrics.density\n\n/**\n * 获取设备收缩密度\n */\nfun Context.getScaleDensity() = resources.displayMetrics.scaledDensity\n\n/**\n * dp转px\n */\nfun Context.dip2px(dipValue: Float) = (dipValue * getDensity() + 0.5f).toInt()\n\n/**\n * px转dp\n */\nfun Context.px2dip(pxValue: Float) = ((pxValue / getDensity()) + 0.5f).toInt()\n\n/**\n * sp转px\n */\nfun Context.sp2px(spValue: Float) = ((spValue * getScaleDensity()) + 0.5f).toInt()\n\n/**\n * px转sp\n */\nfun Context.px2sp(pxValue: Float) = (pxValue / getScaleDensity() + 0.5f).toInt()\n\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/IntentRouterUtils.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils\n\nimport android.app.Activity\nimport androidx.fragment.app.Fragment\nimport com.jennifer.andy.simpleeyes.utils.ext.intentFor\n\n/**\n * 跳转到相应的activity 并携带bundle数据\n */\ninline fun <reified T : Any> Activity.readyGo(extras: Map<String, Any> = emptyMap()) {\n    val intent = intentFor<T>(extras)\n    startActivity(intent)\n}\n\n/**\n * 跳转到相应的activity,并携带bundle数据，接收返回码\n */\ninline fun <reified T : Any> Activity.readyGoForResult(extras: Map<String, Any> = emptyMap(), requestCode: Int) {\n    val intent = intentFor<T>(extras)\n    startActivityForResult(intent, requestCode)\n}\n\n/**\n * 跳转到相应的activity并携带bundle数据，然后干掉当前Activity\n *\n */\ninline fun <reified T : Any> Activity.readyGoThenKillSelf(extras: Map<String, Any> = emptyMap()) {\n    val intent = intentFor<T>(extras)\n    startActivity(intent)\n    finish()\n}\n\n\n/**\n * 跳转到相应的activity 并携带bundle数据\n */\ninline fun <reified T : Any> Fragment.readyGo(extras: Map<String, Any> = emptyMap()) {\n    val intent = intentFor<T>(extras)\n    startActivity(intent)\n}\n\n/**\n * 跳转到相应的activity,并携带bundle数据，接收返回码\n */\ninline fun <reified T : Any> Fragment.readyGoForResult(extras: Map<String, Any> = emptyMap(), requestCode: Int) {\n    val intent = intentFor<T>(extras)\n    startActivityForResult(intent, requestCode)\n}\n\n/**\n * 跳转到相应的activity并携带bundle数据，然后干掉当前Fragment所属Activity\n *\n */\ninline fun <reified T : Any> Fragment.readyGoThenKillSelf(extras: Map<String, Any> = emptyMap()) {\n    val intent = intentFor<T>(extras)\n    startActivity(intent)\n    requireActivity().finish()\n}\n\n\n\n\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/KeyboardUtils.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils\n\nimport android.app.Activity\nimport android.content.Context\nimport android.view.inputmethod.InputMethodManager\n\n\n\n/**\n * 是否显示键盘\n */\nfun Activity.showKeyboard(isShow: Boolean) {\n    val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager\n    if (isShow) {\n        if (currentFocus == null) {\n            imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)\n        } else {\n            imm.showSoftInput(currentFocus, 0)\n        }\n    } else {\n        if (currentFocus != null) {\n            imm.hideSoftInputFromWindow(currentFocus!!.windowToken, 0)\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/NetWorkUtils.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils\n\nimport android.content.Context\nimport android.net.ConnectivityManager\n\n\n/**\n * 判断当前网络时候连接\n */\nfun Context.isNetWorkConnected(): Boolean {\n    val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager\n    val networkInfo = connectivityManager.activeNetworkInfo//返回当前网络的详细信息\n    return networkInfo.isAvailable\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/ProcessUtils.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils\n\nimport android.app.ActivityManager\nimport android.content.Context\n\n\n/**\n * 获取当前进程名称\n */\nfun Context.getProcessName(): String? {\n    val pid = android.os.Process.myPid()\n    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager\n    activityManager.runningAppProcesses.forEach {\n        if (it.pid == pid) {\n            return it.processName\n        }\n    }\n    return null\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/ScreenUtils.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils\n\nimport android.content.Context\nimport android.util.DisplayMetrics\nimport android.view.WindowManager\n\n\n/**\n * 获取屏幕的宽度\n */\nfun Context.getScreenWidth(): Int {\n    val wm = getSystemService(Context.WINDOW_SERVICE) as WindowManager\n    val dm = DisplayMetrics()\n    wm.defaultDisplay.getMetrics(dm)\n    return dm.widthPixels\n}\n\n/**\n * 获取屏幕的高度\n */\nfun Context.getScreenHeight(): Int {\n    val wm = getSystemService(Context.WINDOW_SERVICE) as WindowManager\n    val dm = DisplayMetrics()\n    wm.defaultDisplay.getMetrics(dm)\n    return dm.heightPixels\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/SystemUtils.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils\n\nimport com.jennifer.andy.simpleeyes.ui.base.model.BaseModel\nimport com.jennifer.andy.simpleeyes.ui.base.presenter.BasePresenter\nimport java.lang.reflect.ParameterizedType\n\n\n/**\n * 获取当前类上的泛型参数，并实例化\n *\n * @param any 当前类\n * @param index  类泛型参数角标\n * @param <T>    泛型实例化对象\n * @return 泛型实例化对象\n */\nfun <T> getGenericInstance(any: Any, index: Int): T {\n    try {\n        val type = any.javaClass.genericSuperclass as ParameterizedType//获取当前类的父类泛型参数\n        val clazz = type.actualTypeArguments[index] as Class<T>//获取泛型class\n        val instance = clazz.newInstance()\n        return if (instance is BasePresenter<*> || instance is BaseModel) {\n            instance\n        } else {\n            throw IllegalStateException(\"if you use mvp user must support generic!!!\")\n        }\n    } catch (e: Exception) {\n        e.printStackTrace()\n        throw IllegalStateException(\"translate fail!!\")\n    }\n}\n\n\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/TimeUtils.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils\n\nimport java.text.SimpleDateFormat\nimport java.util.*\n\n\n/**\n * 将秒数转换为 00 00' 00''格式\n * @seconds 秒\n */\nfun getElapseTimeForShow(seconds: Int): String {\n    val sb = StringBuilder()\n    val hour = seconds / (60 * 60)\n    if (hour != 0) {\n        sb.append(hour).append(\"\")\n    }\n    val minute = (seconds - 60 * 60 * hour) / 60\n    if (minute != 0 && minute > 9) {\n        sb.append(minute).append(\"' \")\n    } else {\n        sb.append(\"0\").append(minute).append(\"' \")\n    }\n    val second = seconds - 60 * 60 * hour - 60 * minute\n    if (second != 0 && second > 9) {\n        sb.append(second).append(\"\\\"\")\n    } else {\n        sb.append(\"0\").append(second).append(\"\\\"\")\n    }\n    return sb.toString()\n}\n\n\n/**\n * 将日期转换为指定格式的日期字符串\n *\n * @param date   日期\n * @param format 格式化字符串\n */\nfun getDateString(date: Date, format: String): String {\n    val formatter = SimpleDateFormat(format, Locale.getDefault())\n    return formatter.format(date)\n}\n\n/**\n * 判断是否是今天\n *\n */\nfun isCurrentDay(time: Long): Boolean {\n    return isSameDay(Date(), Date(time))\n}\n\n\n/**\n * 是否是同一天\n */\nfun isSameDay(date1: Date, date2: Date): Boolean {\n    val cal1 = Calendar.getInstance()\n    val cal2 = Calendar.getInstance()\n    cal1.time = date1\n    cal2.time = date2\n    return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)\n}\n\n\n/**\n * 格式化时间\n * 如果是当天就格式化时间为 HH:mm\n * 如果是一周前发布的就显示几天前\n * 如果超过5天显示一周前\n * @currentDate 当前日期\n */\nfun getTimeStr(currentDate: Date): String {\n    val todayStart = Calendar.getInstance()\n    todayStart.set(Calendar.HOUR_OF_DAY, 0)\n    todayStart.set(Calendar.MINUTE, 0)\n    todayStart.set(Calendar.SECOND, 0)\n    todayStart.set(Calendar.MILLISECOND, 0)\n    val numberBeforeCurrentDay = getNumberBeforeCurrentDay(todayStart.time, currentDate)\n    return when {\n        numberBeforeCurrentDay == 0 -> {//当天发布\n            val timeFormatter24 = SimpleDateFormat(\"HH:mm\", Locale.getDefault())\n            timeFormatter24.format(currentDate)\n        }\n        numberBeforeCurrentDay <= 5 -> {//小于一周前\n            \"$numberBeforeCurrentDay 天前\"\n        }\n        else -> {//一周前\n            \"1 周前\"\n        }\n    }\n}\n\nprivate fun getNumberBeforeCurrentDay(todayBegin: Date, currentDate: Date): Int {\n    var index = 7\n    while (index >= 1) {\n        val date = Date(todayBegin.time - 3600 * 24 * 1000 * index)\n        if (currentDate.before(date)) break else index--\n    }\n    return index\n\n}\n\n\n/**\n * 格式化时间\n */\nfun stringForTime(timeMs: Int): String {\n    val sb = StringBuilder()\n    val totalSeconds = timeMs / 1000\n    val seconds = totalSeconds % 60\n    val minutes = totalSeconds / 60 % 60\n    val hours = totalSeconds / 3600\n    sb.setLength(0)\n    val mFormatter = Formatter(sb, Locale.getDefault())\n    return if (hours > 0) {\n        mFormatter.format(\"%d:%02d:%02d\", hours, minutes, seconds).toString()\n    } else {\n        mFormatter.format(\"%02d:%02d\", minutes, seconds).toString()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/ToastUtils.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils\n\nimport android.content.Context\nimport android.os.Handler\nimport android.widget.Toast\nimport com.jennifer.andy.simpleeyes.AndyApplication\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-11-17 22:43\n * Description:\n */\n\n\nfun toast(value: () -> String) = AndyApplication.INSTANCE.toast(value)\n\nfun toast(value: String) = toast { value }\n\ninline fun Context.toast(crossinline value: () -> String, duration: Int = Toast.LENGTH_SHORT) {\n    Handler(mainLooper).post { Toast.makeText(this, value(), duration).show() }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/UDIDUtils.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils\n\nimport java.util.*\n\n/**\n * 获取随机uuid\n */\nfun getRandomUUID() = UUID.randomUUID().toString().replace(\"-\", \"\")\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/VideoPlayerUtils.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.ContextWrapper\nimport android.view.Window\nimport android.view.WindowManager\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.appcompat.view.ContextThemeWrapper\n\n\n/**\n * 隐藏ActionBar并使当前界面全屏\n */\nfun hideActionBar(context: Context?) {\n    val supportActionBar = getAppCompatActivity(context)?.supportActionBar\n    supportActionBar?.let {\n        supportActionBar.setShowHideAnimationEnabled(false)\n        supportActionBar.hide()\n    }\n    getActivity(context)?.window?.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)\n}\n\n/**\n * 显示ActionBar并退出全屏\n */\nfun showActionBar(context: Context?) {\n    val supportActionBar = getAppCompatActivity(context)?.supportActionBar\n    supportActionBar?.let {\n        supportActionBar.setShowHideAnimationEnabled(false)\n        supportActionBar.show()\n    }\n    getActivity(context)?.window?.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)\n}\n\n/**\n * 获取AppCompatActivity\n */\nprivate fun getAppCompatActivity(context: Context?): AppCompatActivity? {\n    if (context == null) {\n        return null\n    }\n    if (context is AppCompatActivity) {\n        return context\n    } else if (context is ContextThemeWrapper) {\n        return getAppCompatActivity(context.baseContext)\n    }\n    return null\n}\n\n/**\n * 获取Activity\n */\nfun getActivity(context: Context?): Activity? {\n    if (context == null) {\n        return null\n    }\n    if (context is Activity) {\n        return context\n    } else if (context is ContextWrapper) {\n        return getActivity(context.baseContext)\n    }\n    return null\n}\n\n/**\n * 获取Window\n */\nfun getWindow(context: Context?): Window? {\n    return if (getAppCompatActivity(context) != null) {\n        getAppCompatActivity(context)?.window\n    } else {\n        getActivity(context)?.window\n    }\n}\n\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/utils/ext/IntentEx.kt",
    "content": "package com.jennifer.andy.simpleeyes.utils.ext\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport android.os.Parcelable\nimport androidx.fragment.app.Fragment\nimport java.io.Serializable\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-12-08 16:52\n * Description:根据传入的泛型创建Intent比填充相应的参数\n */\n\n\ninline fun <reified T : Any> Context.intentFor(params: Map<String, Any>): Intent {\n    return createIntent(this, T::class.java, params)\n}\n\ninline fun <reified T : Any> Fragment.intentFor(params: Map<String, Any>): Intent {\n    return createIntent(requireContext(), T::class.java, params)\n}\n\nfun <T> createIntent(ctx: Context, clazz: Class<out T>, params: Map<String, Any>): Intent {\n    val intent = Intent(ctx, clazz)\n    if (params.isNotEmpty()) fillIntentArguments(intent, params)\n    return intent\n}\n\n\nprivate fun fillIntentArguments(intent: Intent, params: Map<String, Any>) {\n    params.keys.forEach { key ->\n        when (val value = params[key]) {\n            null -> intent.putExtra(key, null as Serializable?)\n            is Int -> intent.putExtra(key, value)\n            is Long -> intent.putExtra(key, value)\n            is CharSequence -> intent.putExtra(key, value)\n            is String -> intent.putExtra(key, value)\n            is Float -> intent.putExtra(key, value)\n            is Double -> intent.putExtra(key, value)\n            is Char -> intent.putExtra(key, value)\n            is Short -> intent.putExtra(key, value)\n            is Boolean -> intent.putExtra(key, value)\n            is Serializable -> intent.putExtra(key, value)\n            is Bundle -> intent.putExtra(key, value)\n            is Parcelable -> intent.putExtra(key, value)\n            is Array<*> -> when {\n                value.isArrayOf<CharSequence>() -> intent.putExtra(key, value)\n                value.isArrayOf<String>() -> intent.putExtra(key, value)\n                value.isArrayOf<Parcelable>() -> intent.putExtra(key, value)\n                else -> error(\"Intent extra $key has wrong type ${value.javaClass.name}\")\n            }\n            is IntArray -> intent.putExtra(key, value)\n            is LongArray -> intent.putExtra(key, value)\n            is FloatArray -> intent.putExtra(key, value)\n            is DoubleArray -> intent.putExtra(key, value)\n            is CharArray -> intent.putExtra(key, value)\n            is ShortArray -> intent.putExtra(key, value)\n            is BooleanArray -> intent.putExtra(key, value)\n            else -> throw error(\"Intent extra $key has wrong type ${value.javaClass.name}\")\n        }\n        return@forEach\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/BottomBar.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget\n\nimport android.content.Context\nimport android.graphics.Color\nimport android.os.Parcel\nimport android.os.Parcelable\nimport android.util.AttributeSet\nimport android.view.ViewGroup\nimport android.widget.LinearLayout\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/27 22:00\n * Description: 底部导航栏\n */\nclass BottomBar : LinearLayout {\n\n    private lateinit var mTabLayout: LinearLayout\n    private lateinit var mTabParams: LayoutParams\n    private var mCurrentPosition = 0//当前默认位置\n    private var mBottomItemLayouts = mutableListOf<BottomItemLayout>()\n\n    private var mListener: TabSelectedListener? = null\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        init(context)\n    }\n\n    private fun init(context: Context) {\n        orientation = HORIZONTAL\n\n        mTabLayout = LinearLayout(context)\n        mTabLayout.setBackgroundColor(Color.WHITE)\n        mTabLayout.orientation = HORIZONTAL\n        mTabParams = LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT)\n        mTabParams.weight = 1f\n\n        addView(mTabLayout, mTabParams)\n    }\n\n    fun addItem(bottomItem: BottomItem) {\n\n        val bottomItemLayout = BottomItemLayout(context)\n        bottomItemLayout.setTabPosition(mBottomItemLayouts.size)\n        bottomItemLayout.setBottomItem(bottomItem)\n        mTabLayout.addView(bottomItemLayout, mTabParams)\n        mBottomItemLayouts.add(bottomItemLayout)\n\n        bottomItemLayout.setOnClickListener {\n            val position = bottomItemLayout.getTabPosition()\n            if (position == mCurrentPosition) {\n                mListener?.onTabReselected(position)\n            } else {\n                mListener?.onTabSelected(position, mCurrentPosition)\n                bottomItemLayout.isSelected = true\n                mListener?.onTabUnselected(mCurrentPosition)\n                mBottomItemLayouts[mCurrentPosition].isSelected = false\n                mCurrentPosition = position\n            }\n        }\n    }\n\n\n    fun setOnTabSelectedListener(TabSelectedListener: TabSelectedListener) {\n        mListener = TabSelectedListener\n    }\n\n    /**\n     * 选项卡选择监听\n     */\n    interface TabSelectedListener {\n\n        /**\n         * 当选项卡被选择\n         */\n        fun onTabSelected(position: Int, prePosition: Int)\n\n        /**\n         * 当前选项卡没有被选择\n         */\n        fun onTabUnselected(position: Int)\n\n        /**\n         * 当选项卡被重复选择\n         */\n        fun onTabReselected(position: Int)\n    }\n\n    /**\n     * 初始化\n     */\n    fun initialise() {\n        mBottomItemLayouts[mCurrentPosition].isSelected = true\n    }\n\n    internal class SavedState : BaseSavedState {\n\n        var selectPosition: Int = 0\n\n        companion object {\n\n            @JvmField\n            val CREATOR: Parcelable.Creator<SavedState> = object : Parcelable.Creator<SavedState> {\n                override fun createFromParcel(`in`: Parcel): SavedState {\n                    return SavedState(`in`)\n                }\n\n                override fun newArray(size: Int): Array<SavedState?> {\n                    return arrayOfNulls(size)\n                }\n            }\n        }\n\n        constructor(superState: Parcelable?) : super(superState)\n\n\n        private constructor(`in`: Parcel) : super(`in`) {\n            selectPosition = `in`.readInt()\n        }\n\n        override fun writeToParcel(out: Parcel, flags: Int) {\n            super.writeToParcel(out, flags)\n            out.writeValue(selectPosition)\n        }\n        override fun toString(): String {\n            return (\"CompoundButton.SavedState{\"\n                    + Integer.toHexString(System.identityHashCode(this))\n                    + \" checked=\" + selectPosition + \"}\")\n        }\n\n\n    }\n\n    override fun onSaveInstanceState(): Parcelable? {\n        return SavedState(super.onSaveInstanceState()).apply {\n          selectPosition = mCurrentPosition\n        }\n    }\n\n    override fun onRestoreInstanceState(state: Parcelable?) {\n        val ss = state as SavedState\n        super.onRestoreInstanceState(ss.superState)\n        mCurrentPosition = ss.selectPosition\n        initialise()\n    }\n}\n\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/BottomItem.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget\n\nimport android.graphics.drawable.Drawable\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/10/7 22:52\n * Description:\n */\n\nclass BottomItem{\n\n\n    var mSelectDrawable: Drawable? = null\n    var mSelectResource = -1\n\n    var mTitleResource = -1\n    var mTitle: String? = null\n\n    var mUnSelectedDrawable: Drawable? = null\n    var mUnSelectedResource = -1\n\n\n    constructor(selectDrawable: Drawable, title: String) {\n        this.mSelectDrawable = selectDrawable\n        this.mTitle = title\n    }\n\n    constructor(selectResource: Int, title: String) {\n        this.mSelectResource = selectResource\n        this.mTitle = title\n    }\n\n    constructor(mSelectDrawable: Drawable, mTitleResource: Int) {\n        this.mSelectDrawable = mSelectDrawable\n        this.mTitleResource = mTitleResource\n    }\n\n    constructor(mSelectResource: Int, mTitleResource: Int) {\n        this.mSelectResource = mSelectResource\n        this.mTitleResource = mTitleResource\n    }\n\n\n    fun setUnSelectedDrawable(unSelectedDrawable: Drawable) {\n        this.mUnSelectedDrawable = unSelectedDrawable\n    }\n\n    fun setUnSelectedDrawable(unSelectedResource: Int) {\n        this.mUnSelectedResource = unSelectedResource\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/BottomItemLayout.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.util.AttributeSet\nimport android.view.Gravity\nimport android.view.LayoutInflater\nimport android.widget.ImageView\nimport android.widget.LinearLayout\nimport android.widget.TextView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.utils.bindView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/9/27 22:01\n * Description:\n */\n\nclass BottomItemLayout : LinearLayout {\n\n\n    private var mSelectedTextColor = resources.getColor(R.color.colorPrimaryDark)\n    private var mUnselectedTextColor = resources.getColor(R.color.SecondaryText)\n\n    private val mIcon by bindView<ImageView>(R.id.iv_image)\n    private val mIconTitle by bindView<TextView>(R.id.tv_title)\n    private var mSelectedDrawable: Drawable? = null\n    private var mUnSelectedDrawable: Drawable? = null\n    private var mTitle: String? = null\n\n    private var mTabPosition = -1\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        init(context)\n    }\n\n    private fun init(context: Context) {\n        LayoutInflater.from(context).inflate(R.layout.layout_bottom_item, this, true)\n        gravity = Gravity.CENTER\n    }\n\n    override fun setSelected(selected: Boolean) {\n        super.setSelected(selected)\n        if (selected) {\n            if (mSelectedDrawable != null) {\n                mIcon.setImageDrawable(mSelectedDrawable)\n                mIconTitle.setTextColor(mSelectedTextColor)\n            }\n        } else {\n            if (mUnSelectedDrawable != null) {\n                mIcon.setImageDrawable(mUnSelectedDrawable)\n                mIconTitle.setTextColor(mUnselectedTextColor)\n            }\n        }\n    }\n\n    fun setTabPosition(position: Int) {\n        mTabPosition = position\n        if (position == 0) {\n            isSelected = true\n        }\n    }\n\n    fun getTabPosition() = mTabPosition\n\n    fun setBottomItem(bottomItem: BottomItem) {\n        //设置选中图片\n        if (bottomItem.mSelectResource > -1) {\n            mSelectedDrawable = resources.getDrawable(bottomItem.mSelectResource)\n        } else if (bottomItem.mSelectDrawable != null) {\n            mSelectedDrawable = bottomItem.mSelectDrawable!!\n        }\n        //设置未被选中团片\n        if (bottomItem.mUnSelectedResource > -1) {\n            mUnSelectedDrawable = resources.getDrawable(bottomItem.mUnSelectedResource)\n        } else if (bottomItem.mUnSelectedDrawable != null) {\n            mUnSelectedDrawable = bottomItem.mUnSelectedDrawable!!\n        }\n        //设置标题\n        if (bottomItem.mTitleResource > -1) {\n            mTitle = resources.getString(bottomItem.mTitleResource)\n        } else if (bottomItem.mTitle != null) {\n            mTitle = bottomItem.mTitle!!\n        }\n        //初始化\n        mIcon.setImageDrawable(mUnSelectedDrawable)\n        mIconTitle.text = mTitle\n\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/CollectionOfHorizontalScrollCardView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.widget.FrameLayout\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.utils.getElapseTimeForShow\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/9 15:31\n * Description:\n */\n\nclass CollectionOfHorizontalScrollCardView : FrameLayout {\n\n    private val mEliteImageView by bindView<EliteImageView>(R.id.scroll_elite_view)\n    private val mTvTitle by bindView<CustomFontTextView>(R.id.scroll_tv_title)\n    private val mTvDesc by bindView<CustomFontTextView>(R.id.scroll_tv_desc)\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        init(context)\n    }\n\n    private fun init(context: Context) {\n        LayoutInflater.from(context).inflate(R.layout.layout_collection_of_horizontal_scroll_card, this, true)\n    }\n\n\n    fun setData(content: Content) {\n        setImageUrl(content.data.cover.feed)\n        val description = \"#${content.data.category}   /   ${getElapseTimeForShow(content.data.duration)}\"\n        setTitleAndDesc(content.data.title, description)\n        setDailyVisible(content.data.library == \"DAILY\")\n    }\n\n    /**\n     * 设置图片显示\n     * @param url 图片地址\n     */\n    private fun setImageUrl(url: String) {\n        mEliteImageView.setImageUrl(url)\n    }\n\n    /**\n     * 是指标题与描述\n     */\n    private fun setTitleAndDesc(title: String, desc: String) {\n        mTvTitle.text = title\n        mTvDesc.text = desc\n    }\n\n    /**\n     * 设置精选是否可见\n     */\n    private fun setDailyVisible(visible: Boolean) {\n        mEliteImageView.setDailyVisible(visible)\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/CustomLoadMoreView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget\n\nimport com.chad.library.adapter.base.loadmore.LoadMoreView\nimport com.jennifer.andy.simpleeyes.R\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/12/18 15:03\n * Description:自定义加载更多\n */\n\nclass CustomLoadMoreView : LoadMoreView() {\n\n    override fun getLayoutId() = R.layout.layout_load_more_view\n\n    override fun getLoadingViewId() = R.id.ll_load_more_loading_view\n\n    override fun getLoadEndViewId() = R.id.rl_load_end_view\n\n    override fun getLoadFailViewId() = R.id.fl_load_more_load_fail_view\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/EliteImageView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget\n\nimport android.content.Context\nimport android.text.TextUtils\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.widget.FrameLayout\nimport android.widget.ImageView\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/11/3 17:44\n * Description:精选imageView 包含翻译图片 翻译\n */\n\nclass EliteImageView : FrameLayout {\n\n    private val ivImageView by bindView<SimpleDraweeView>(R.id.iv_elite_image)\n    private val tvTranslate by bindView<CustomFontTextView>(R.id.tv_translate)\n    private val ivArrow by bindView<ImageView>(R.id.iv_arrow)\n    private val ivDaily by bindView<ImageView>(R.id.iv_daily)\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        init(context)\n    }\n\n    private fun init(context: Context) {\n        LayoutInflater.from(context).inflate(R.layout.layout_choiceness, this, true)\n    }\n\n    /**\n     * 设置箭头是否可见\n     *\n     * @param visible 是否可见\n     */\n    fun setArrowVisible(visible: Boolean) {\n        ivArrow.visibility = if (visible) View.VISIBLE else View.GONE\n    }\n\n    /**\n     * 设置精选是否可见\n     */\n    fun setDailyVisible(visible: Boolean) {\n        ivDaily.visibility = if (visible) View.VISIBLE else View.GONE\n    }\n\n    /**\n     * 设置翻译文字\n     */\n    fun setTranslateText(text: String) {\n        if (!TextUtils.isEmpty(text)) {\n            tvTranslate.visibility = View.VISIBLE\n            tvTranslate.text = text\n        } else {\n            tvTranslate.visibility = View.GONE\n        }\n    }\n\n    /**\n     * 设置图片显示\n     * @param url 图片地址\n     */\n    fun setImageUrl(url: String) {\n        ivImageView.setImageURI(url)\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/FullScreenVideoView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.widget.VideoView\nimport com.jennifer.andy.simpleeyes.player.MeasureHelper\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-08-27 10:43\n * Description:全屏VideoView\n */\n\nclass FullScreenVideoView : VideoView {\n\n\n    private var mMeasureHelper: MeasureHelper = MeasureHelper(this)\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec)\n        setMeasuredDimension(mMeasureHelper.measuredWidth, mMeasureHelper.measuredHeight)\n    }\n\n    /**\n     * 设置视频比例\n     */\n    fun setAspectRatio(aspectRatio: Int) {\n        mMeasureHelper.setAspectRatio(aspectRatio)\n        requestLayout()\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/GridItemDecoration.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget\n\nimport android.graphics.Rect\nimport android.view.View\nimport androidx.recyclerview.widget.RecyclerView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/8 11:15\n * Description:\n */\nclass GridItemDecoration constructor( val spanCount: Int, val spacing: Int, val isIncludeEdge: Boolean) : RecyclerView.ItemDecoration() {\n\n    \n\n    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView,\n                                state: RecyclerView.State) {\n        val position = parent.getChildAdapterPosition(view) // 获取当前view的位置\n        val column = position % spanCount // 判断当前view在第几列\n\n        if (isIncludeEdge) {//判断是否包括左右两边\n            // spacing - column * ((1f / spanCount) * spacing)\n            outRect.left = spacing - column * spacing / spanCount\n            // (column + 1) * ((1f / spanCount) * spacing)\n            outRect.right = (column + 1) * spacing / spanCount\n\n            if (position < spanCount) { // top edge\n                outRect.top = spacing\n            }\n            outRect.bottom = spacing // item bottom\n        } else {\n            // column * ((1f / spanCount) * spacing)\n            outRect.left = column * spacing / spanCount\n            // spacing - (column + 1) * ((1f / spanCount) * spacing)\n            outRect.right = spacing - (column + 1) * spacing / spanCount\n            if (position >= spanCount) {\n                outRect.top = spacing // item top\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/ItemHeaderView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget\n\nimport android.content.Context\nimport android.net.Uri\nimport android.util.AttributeSet\nimport android.view.Gravity\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.widget.FrameLayout\nimport android.widget.ImageView\nimport android.widget.TextView\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.facebook.drawee.generic.RoundingParams\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.ContentBean\nimport com.jennifer.andy.simpleeyes.entity.Header\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.utils.getTimeStr\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\nimport com.jennifer.andy.simpleeyes.widget.font.FontType\nimport java.util.*\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-07-14 15:57\n * Description:列表中item对应的header\n */\nclass ItemHeaderView : FrameLayout {\n\n    private val tvTitle by bindView<CustomFontTextView>(R.id.tv_title)\n    private val tvSubTitle by bindView<CustomFontTextView>(R.id.tv_sub_title)\n    private val tvDesc by bindView<CustomFontTextView>(R.id.tv_desc)\n    private val tvFocus by bindView<CustomFontTextView>(R.id.tv_focus)\n    private val ivMore by bindView<ImageView>(R.id.iv_more)\n    private val imageView by bindView<SimpleDraweeView>(R.id.iv_source)\n    private val mTvDate by bindView<TextView>(R.id.tv_date)\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        init(context)\n    }\n\n    private fun init(context: Context) {\n        LayoutInflater.from(context).inflate(R.layout.layout_common_text, this, true)\n    }\n\n\n    fun setHeader(header: Header, type: String?) {\n\n        with(header) {\n            //设置标题\n            title?.let {\n\n                //分类就居中\n                if (type == \"videoCollectionOfHorizontalScrollCard\") {\n                    tvTitle.gravity = Gravity.CENTER\n                    imageView.visibility = View.GONE\n                    ivMore.visibility = View.VISIBLE\n                } else {\n                    tvTitle.gravity = getGravity(header.textAlign)\n                }\n                tvTitle.visibility = View.VISIBLE\n                tvTitle.text = it\n                tvTitle.setFontType(font)//默认标题加粗\n            }\n\n            //副标题\n            subTitle?.let { it ->\n\n                //分类就居中\n                if (type == \"videoCollectionOfHorizontalScrollCard\") {\n                    tvSubTitle.gravity = Gravity.CENTER\n                    imageView.visibility = View.GONE\n                } else {\n                    tvSubTitle.gravity = getGravity(header.textAlign)\n                }\n                tvSubTitle.visibility = View.VISIBLE\n                tvSubTitle.text = it\n                tvSubTitle.setFontType(subTitleFont)\n            }\n\n\n            //设置图片\n            icon?.let {\n                with(imageView) {\n                    imageView.visibility = View.VISIBLE\n                    //判断图片类型\n                    when (iconType) {\n                        //圆形图片\n                        \"round\" -> imageView.hierarchy.roundingParams = RoundingParams.asCircle()\n                        ////正方形音乐带播放按钮类型\n                        \"squareWithPlayButton\" -> hierarchy.setOverlayImage(context.getDrawable(R.drawable.icon_cover_play_button))\n                        else -> hierarchy.roundingParams?.roundAsCircle = false\n                    }\n                    setImageURI(icon)\n                }\n\n            }\n\n            //设置发布时间\n            time?.let {\n                mTvDate.visibility = View.VISIBLE\n                mTvDate.text = getTimeStr(Date(time!!))\n            }\n\n            //点击关注\n            follow?.let {\n                tvFocus.visibility = View.VISIBLE\n            }\n            //设置描述\n            description?.let {\n                tvDesc.visibility = View.VISIBLE\n                tvDesc.text = description\n            }\n\n\n            //跳转到指定界面\n            actionUrl?.let {\n                setOnClickListener {\n                    ARouter.getInstance().build(Uri.parse(header.actionUrl)).navigation()\n                }\n            }\n        }\n    }\n\n\n    fun setAuthorHeader(content: ContentBean) {\n        with(content) {\n            //设置标题\n            title?.let {\n                tvTitle.visibility = View.VISIBLE\n                tvTitle.text = it\n                tvTitle.setFontType(FontType.BOLD)//默认标题加粗\n            }\n\n\n            //设置图片\n            icon?.let {\n                with(imageView) {\n                    imageView.visibility = View.VISIBLE\n                    //判断图片类型\n                    when (iconType) {\n                        //圆形图片\n                        \"round\" -> imageView.hierarchy.roundingParams = RoundingParams.asCircle()\n                        ////正方形音乐带播放按钮类型\n                        \"squareWithPlayButton\" -> hierarchy.setOverlayImage(context.getDrawable(R.drawable.icon_cover_play_button))\n                        else -> hierarchy.roundingParams?.roundAsCircle = false\n                    }\n                    setImageURI(icon)\n                }\n\n            }\n\n            //点击关注\n            follow?.let {\n                tvFocus.visibility = View.VISIBLE\n            }\n            //设置描述\n            description?.let {\n                tvDesc.visibility = View.VISIBLE\n                tvDesc.text = description\n            }\n\n\n            //跳转到指定界面\n            actionUrl?.let {\n                setOnClickListener {\n                    ARouter.getInstance().build(Uri.parse(content.actionUrl)).navigation()\n                }\n            }\n        }\n    }\n\n    private fun getGravity(textAlign: String?): Int {\n        if (textAlign !== null) {\n            when (textAlign) {\n                \"right\" -> return Gravity.END\n                \"left\" -> return Gravity.START\n                \"middle\" -> return Gravity.CENTER\n            }\n        }\n        return Gravity.START\n    }\n\n\n}\n\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/SearchHotRemindView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.widget.FrameLayout\nimport android.widget.TextView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.utils.bindView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/9 14:40\n * Description:热门搜索词提示界面\n */\n\nclass SearchHotRemindView : FrameLayout {\n\n    private val mTitle: TextView by bindView(R.id.tv_title)\n    private val mResult: TextView by bindView(R.id.tv_result)\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        LayoutInflater.from(context).inflate(R.layout.layout_search_hot_remind_view, this)\n    }\n\n    /**\n     * 设置搜索结果\n     * @param queryWord 搜索内容\n     * @param count 搜索个数\n     */\n    fun setSearchResult(queryWord: CharSequence, count: Int) {\n        mTitle.visibility = View.GONE\n        if (count > 0) {\n            mResult.text = \"- [ $queryWord ] 搜索结果共${count}个 -\"\n            mResult.visibility = View.VISIBLE\n        } else {\n            mResult.visibility = View.GONE\n\n        }\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/StickyNavLayout.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.util.Log\nimport android.view.View\nimport android.widget.LinearLayout\nimport androidx.core.view.NestedScrollingParent2\nimport androidx.core.view.NestedScrollingParentHelper\nimport androidx.viewpager.widget.ViewPager\nimport com.jennifer.andy.simpleeyes.R\nimport kotlinx.android.synthetic.main.layout_blank_card.view.*\n\n\n/**\n * Author:  andy.xwt\n * Date:    2019-06-24 12:16\n * Description: 粘性头部+导航栏+viewPager父控件\n */\n\nclass StickyNavLayout : LinearLayout, NestedScrollingParent2 {\n\n    private var mScrollingParentHelper: NestedScrollingParentHelper = NestedScrollingParentHelper(this)\n\n    private lateinit var mTopView: View//头部view\n    private lateinit var mNavView: View//导航view\n    private lateinit var mViewPager: View//viewpager\n\n    private var mCanScrollDistance: Float = 0f\n\n    private var mListener: ScrollChangeListener? = null\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        orientation = VERTICAL\n    }\n\n\n    /**\n     * 有嵌套滑动到来了，判断父view是否接受嵌套滑动\n     *\n     * @param child 嵌套滑动对应的父类的子类(因为嵌套滑动对于的父View不一定是一级就能找到的，可能挑了两级父View的父View，child的辈分>=target)\n     * @param target 具体嵌套滑动的那个子类\n     * @param axes   支持嵌套滚动轴。水平方向，垂直方向，或者不指定\n     * @param type  滑动类型，ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手势滑动\n     */\n    override fun onStartNestedScroll(child: View, target: View, axes: Int, type: Int) = true\n\n    /**\n     * 当父view接受嵌套滑动，当onStartNestedScroll方法返回true该方法会调用\n     * @param type  滑动类型，ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手势滑动\n     */\n    override fun onNestedScrollAccepted(child: View, target: View, axes: Int, type: Int) {\n        mScrollingParentHelper.onNestedScrollAccepted(child, target, axes, type)\n    }\n\n    /**\n     * 在嵌套滑动的子View未滑动之前，判断父view是否优先与子view处理(也就是父view可以先消耗，然后给子view消耗）\n     *\n     * @param target   具体嵌套滑动的那个子类\n     * @param dx       水平方向嵌套滑动的子View想要变化的距离\n     * @param dy       垂直方向嵌套滑动的子View想要变化的距离 dy<0向下滑动 dy>0 向上滑动\n     * @param consumed 这个参数要我们在实现这个函数的时候指定，回头告诉子View当前父View消耗的距离\n     *                 consumed[0] 水平消耗的距离，consumed[1] 垂直消耗的距离 好让子view做出相应的调整\n     * @param type  滑动类型，ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手势滑动\n     */\n    override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {\n        //这里不管是手势滑动还是fling都需要父控件处理，因为子控件可能会出现手势滑动->手势滑动->fling,\n        //如果这里只处理手势滚动，那么就会出现，父控件滚动一定距离，子控件再fling的问题。\n\n        //如果子view欲向上滑动，则先交给父view滑动\n        val hideTop = dy > 0 && scrollY < mCanScrollDistance\n        //如果子view预向下滑动，必须要子view不能向下滑动后，才能交给父view滑动\n        val showTop = dy < 0 && scrollY >= 0 && !target.canScrollVertically(-1)\n\n        if (hideTop || showTop) {\n            scrollBy(0, dy)\n            consumed[1] = dy\n        }\n\n    }\n\n\n    /**\n     * 嵌套滑动的子View在滑动之后，判断父view是否继续处理（也就是父消耗一定距离后，子再消耗，最后判断父消耗不）\n     *\n     * @param target       具体嵌套滑动的那个子类\n     * @param dxConsumed   水平方向嵌套滑动的子View滑动的距离(消耗的距离)\n     * @param dyConsumed   垂直方向嵌套滑动的子View滑动的距离(消耗的距离)\n     * @param dxUnconsumed 水平方向嵌套滑动的子View未滑动的距离(未消耗的距离)\n     * @param dyUnconsumed 垂直方向嵌套滑动的子View未滑动的距离(未消耗的距离)\n     * @param type  滑动类型，ViewCompat.TYPE_NON_TOUCH fling 效果ViewCompat.TYPE_TOUCH 手势滑动\n     */\n    override fun onNestedScroll(target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int) {\n    }\n\n    /**\n     * 嵌套滑动时，如果父View处理了fling,那子view就没有办法处理fling了，所以这里要返回为false\n     */\n    override fun onNestedPreFling(target: View, velocityX: Float, velocityY: Float) = false\n\n\n    override fun onFinishInflate() {\n        super.onFinishInflate()\n        mTopView = findViewById(R.id.id_sticky_nav_layout_top_view)\n        mNavView = findViewById(R.id.id_sticky_nav_layout_nav_view)\n        mViewPager = findViewById(R.id.id_sticky_nav_layout_viewpager)\n        if (mViewPager !is ViewPager) {\n            throw  RuntimeException(\"id_sticky_nav_layout_viewpager should be viewpager!\")\n        }\n\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec)\n        //ViewPager修改后的高度= 总高度-导航栏高度\n        val layoutParams = mViewPager.layoutParams\n        layoutParams.height = measuredHeight - mNavView.measuredHeight\n        mViewPager.layoutParams = layoutParams\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec)\n    }\n\n    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {\n        super.onSizeChanged(w, h, oldw, oldh)\n        //可滑动高度为，topView的高度- 标题栏的高度\n        mCanScrollDistance = (mTopView.measuredHeight - resources.getDimension(R.dimen.normal_title_height).toInt()).toFloat()\n    }\n\n    override fun scrollTo(x: Int, y: Int) {\n\n        //控制父view滑动范围为0-mCanScrollDistance.之间\n        var adjustY = y\n        if (y < 0) adjustY = 0\n        if (y > mCanScrollDistance) adjustY = mCanScrollDistance.toInt()\n\n        //将移动比例传出去\n        Log.d(\"wtf\", \"adJustY--> $adjustY   $mCanScrollDistance   --->${adjustY / mCanScrollDistance}\")\n        mListener?.onScroll(adjustY / mCanScrollDistance)\n\n        if (adjustY != scrollY) super.scrollTo(x, adjustY)\n    }\n\n    override fun onStopNestedScroll(target: View, type: Int) {\n        mScrollingParentHelper.onStopNestedScroll(view, type)\n    }\n\n    override fun getNestedScrollAxes(): Int {\n        return mScrollingParentHelper.nestedScrollAxes\n    }\n\n\n    interface ScrollChangeListener {\n        /**\n         * 移动监听\n         * @param moveRatio 移动比例\n         */\n        fun onScroll(moveRatio: Float)\n    }\n\n    fun setScrollChangeListener(scrollChangeListener: ScrollChangeListener) {\n        mListener = scrollChangeListener\n    }\n\n    override fun onDetachedFromWindow() {\n        super.onDetachedFromWindow()\n        mListener = null\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/VerticalProgressBar.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget\n\nimport android.content.Context\nimport android.graphics.Canvas\nimport android.util.AttributeSet\nimport android.widget.ProgressBar\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/2/27 18:14\n * Description:竖直progressBar\n */\n\nclass VerticalProgressBar : ProgressBar {\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)\n\n    override fun onDraw(canvas: Canvas) {\n        canvas.rotate(-90f)//反转90度，将水平ProgressBar竖起来\n        canvas.translate(-height.toFloat(), 0f)//将经过旋转后得到的VerticalProgressBar移到正确的位置,注意经旋转后宽高值互换\n        super.onDraw(canvas)\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec)\n        setMeasuredDimension(heightMeasureSpec, widthMeasureSpec)//互换宽高值\n    }\n\n    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {\n        super.onSizeChanged(h, w, oldw, oldh)//互换宽高值\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/VideoDetailAuthorView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget\n\nimport android.content.Context\nimport android.os.Bundle\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.widget.FrameLayout\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.AuthorBean\nimport com.jennifer.andy.simpleeyes.net.Extras\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/2/12 15:19\n * Description:\n */\n\nclass VideoDetailAuthorView : FrameLayout, View.OnClickListener {\n\n    private val mIvImage by bindView<SimpleDraweeView>(R.id.iv_image)\n    private val mTvTitle by bindView<CustomFontTextView>(R.id.tv_title)\n    private val mDescription by bindView<CustomFontTextView>(R.id.tv_desc)\n    private val mAddFollow by bindView<CustomFontTextView>(R.id.tv_follow)\n\n\n    private lateinit var mTitle: String\n    private lateinit var mId: String\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        init(context)\n    }\n\n    private fun init(context: Context) {\n        LayoutInflater.from(context).inflate(R.layout.layout_video_author_head, this, true)\n        setOnClickListener(this)\n    }\n\n    override fun onClick(v: View?) {\n        val bundle = Bundle().apply {\n            putString(Extras.TAB_INDEX, \"0\")\n            putString(Extras.TITLE, mTitle)\n            putString(Extras.ID, mId)\n        }\n        ARouter.getInstance()\n                .build(\"/pgc/detail\")\n                .with(bundle)\n                .navigation()\n    }\n\n    /**\n     * 设置视频作者信息\n     */\n    fun setVideoAuthorInfo(author: AuthorBean) {\n        mIvImage.setImageURI(author.icon)\n        mTvTitle.text = author.name\n        mDescription.text = author.description\n        mTitle = author.name\n        mId = author.id\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/font/CustomFontTextView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.font\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.appcompat.widget.AppCompatTextView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/10/30 16:45\n * Description:自定义字体textView\n */\n\nopen class CustomFontTextView : AppCompatTextView {\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        if (!isInEditMode) {\n            TypefaceManager.setTextTypeFace(context, attrs, this)\n        }\n    }\n\n    /**\n     * 根据字体类型设置字体\n     *\n     * @param fontType\n     */\n    fun setFontType(fontType: FontType) {\n        if (!isInEditMode) {\n            TypefaceManager.setTextTypeFace(this, fontType)\n        }\n    }\n\n    /** 根据字体名称设置，如果没有找到相应的字体，则使用默认的字体\n     * @param fontName   字体名称\n     * @param defaultFontType 默认字体\n     */\n    fun setFontType(fontName: String?, defaultFontType: FontType = FontType.BOLD) {\n        if (!isInEditMode) {\n            val fontType = TypefaceManager.getFontTypeByName(fontName)\n            TypefaceManager.setTextTypeFace(this, fontType ?: defaultFontType)\n\n\n        }\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/font/CustomFontTypeWriterTextView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.font\n\nimport android.content.Context\nimport android.text.TextUtils\nimport android.util.AttributeSet\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/12/12 11:10\n * Description:打印文字TextView\n */\n\nclass CustomFontTypeWriterTextView : CustomFontTextView {\n\n\n    private lateinit var mSloganSpanGroup: PrintSpanGroup\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)\n\n    /**\n     * 打印文字\n     */\n    fun printText(text: String?, printTime: Long = 0) {\n        if (!TextUtils.isEmpty(text)) {\n            mSloganSpanGroup = PrintSpanGroup(text!!, printTime)\n            mSloganSpanGroup.startPrint(this)\n        }\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/font/FontType.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.font\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/11/2 14:41\n * Description:字体常量 包括 方正兰亭细黑简体,方正兰亭中粗黑简体，拉丁，龙虾字体\n */\nenum class FontType(var index: Int, var fontName: String, val path: String) {\n\n    NORMAL(0, \"Normal\", \"fonts/FZLanTingHeiS-L-GB-Regular.TTF\"),//方正兰亭细黑简体\n    BOLD(1, \"Bold\", \"fonts/FZLanTingHeiS-DB1-GB-Regular.TTF\"),//方正兰亭中粗黑简体\n    FUTURE(2, \"Future\", \"fonts/Futura-CondensedMedium.ttf\"),//拉丁\n    LOBSTER(3, \"Lobster\", \"fonts/Lobster-1.4.otf\");//龙虾字体\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/font/PrintSpan.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.font\n\nimport android.text.TextPaint\nimport android.text.style.MetricAffectingSpan\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/11/27 15:17\n * Description:\n */\n\nclass PrintSpan(var printAlpha: Int) : MetricAffectingSpan() {\n\n\n    override fun updateMeasureState(tp: TextPaint) {\n        tp.alpha = printAlpha\n    }\n\n    override fun updateDrawState(tp: TextPaint) {\n        tp.alpha = printAlpha\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/font/PrintSpanGroup.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.font\n\nimport android.animation.ObjectAnimator\nimport android.graphics.Color\nimport android.text.SpannableString\nimport android.text.Spanned\nimport android.util.Property\nimport android.widget.TextView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/11/27 15:29\n * Description:\n */\n\nclass PrintSpanGroup constructor(printText: CharSequence, var printTime: Long = 0) {\n\n    private var mSpans: MutableList<PrintSpan> = mutableListOf()\n    private var spannableString: SpannableString = SpannableString(printText)\n    private var mAlpha: Float = 255f\n\n    companion object {\n         const val DEFAULT_PRINT_TIME = 500L\n    }\n\n\n    private val TYPE_WRITER_GROUP_ALPHA_PROPERTY = object : Property<PrintSpanGroup, Float>(Float::class.java, \"type_writer_group_alpha_property\") {\n\n        override fun set(printGroup: PrintSpanGroup, alpha: Float) {\n            setAlpha(alpha)\n        }\n\n        override fun get(printGroup: PrintSpanGroup): Float? {\n            return mAlpha\n        }\n    }\n\n    init {\n        buildPrintSpanGroup(0, printText.length - 1)\n        printTime = if (printTime > 0) printTime else DEFAULT_PRINT_TIME\n    }\n\n    /**\n     * 将text拆分成单个span\n     */\n    private fun buildPrintSpanGroup(start: Int, end: Int) {\n        for (i in start..end) {\n            val printSpan = PrintSpan(Color.TRANSPARENT)\n            spannableString.setSpan(printSpan, i, i + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)\n            mSpans.add(printSpan)\n        }\n\n    }\n\n    /**\n     * 设置单个span的颜色显示\n     */\n    private fun setAlpha(alpha: Float) {\n        val size = mSpans.size\n        var total = size * 1f * alpha//计算范围，哪个范围显示，哪个范围不显示\n        for (i in 0 until size) {\n            val printSpan = mSpans[i]\n            if (i <= total) {\n                printSpan.printAlpha = 255\n            } else {\n                printSpan.printAlpha = 0\n            }\n        }\n    }\n\n    /**\n     * 执行打印请求\n     */\n    fun startPrint(textView: TextView) {\n        val objectAnimator = ObjectAnimator.ofFloat(this, TYPE_WRITER_GROUP_ALPHA_PROPERTY, 0f, 1f)\n        objectAnimator.duration = printTime\n        objectAnimator.start()\n        objectAnimator.addUpdateListener {\n            textView.text = spannableString\n        }\n\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/font/TypefaceManager.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.font\n\nimport android.content.Context\nimport android.graphics.Typeface\nimport android.util.AttributeSet\nimport android.widget.TextView\nimport com.jennifer.andy.simpleeyes.AndyApplication\nimport com.jennifer.andy.simpleeyes.R\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/10/30 17:19\n * Description:字体管理工具类\n */\n\nobject TypefaceManager {\n\n    private val mTypeFaceMap: MutableMap<FontType, Typeface> = mutableMapOf()\n    private var mTypeFaceIndex: Int = FontType.NORMAL.index\n\n    /**\n     * 设置textView字体,如果参数中有字体，就采用本身的，如果没有就根据设置的值设置字体\n     *\n     *@param context  上下文\n     *@param attributes 参数\n     *@param textView  textView\n     */\n    fun setTextTypeFace(context: Context, attributes: AttributeSet?, textView: TextView) {\n        if (textView.typeface != null && textView.typeface.style != 0) {\n            return\n        }\n        val typeArray = context.obtainStyledAttributes(attributes, R.styleable.CustomFontTextView)\n        mTypeFaceIndex = typeArray.getInteger(R.styleable.CustomFontTextView_font_name, mTypeFaceIndex)\n        if (mTypeFaceIndex in 0..FontType.values().size) {\n            textView.typeface = getTypeFace(FontType.values()[mTypeFaceIndex])\n        }\n        typeArray.recycle()\n\n    }\n\n    /**\n     * 根据字体设置textVie显示的字体\n     */\n    fun setTextTypeFace(textView: TextView, fontType: FontType?) {\n        val localTypeFace = getTypeFace(fontType)\n        textView.typeface = localTypeFace\n    }\n\n    /**\n     * 根据名称获取字体类型\n     * @return  字体类型\n     */\n    fun getFontTypeByName(fontName: String?): FontType? {\n        return FontType.values().firstOrNull { it.fontName == fontName }\n    }\n\n\n    /**\n     * 根据字体类型获取字体\n     * @param  fontType 字体类型\n     */\n    fun getTypeFace(fontType: FontType?): Typeface? {\n        return fontType?.let {\n            var typeFace = mTypeFaceMap[fontType]\n            if (typeFace == null) {\n                typeFace = Typeface.createFromAsset(AndyApplication.INSTANCE.assets, fontType.path)\n                mTypeFaceMap[fontType] = typeFace\n            }\n            return typeFace\n        }\n    }\n\n}\n\n\n\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/image/CenterAlignImageSpan.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.image\n\nimport android.graphics.Bitmap\nimport android.graphics.Canvas\nimport android.graphics.Paint\nimport android.graphics.drawable.Drawable\nimport android.text.style.ImageSpan\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/4/4 15:56\n * Description:文字图片居中显示\n */\n\nclass CenterAlignImageSpan : ImageSpan {\n\n    constructor(drawable: Drawable) : super(drawable)\n\n    constructor(bitmap: Bitmap) : super(bitmap)\n\n    override fun draw(canvas: Canvas, text: CharSequence?, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {\n        val drawable = drawable\n        val fm = paint.fontMetrics\n        val transY = (y + fm.descent + y + fm.ascent) / 2 - drawable.bounds.bottom / 2//计算y方向的位移\n        canvas.save()\n        canvas.translate(x, transY)//绘制图片位移一段距离\n        drawable.draw(canvas)\n        canvas.restore()\n    }\n\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/image/imageloader/FrescoImageLoader.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.image.imageloader\n\nimport android.content.Context\nimport android.net.Uri\nimport android.widget.ImageView\nimport com.facebook.drawee.view.SimpleDraweeView\nimport com.youth.banner.loader.ImageLoader\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/11/3 11:58\n * Description:banner中的图片加载器\n */\n\nclass FrescoImageLoader : ImageLoader() {\n\n    override fun displayImage(context: Context?, path: Any?, imageView: ImageView?) {\n        imageView?.setImageURI(Uri.parse(path as String))\n    }\n\n\n    override fun createImageView(context: Context?): ImageView {\n        return SimpleDraweeView(context)\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/pull/head/EliteHeaderView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.pull.head\n\nimport android.animation.ArgbEvaluator\nimport android.animation.ValueAnimator\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.widget.ImageView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\nimport com.jennifer.andy.simpleeyes.widget.pull.refresh.PullRefreshView\nimport kotlin.math.abs\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/6/25 13:16\n * Description: 每日精选刷新头布局，包括内部眼睛的旋转，和文字的变色\n */\n\nclass EliteHeaderView : PullRefreshView {\n\n    private val mHeadInner: ImageView by bindView(R.id.iv_head_inner)\n    private val mHeadOuter: ImageView by bindView(R.id.iv_head_outer)\n\n    private val mLoadingMessage: CustomFontTextView by bindView(R.id.tv_loading_msg)\n    private var mRotationAnimator: ValueAnimator? = null\n\n    private var mYDistance = 0f\n\n    companion object {\n        private const val ROTATION_DAMP = 2f//阻尼系数\n    }\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        init(context)\n    }\n\n    private fun init(context: Context) {\n        LayoutInflater.from(context).inflate(R.layout.refresh_daily_elite_header, this, true)\n    }\n\n    /**\n     * 处理额外的下拉 dy\n     */\n    override fun handleExtraPullEvent(dy: Float) {\n        //处理眼睛旋转\n        mHeadInner.rotation = mHeadInner.rotation + (dy.toInt() / ROTATION_DAMP)\n\n    }\n\n    /**\n     * 处理有效的下拉 dy\n     */\n    override fun handleValidPullEvent(dy: Float) {\n        //处理文字透明度\n        mYDistance += dy\n        if (mYDistance >= height - mLoadingMessage.bottom && mYDistance <= height - mLoadingMessage.top) {\n            val argbEvaluator = ArgbEvaluator()\n            val fraction = abs(mLoadingMessage.bottom - height + mYDistance) / (mLoadingMessage.height)\n            val textColor = argbEvaluator.evaluate(fraction, 0, 0xff444444.toInt()) as Int\n            mLoadingMessage.setTextColor(textColor)\n        }\n    }\n\n\n    override fun doRefresh() {\n        doInnerEyeAnimator()\n    }\n\n    override fun getDoRefreshHeight() = mHeadOuter.height\n\n    /**\n     * 执行内部眼睛动画,执行之前，先停止之前的\n     */\n    private fun doInnerEyeAnimator() {\n        reset()\n        mRotationAnimator = ValueAnimator.ofFloat(0f, 360f)\n        mRotationAnimator?.addUpdateListener { mHeadInner.rotation += 10 }\n        mRotationAnimator?.duration = 200\n        mRotationAnimator?.repeatCount = -1\n        mRotationAnimator?.start()\n    }\n\n    override fun reset() {\n        if (mRotationAnimator != null) {\n            mRotationAnimator?.cancel()\n        }\n    }\n\n    override fun onDetachedFromWindow() {\n        super.onDetachedFromWindow()\n        reset()\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/pull/head/HeaderRefreshView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.pull.head\n\nimport android.animation.ValueAnimator\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.view.animation.Animation\nimport android.view.animation.LinearInterpolator\nimport android.view.animation.RotateAnimation\nimport android.widget.FrameLayout\nimport android.widget.ImageView\nimport android.widget.RelativeLayout\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.utils.dip2px\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/12/13 16:50\n * Description:\n */\n\nclass HeaderRefreshView : FrameLayout {\n\n    private val mRefreshContainer by bindView<RelativeLayout>(R.id.rl_refresh_container)\n    private val mIvRefresh by bindView<ImageView>(R.id.iv_refresh)\n    private var mRotateAnimation: RotateAnimation? = null\n\n    /**\n     * 执行刷新阀值 50dp\n     */\n    companion object {\n        private const val REFRESH_THRESHOLD_VALUE = 50f\n    }\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        init(context)\n    }\n\n    private fun init(context: Context) {\n        LayoutInflater.from(context).inflate(R.layout.refresh_category_header, this, true)\n        mRefreshContainer.background.alpha = 0\n        mIvRefresh.imageAlpha = 0\n    }\n\n    /**\n     * 显示刷新遮罩\n     */\n    fun showRefreshCover(scrollValue: Int) {\n        if (scrollValue in 1..getRefreshThresholdValue()) {\n            val percent = (scrollValue.toFloat() / getRefreshThresholdValue())\n            mRefreshContainer.background.alpha = (percent * 255).toInt()\n            mIvRefresh.imageAlpha = (percent * 255).toInt()\n            mIvRefresh.scaleX = percent\n            mIvRefresh.scaleY = percent\n        } else {\n            mRefreshContainer.background.alpha = 255\n            mIvRefresh.imageAlpha = 255\n            startRefreshAnimation()\n        }\n\n    }\n\n    /**\n     * 执行刷新动画\n     */\n    private fun startRefreshAnimation() {\n        if (mRotateAnimation == null) {\n            mRotateAnimation = RotateAnimation(0f, 360f,\n                    Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)\n                    .apply {\n                        interpolator = LinearInterpolator()\n                        repeatCount = -1\n                        duration = 1000\n                    }\n            mIvRefresh.startAnimation(mRotateAnimation)\n        }\n    }\n\n    /**\n     * 关闭刷新遮罩\n     */\n    fun hideRefreshCover() {\n        mIvRefresh.clearAnimation()\n        mRotateAnimation = null\n        ValueAnimator.ofFloat(1f, 0f).apply {\n            duration = 500\n            addUpdateListener {\n                val animatedValue = (it.animatedValue) as Float\n                mRefreshContainer.background.alpha = (animatedValue * 255).toInt()\n                mIvRefresh.imageAlpha = animatedValue.toInt()\n            }\n        }.start()\n    }\n\n    /**\n     * 获取刷新阀值\n     */\n    fun getRefreshThresholdValue() = context.dip2px(REFRESH_THRESHOLD_VALUE)\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/pull/head/HomePageHeaderView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.pull.head\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.widget.FrameLayout\nimport android.widget.ImageView\nimport android.widget.RelativeLayout\nimport androidx.viewpager.widget.ViewPager\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.entity.TopIssue\nimport com.jennifer.andy.simpleeyes.ui.base.BaseFragment\nimport com.jennifer.andy.simpleeyes.ui.home.DailyEliteActivity\nimport com.jennifer.andy.simpleeyes.ui.search.SearchHotActivity\nimport com.jennifer.andy.simpleeyes.ui.video.VideoDetailActivity\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.utils.readyGo\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTypeWriterTextView\nimport com.jennifer.andy.simpleeyes.widget.image.imageloader.FrescoImageLoader\nimport com.youth.banner.Banner\nimport com.youth.banner.BannerConfig\nimport java.util.*\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/11/27 13:55\n * Description:主界面头部布局\n */\n\nclass HomePageHeaderView : FrameLayout {\n\n    private val mBanner: Banner by bindView(R.id.head_banner)\n    private val mTitle: CustomFontTypeWriterTextView by bindView(R.id.tv_title)\n    private val mText: CustomFontTypeWriterTextView by bindView(R.id.tv_text)\n    private val mHeadRefreshView: HeaderRefreshView by bindView(R.id.head_refresh)\n    private val mIvSearch: ImageView by bindView(R.id.iv_search)\n    private val mMoreContainer: RelativeLayout by bindView(R.id.rl_more_container)\n\n    private lateinit var mTopIssue: TopIssue\n    private lateinit var mBaseFragment: BaseFragment<*, *>\n\n    private var mScrollValue = 0\n    private var currentPosition = -1\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        LayoutInflater.from(context).inflate(R.layout.layout_category_head_view, this, true)\n        init()\n    }\n\n    /**\n     * 初始化\n     */\n    private fun init() {\n        //设置banner滑动监听\n        mBanner.setOnPageChangeListener(object : ViewPager.OnPageChangeListener {\n            override fun onPageScrollStateChanged(state: Int) {\n            }\n\n            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {\n            }\n\n            override fun onPageSelected(position: Int) {\n                //播放动画，并设置打印文字\n                if (currentPosition != position) {//处理banner的position问题\n                    mTitle.printText(mTopIssue.data.itemList[position].data.title)\n                    mText.printText(mTopIssue.data.itemList[position].data.slogan)\n                    currentPosition = position\n                }\n            }\n        })\n        //跳转到搜索界面\n        mIvSearch.setOnClickListener {\n            mBaseFragment.readyGo<SearchHotActivity>()\n        }\n        //跳转到每日精选\n        mMoreContainer.setOnClickListener {\n            mBaseFragment.readyGo<DailyEliteActivity>()\n        }\n\n    }\n\n    /**\n     * 设置头部信息\n     */\n    fun setHeaderInfo(topIssue: TopIssue, videoListInfo: MutableList<Content>, baseFragment: BaseFragment<*, *>) {\n        mTopIssue = topIssue\n        mBaseFragment = baseFragment\n        mBanner.setImageLoader(FrescoImageLoader())\n        mBanner.setImages(getTopIssueCardUrl(topIssue.data.itemList))\n        mBanner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR)\n        mBanner.setIndicatorGravity(BannerConfig.CENTER)\n        mBanner.isAutoPlay(true)\n        mBanner.start()\n        mBanner.setDelayTime(6000)\n        mBanner.setOnBannerListener {\n            val item = mTopIssue.data.itemList[it]\n            VideoDetailActivity.start(context, item.data, videoListInfo as ArrayList, 0)\n        }\n    }\n\n    /**\n     * 显示刷新遮罩\n     */\n    fun showRefreshCover(scrollValue: Int) {\n        mScrollValue = scrollValue\n        mHeadRefreshView.showRefreshCover(scrollValue)\n        mBanner.stopAutoPlay()\n    }\n\n    /**\n     * 关闭刷新遮罩\n     */\n    fun hideRefreshCover() {\n        mHeadRefreshView.hideRefreshCover()\n        mBanner.startAutoPlay()\n    }\n\n    /**\n     * 获取顶部图片地址集合\n     */\n    private fun getTopIssueCardUrl(itemList: MutableList<Content>) = itemList.map { it.data.cover.feed }\n\n    /**\n     * 判断是否达到刷新阀值,\n     */\n    fun judgeCanRefresh() = mScrollValue >= mHeadRefreshView.getRefreshThresholdValue()\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/pull/head/VideoDetailHeadView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.pull.head\n\nimport android.animation.ObjectAnimator\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.widget.FrameLayout\nimport com.alibaba.android.arouter.launcher.ARouter\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.UserPreferences\nimport com.jennifer.andy.simpleeyes.utils.getElapseTimeForShow\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\nimport com.jennifer.andy.simpleeyes.widget.font.CustomFontTypeWriterTextView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/2/11 18:02\n * Description:包含视频的介绍视频信息，收藏、分享、回复等操作\n */\n\nclass VideoDetailHeadView : FrameLayout, View.OnClickListener {\n\n    private val mTitle by bindView<CustomFontTypeWriterTextView>(R.id.tv_title)\n    private val mTvTime by bindView<CustomFontTypeWriterTextView>(R.id.tv_time)\n    private val mDescription by bindView<CustomFontTypeWriterTextView>(R.id.tv_desc)\n\n    private val mFavorite by bindView<CustomFontTextView>(R.id.tv_favorite)\n    private val mShare by bindView<CustomFontTextView>(R.id.tv_share)\n    private val mReply by bindView<CustomFontTextView>(R.id.tv_reply)\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        init(context)\n    }\n\n    private fun init(context: Context) {\n        LayoutInflater.from(context).inflate(R.layout.layout_video_detail_head, this, true)\n        mFavorite.setOnClickListener(this)\n        mShare.setOnClickListener(this)\n        mReply.setOnClickListener(this)\n        mFavorite.setOnClickListener(this)\n    }\n\n\n    /**\n     * 开启动画\n     */\n    fun startScrollAnimation() {\n        val widthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)\n        val heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)\n        measure(widthSpec, heightSpec)\n        ObjectAnimator.ofFloat(this, \"translationY\", -measuredHeight.toFloat(), 0f).apply {\n            duration = 300\n            start()\n        }\n    }\n\n    override fun onClick(v: View) {\n        val userIsLogin = UserPreferences.getUserIsLogin()\n        if (!userIsLogin) //如果用户没登录，直接跳转到登录界面\n            ARouter.getInstance().build(\"/github/Login\").navigation()\n        else {\n            when (v.id) {\n                R.id.tv_favorite -> addFavorite()\n                R.id.tv_share -> showShare()\n                R.id.tv_reply -> addReply()\n                R.id.tv_download -> downloadVideo()\n            }\n        }\n\n    }\n\n\n    /**\n     * 添加收藏\n     */\n    private fun addFavorite() {\n\n    }\n\n    /**\n     * 显示分享\n     */\n    private fun showShare() {\n\n    }\n\n    /**\n     * 添加回复\n     */\n    private fun addReply() {\n\n    }\n\n    /**\n     * 下载视频\n     */\n    private fun downloadVideo() {\n\n\n    }\n\n\n    /**\n     * 设置收藏次数\n     */\n    fun setFavoriteCount(count: String) {\n        mFavorite.text = count\n    }\n\n    /**\n     * 设置分享次数\n     */\n    fun setShareCount(count: String) {\n        mShare.text = count\n    }\n\n    /**\n     * 设置评论个数\n     */\n    fun setReplayCount(count: String) {\n        mReply.text = count\n    }\n\n    /**\n     * 设置标题\n     */\n    fun setTitle(title: String) {\n        mTitle.printText(title)\n    }\n\n    /**\n     * 设置描述\n     */\n    fun setDescription(description: String) {\n        mDescription.printText(description)\n    }\n\n    /**\n     * 设置种类与时间\n     */\n    fun setCategoryAndTime(category: String, duration: Int) {\n        val description = \"#$category   /   ${getElapseTimeForShow(duration)}\"\n        mTvTime.text = description\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/pull/refresh/LinearLayoutManagerWithSmoothScroller.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.pull.refresh\n\nimport android.content.Context\nimport android.graphics.PointF\nimport android.util.DisplayMetrics\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.LinearSmoothScroller\nimport androidx.recyclerview.widget.RecyclerView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/6/19 22:24\n * Description:SmoothScrollerToTop 平滑滚动式 滚动位置的View与RecyclerView顶部对齐\n */\n\nclass LinearLayoutManagerWithSmoothScroller : LinearLayoutManager {\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, orientation: Int, reverseLayout: Boolean) : super(context, orientation, reverseLayout)\n\n    override fun smoothScrollToPosition(recyclerView: RecyclerView, state: RecyclerView.State?, position: Int) {\n        val smoothScroller = TopSnappedSmoothScroller(recyclerView.context)\n        smoothScroller.targetPosition = position\n        startSmoothScroll(smoothScroller)\n\n    }\n\n    inner class TopSnappedSmoothScroller(context: Context) : LinearSmoothScroller(context) {\n\n\n        override fun computeScrollVectorForPosition(targetPosition: Int): PointF? {\n            return this@LinearLayoutManagerWithSmoothScroller.computeScrollVectorForPosition(targetPosition)\n        }\n\n        /**\n         * 默认顶部与RecyclerView对齐\n         */\n        override fun getVerticalSnapPreference(): Int {\n            return SNAP_TO_START\n        }\n\n        /**\n         * 滚动一英寸默认需要时间这里我改小了，意味着滚动速度变快了\n         */\n        override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics) = 15f / displayMetrics.densityDpi\n\n\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/pull/refresh/PullRefreshView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.pull.refresh\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.widget.FrameLayout\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/6/25 17:29\n * Description:\n */\n\nabstract class PullRefreshView : FrameLayout {\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)\n\n\n    /**\n     * 处理额外下拉事件\n     */\n    abstract fun handleExtraPullEvent(dy: Float)\n\n    /**\n     * 处理有效下拉事件\n     */\n    abstract fun handleValidPullEvent(dy: Float)\n\n    /**\n     * 执行刷新操作\n     */\n    abstract fun doRefresh()\n\n    /**\n     * 执行刷新的高度\n     */\n    open fun getDoRefreshHeight() = height\n\n\n    /**\n     * 重置操作\n     */\n    abstract fun reset()\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/pull/refresh/PullToRefresh.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.pull.refresh\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/6/15 10:12\n * Description:\n */\n\ninterface PullToRefresh<T> {\n\n    /**\n     * 设置刷新View\n     */\n    fun initRefreshView(): PullRefreshView?\n\n\n    /**\n     * 是否正在刷新\n     */\n    fun isRefreshing(): Boolean\n\n    /**\n     * 刷新完毕\n     */\n    fun refreshComplete()\n\n    /**\n     * 下拉刷新是否可用\n     */\n    fun isPullToRefreshEnabled(): Boolean\n\n    /**\n     * 获取根布局\n     */\n    fun getRootView(): T\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/pull/refresh/PullToRefreshBase.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.pull.refresh\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.*\nimport android.widget.LinearLayout\nimport android.widget.Scroller\nimport androidx.customview.widget.ViewDragHelper.INVALID_POINTER\nimport kotlin.math.abs\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/6/15 10:16\n * Description: 基础刷新父布局。处理竖直方向上的事件分发。处理了多点触摸下的刷新。\n */\n\nabstract class PullToRefreshBase<T : View> : LinearLayout, PullToRefresh<T> {\n\n\n    protected lateinit var mRootView: T\n    protected var mRefreshView: PullRefreshView? = null\n    private lateinit var mSmoothScroller: Scroller\n    private var mTouchSlop: Int = 0\n    lateinit var refreshListener: () -> Unit\n\n\n    /**\n     * 用来记录y轴上的速度\n     */\n    private lateinit var mVelocityTracker: VelocityTracker\n\n    /**\n     * 刷新View实际高度\n     */\n    private var mRefreshHeight = 0\n\n    /**\n     * 拖动的时候有效的手指id\n     */\n    private var mActivePointerId = INVALID_POINTER\n\n    /**\n     * 当前刷新状态\n     */\n    var mScrollState = SCROLL_STATE_IDLE\n\n    companion object {\n        /**\n         * 当前刷新是空闲的\n         */\n        const val SCROLL_STATE_IDLE = 0\n\n        /**\n         * 当前正在拖动\n         */\n        const val SCROLL_STATE_DRAGGING = 1\n\n        /**\n         * 本身的滑动\n         */\n        const val SCROLL_STATE_SETTLING = 2\n\n        /**\n         * 滑动最小接近距离，单位dp\n         */\n        const val SCROLL_CLOSE_ENOUGH = 2\n\n        private const val ROTATION_DAMP = 1.5f//阻尼系数\n    }\n\n\n    /**\n     * 最后点击的位置\n     */\n    private var mLastMotionX = 0f\n    private var mLastMotionY = 0f\n    private var mInitialMotionX = 0f\n    private var mInitialMotionY = 0f\n\n    private var isRefreshEnable = true //是否能下拉刷新\n    private var isBeingDragged = false//是否正在进行拖拽\n    private var isUnableToDrag = false//当前不能拖拽 用于判断x y轴移动距离的\n    private var isScrollStarted = false//内容是否开始滚动\n    private var isRefreshIng = false//是否正在刷新\n\n    private val mEndScrollRunnable = Runnable {\n        mScrollState = SCROLL_STATE_IDLE\n    }\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        init(context, attrs)\n    }\n\n    private fun init(context: Context, attrs: AttributeSet?) {\n        orientation = VERTICAL\n        mRootView = createRootView(context)\n        mTouchSlop = ViewConfiguration.get(getContext()).scaledTouchSlop\n        mSmoothScroller = Scroller(getContext())\n        mRefreshView = initRefreshView()\n        addView(mRefreshView, 0)\n        addView(mRootView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec)\n        if (mRefreshView != null) {\n            //获取刷新view的高度\n            measureChild(mRefreshView, widthMeasureSpec, heightMeasureSpec)\n            mRefreshHeight = mRefreshView!!.measuredHeight\n\n            val layoutParams = mRefreshView?.layoutParams as LayoutParams\n            layoutParams.topMargin = -mRefreshHeight\n            mRefreshView?.layoutParams = layoutParams\n        } else {\n            throw RuntimeException(\"you not set refreshView!!\")\n        }\n\n    }\n\n    override fun onInterceptTouchEvent(event: MotionEvent): Boolean {\n        if (isPullToRefreshEnabled()) {\n            val action = event.action\n            if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {\n                //重置\n                isBeingDragged = false\n                return false\n            }\n\n            if (action != MotionEvent.ACTION_DOWN) {\n                if (isBeingDragged) {//如果正在进行拖拽，就拦截\n                    return true\n                }\n                if (isUnableToDrag) {//如果不能被拖拽，就不拦截\n                    return false\n                }\n                if (!mSmoothScroller.computeScrollOffset()) {\n                    mSmoothScroller.abortAnimation()\n                }\n            }\n            when (action and MotionEvent.ACTION_MASK) {\n                MotionEvent.ACTION_DOWN -> {\n                    if (isReadyForPullStart()) {\n                        mLastMotionX = event.x\n                        mInitialMotionX = event.x\n                        mLastMotionY = event.y\n                        mInitialMotionY = event.y\n\n                        //获取有效手指 第一个有效手指的角标总是0\n                        mActivePointerId = event.getPointerId(0)\n\n                        isUnableToDrag = false\n                        //在点击的时候，判断当前刷新界面是否下拉显示，或者在回退的路上\n                        if (mScrollState == SCROLL_STATE_SETTLING && getScrollDiff() > SCROLL_CLOSE_ENOUGH) {\n                            isBeingDragged = true\n                            mScrollState = SCROLL_STATE_DRAGGING\n                        } else {\n                            isBeingDragged = false\n                        }\n                    }\n\n                }\n                MotionEvent.ACTION_MOVE -> {\n\n                    //获取有效的手指的距离\n                    val actionPointerId = mActivePointerId\n                    if (mActivePointerId != INVALID_POINTER) {\n\n                        val pointerIndex = event.findPointerIndex(actionPointerId)\n                        val x = event.getX(pointerIndex)\n                        val xAbs = abs(x - mInitialMotionX)\n\n                        val y = event.getY(pointerIndex)\n                        val dy = y - mLastMotionY\n                        val yAbs = abs(dy)\n\n                        //如果竖直移动距离大于水平移动距离且为下拉事件，设置当前为拖动状态\n                        if (yAbs > mTouchSlop && yAbs * 0.5 > xAbs && dy > 0) {\n                            isBeingDragged = true\n                            mScrollState = SCROLL_STATE_DRAGGING\n                            mLastMotionX = x\n                            mLastMotionY = if (dy > 0) mInitialMotionY + mTouchSlop else mInitialMotionY - mTouchSlop\n                        } else if (xAbs > mTouchSlop) {\n                            isUnableToDrag = true\n                        }\n                    }\n                }\n                MotionEvent.ACTION_POINTER_UP -> {\n                    onSecondaryPointerUp(event)\n                }\n            }\n\n        }\n        return isBeingDragged\n    }\n\n    /**\n     * 重新赋值有效的手指，并记录抬起的手指的最后的y轴距离\n     */\n    private fun onSecondaryPointerUp(event: MotionEvent) {\n        val pointerIndex = event.actionIndex\n        val pointerId = event.getPointerId(pointerIndex)\n        if (pointerId == mActivePointerId) {\n            val newPointerIndex = if (pointerIndex == 0) 1 else 0\n            mLastMotionY = event.getY(newPointerIndex)\n            mActivePointerId = event.getPointerId(newPointerIndex)\n\n        }\n    }\n\n    override fun onTouchEvent(event: MotionEvent): Boolean {\n        if (event.action == MotionEvent.ACTION_DOWN && event.edgeFlags != 0)\n            return false\n        if (mRefreshView == null) {\n            return false\n        }\n        val action = event.action\n        when (action and MotionEvent.ACTION_MASK) {\n            MotionEvent.ACTION_CANCEL -> {\n                if (isBeingDragged) {\n                    //滚动回去\n                    isRefreshIng = false\n                    smoothScrollTo(-scrollX, -scrollY)\n                    isBeingDragged = false\n                }\n            }\n            MotionEvent.ACTION_DOWN -> {\n                //这个时候要停止刷新的滚动\n                mLastMotionX = event.x\n                mInitialMotionX = event.x\n                mLastMotionY = event.y\n                mInitialMotionY = event.y\n\n                //获取有效手指 第一个有效手指的角标总是0\n                mActivePointerId = event.getPointerId(0)\n                mSmoothScroller.abortAnimation()\n            }\n            MotionEvent.ACTION_MOVE -> {\n                if (isBeingDragged) {\n                    //获取有效的手指的距离\n                    val actionPointerId = mActivePointerId\n                    if (mActivePointerId != INVALID_POINTER) {\n\n                        val pointerIndex = event.findPointerIndex(actionPointerId)\n                        val x = event.getX(pointerIndex)\n                        val xAbs = abs(x - mLastMotionX)\n\n                        val y = event.getY(pointerIndex)\n                        val dy = y - mLastMotionY\n                        val yAbs = abs(dy)\n\n                        //如果竖直移动距离大于水平移动距离，设置当前为拖动状态\n                        if (yAbs > xAbs && scrollY <= 0) {\n                            isBeingDragged = true\n                            mScrollState = SCROLL_STATE_DRAGGING\n                            mLastMotionX = x\n                            mLastMotionY = y\n                            performDrag(dy / ROTATION_DAMP)\n                            dispatchExtraPullEvent(dy)//将竖直移动距离分发出去\n                        }\n                    }\n\n                }\n            }\n            MotionEvent.ACTION_UP -> {\n                if (isBeingDragged) {\n                    isBeingDragged = false\n                    if (abs(scrollY) > mRefreshHeight / 2) {//如果超过一半就执行请求\n                        //执行刷新请求\n                        isRefreshIng = true\n                        smoothScrollTo(0, -(mRefreshView!!.getDoRefreshHeight() + scrollY), 500)\n                    } else {\n                        isRefreshIng = false\n                        //滚动回去\n                        smoothScrollTo(-scrollX, -scrollY, 500)\n                    }\n                }\n            }\n            MotionEvent.ACTION_POINTER_DOWN -> {\n                val index = event.actionIndex\n                val y = event.getY(index)\n                val x = event.getX(index)\n                mLastMotionY = y\n                mLastMotionX = x\n                mActivePointerId = event.getPointerId(index)\n            }\n            MotionEvent.ACTION_POINTER_UP -> {\n                onSecondaryPointerUp(event)\n            }\n        }\n\n        return true\n    }\n\n    /**\n     * 开始滚动\n     */\n    private fun performDrag(dy: Float) {\n        if (scrollY - dy <= 0) {//控制滚动范围为0到headView的显示高度\n            if (abs(scrollY - dy) in 0..mRefreshHeight) {\n                scrollBy(0, -dy.toInt())\n                dispatchValidPullEvent(dy)\n            }\n        }\n    }\n\n\n    override fun computeScroll() {\n        isScrollStarted = true\n        //处理自己滚动效果，滚动到顶部或者到滚动到RefreshView的高度位置\n        if (mScrollState == SCROLL_STATE_SETTLING) {\n            if (mSmoothScroller.computeScrollOffset()) {\n                val x = mSmoothScroller.currX\n                val y = mSmoothScroller.currY\n                scrollTo(x, y)\n                postInvalidate()\n                return\n            } else {\n                if (isRefreshIng) {\n                    mRefreshView?.doRefresh()\n                    refreshListener()\n                } else\n                    mRefreshView?.reset()\n                mScrollState = SCROLL_STATE_IDLE\n            }\n        }\n    }\n\n    override fun refreshComplete() {\n        if (isRefreshIng) {\n            isRefreshIng = false\n            smoothScrollTo(-scrollX, -scrollY, 1500)//这里增加了点时间,让视图不那么快的滚回去\n        }\n    }\n\n    override fun isRefreshing() = isRefreshIng\n\n    /** 弹性滑动\n     * @param x 水平滑动距离\n     * @param y 竖直方向距离\n     */\n    private fun smoothScrollTo(x: Int, y: Int, duration: Int = 0) {\n        mScrollState = SCROLL_STATE_SETTLING\n        if (duration > 0) {\n            mSmoothScroller.startScroll(scrollX, scrollY, x, y, duration)\n        } else {\n            mSmoothScroller.startScroll(scrollX, scrollY, x, y)\n        }\n        postInvalidate()\n    }\n\n    /**\n     * 获取当前滑动的差值\n     */\n    private fun getScrollDiff(): Int {\n        return abs(mSmoothScroller.finalY - mSmoothScroller.currY)\n    }\n\n    /**\n     * 分发额外的移动距离\n     */\n    open fun dispatchExtraPullEvent(dy: Float) {\n    }\n\n    /**\n     * 处理有效的移动距离\n     */\n    open fun dispatchValidPullEvent(dy: Float) {\n\n    }\n\n\n    /**\n     * 是否允许下拉刷新\n     */\n    override fun isPullToRefreshEnabled() = isRefreshEnable\n\n    /**\n     * 创建根布局\n     */\n    abstract fun createRootView(context: Context): T\n\n\n    /**\n     * 是否准备好下拉\n     */\n    abstract fun isReadyForPullStart(): Boolean\n\n\n    /**\n     * 获取当前RecyclerView\n     */\n    override fun getRootView() = mRootView\n\n    override fun onDetachedFromWindow() {\n        super.onDetachedFromWindow()\n        if (!mSmoothScroller.isFinished) {\n            mSmoothScroller.abortAnimation()\n        }\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/pull/refresh/PullToRefreshRecyclerView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.pull.refresh\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.recyclerview.widget.DefaultItemAnimator\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.chad.library.adapter.base.BaseQuickAdapter\nimport com.jennifer.andy.simpleeyes.widget.pull.head.EliteHeaderView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/6/19 14:37\n * Description:下拉刷新RecyclerView\n */\n\nclass PullToRefreshRecyclerView : PullToRefreshBase<RecyclerView> {\n\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)\n\n    override fun createRootView(context: Context): RecyclerView = RecyclerView(context)\n\n\n    /**\n     * 判断当前recyclerView是否滑动到顶部，如果是在顶部就可以进行下拉\n     */\n    override fun isReadyForPullStart(): Boolean {\n        val adapter = mRootView.adapter\n        return if (adapter == null || adapter.itemCount == 0) true else !mRootView.canScrollVertically(-1)\n\n    }\n\n\n    /**\n     * 设置适配器与布局管理器\n     */\n    fun setAdapterAndLayoutManager(adapter: BaseQuickAdapter<*, *>, layoutManager: LinearLayoutManager) {\n        mRootView.adapter = adapter\n        mRootView.layoutManager = layoutManager\n        mRootView.itemAnimator = DefaultItemAnimator()\n    }\n\n\n    /**\n     * 滚动到相应位置\n     */\n    fun smoothScrollToPosition(position: Int) {\n        mRootView.smoothScrollToPosition(position)\n    }\n\n    /**\n     * 添加滑动监听\n     */\n    fun addOnScrollListener(onScrollListener: RecyclerView.OnScrollListener) {\n        mRootView.addOnScrollListener(onScrollListener)\n    }\n\n    override fun initRefreshView() = EliteHeaderView(context)\n\n\n    override fun dispatchExtraPullEvent(dy: Float) {\n        mRefreshView?.handleExtraPullEvent(dy)\n    }\n\n    override fun dispatchValidPullEvent(dy: Float) {\n        mRefreshView?.handleValidPullEvent(dy)\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/pull/zoom/PullToZoom.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.pull.zoom\n\nimport android.content.res.TypedArray\nimport android.view.View\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/11/11 16:14\n * Description:下拉变焦 T 下拉变焦根布局\n */\n\ninterface PullToZoom<out T : View> {\n\n    /**\n     * 获取变焦的view\n     */\n    fun getZoomView(): View?\n\n    /**\n     * 获取头布局\n     */\n    fun getHeaderView(): View?\n\n    /**\n     * 获取下拉变焦根布局\n     */\n    fun getPullRootView(): T\n\n    /**\n     * 下拉变焦是否可用\n     */\n    fun isPullToZoomEnabled(): Boolean\n\n    /**\n     * 是否正在变焦\n     */\n    fun isZooming(): Boolean\n\n    /**\n     * 是否存在视差\n     */\n    fun isParallax(): Boolean\n\n    /**\n     * 是否隐藏头部\n     */\n    fun isHideHeader(): Boolean\n\n    /**\n     * 处理配置的zoomView与headerView\n     */\n    fun handleStyledAttributes(typedArray: TypedArray)\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/pull/zoom/PullToZoomBase.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.pull.zoom\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.*\nimport android.widget.LinearLayout\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.utils.getScreenHeight\nimport kotlin.math.abs\nimport kotlin.math.min\nimport kotlin.math.roundToInt\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/11/11 16:22\n * Description:下拉变焦基础类\n */\n\nabstract class PullToZoomBase<T : View> : LinearLayout, PullToZoom<T> {\n\n    private var mTouchSlop = 0\n    private var mScreenHeight = 0\n    private var mScreenWidth = 0\n\n    protected lateinit var mRootView: T\n    protected var mZoomView: View? = null\n    protected var mHeadView: View? = null\n\n    private var isParallax = true\n    private var isZoomEnable = true\n    private var isZooming = false\n    private var isHideHeader = false\n\n    private var mIsBeingDragged = false//是否被拖拽\n\n    private var mInterceptPressedX = 0f\n    private var mInterceptPressedY = 0f\n    private var mTouchPressedX = 0f\n    private var mTouchPressedY = 0f\n\n    private val DAMPING = 3f//阻尼系数\n\n    protected var mPullZoomListener: OnPullZoomListener? = null\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        init(context, attrs)\n    }\n\n    private fun init(context: Context, attrs: AttributeSet?) {\n        gravity = Gravity.CENTER\n        mTouchSlop = ViewConfiguration.get(context).scaledTouchSlop\n        mScreenHeight = context.getScreenHeight()\n        mScreenWidth = context.getScreenHeight()\n        mRootView = createRootView(context)\n\n        attrs?.let {\n            val layoutInflater = LayoutInflater.from(context)\n            val typeArray = context.obtainStyledAttributes(attrs, R.styleable.PullToZoomBase)\n            val zoomViewResId = typeArray.getResourceId(R.styleable.PullToZoomBase_zoomView, 0)\n            val headViewResId = typeArray.getResourceId(R.styleable.PullToZoomBase_headView, 0)\n            isParallax = typeArray.getBoolean(R.styleable.PullToZoomBase_isHeaderParallax, true)\n            if (zoomViewResId > 0) {\n                mZoomView = layoutInflater.inflate(zoomViewResId, null, false)\n            }\n            if (headViewResId > 0) {\n                mHeadView = layoutInflater.inflate(headViewResId, null, false)\n            }\n            handleStyledAttributes(typeArray)\n            typeArray.recycle()\n        }\n        addView(mRootView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)\n    }\n\n    /**\n     * 处理竖直拖动滑动监听,并记录当前拖动的状态\n     */\n    override fun onInterceptTouchEvent(event: MotionEvent): Boolean {\n        if (isPullToZoomEnabled() && !isHideHeader) {\n            val action = event.action\n            //如果是取消或者手指抬起不拦截\n            if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {\n                mIsBeingDragged = false\n                return false\n            }\n            //如果正在拖拽，拦截\n            if (action != MotionEvent.ACTION_DOWN && mIsBeingDragged) {\n                return true\n            }\n            when (action) {\n\n                MotionEvent.ACTION_DOWN -> {\n                    if (isReadyForPullStart()) {\n                        mInterceptPressedX = event.x\n                        mInterceptPressedY = event.y\n                        mIsBeingDragged = false\n                        mTouchPressedY = event.y\n                        mTouchPressedX = event.x\n                    }\n                }\n\n                MotionEvent.ACTION_MOVE -> {\n                    val dy: Float\n                    val dx: Float\n                    val currentX = event.x\n                    val currentY = event.y\n                    dy = currentY - mInterceptPressedY\n                    dx = currentX - mInterceptPressedX\n                    if (dy > mTouchSlop && (abs(dy) > abs(dx)) && isReadyForPullStart()) {\n                        mInterceptPressedX = currentX\n                        mInterceptPressedY = currentY\n                        mIsBeingDragged = true\n                    }\n\n                }\n            }\n        }\n\n        return mIsBeingDragged\n    }\n\n    override fun onTouchEvent(event: MotionEvent): Boolean {\n        if (isPullToZoomEnabled() && !isHideHeader()) {\n            //如果是在边缘也不拦截\n            if (event.action == MotionEvent.ACTION_DOWN && event.edgeFlags != 0) {\n                return false\n            }\n            when (event.action) {\n                MotionEvent.ACTION_MOVE -> {\n                    if (mIsBeingDragged) {\n                        mInterceptPressedY = event.y\n                        mInterceptPressedX = event.x\n                        dispatchPullEvent()\n                        isZooming = true\n                        return true\n                    }\n                }\n                MotionEvent.ACTION_DOWN -> {\n                    if (isReadyForPullStart()) {\n                        mTouchPressedX = event.x\n                        mTouchPressedY = event.y\n                        return true\n                    }\n                }\n                MotionEvent.ACTION_CANCEL,\n                MotionEvent.ACTION_UP -> {\n                    if (mIsBeingDragged) {\n                        mIsBeingDragged = false\n                        if (isZooming()) {\n                            smoothScrollToTop()\n                            isZooming = false\n                        }\n                    }\n                    return true\n\n                }\n            }\n        }\n        return false\n    }\n\n\n    /**\n     * 创建根布局\n     */\n    abstract fun createRootView(context: Context): T\n\n    /**\n     * 是否准备好下拉\n     */\n    abstract fun isReadyForPullStart(): Boolean\n\n    /**\n     * 下拉变焦头布局\n     */\n    abstract fun pullHeadToZoom(scrollValue: Int)\n\n    /**\n     * 平滑滑动到顶部\n     */\n    abstract fun smoothScrollToTop()\n\n    /**\n     * 设置头view\n     */\n    abstract fun setHeaderView(headerView: View?)\n\n    /**\n     * 设置头布局的高度\n     */\n    abstract fun setHeaderViewLayoutParams(layoutParams: LayoutParams)\n\n    /**\n     * 设置变焦view\n     */\n    abstract fun setZoomView(zoomView: View?)\n\n    /**\n     * 处理下拉事件\n     */\n    private fun dispatchPullEvent() {\n        val scrollValue = (min(mTouchPressedY - mInterceptPressedY, 0f) / DAMPING).roundToInt()\n        pullHeadToZoom(scrollValue)\n        mPullZoomListener?.onPullZooming(abs(scrollValue))\n    }\n\n\n    override fun getZoomView() = mZoomView\n\n    override fun getHeaderView() = mHeadView\n\n    override fun getPullRootView(): T = mRootView\n\n    override fun isPullToZoomEnabled() = isZoomEnable\n\n    override fun isZooming() = isZooming\n\n    override fun isParallax() = isParallax\n\n    override fun isHideHeader() = isHideHeader\n\n    fun setHideHeader(isHideHeader: Boolean) {\n        this.isHideHeader = isHideHeader\n    }\n\n    fun setIsParallax(isParallax: Boolean) {\n        this.isParallax = isParallax\n    }\n\n    fun setIsZoomEnable(isZoomEnable: Boolean) {\n        this.isZoomEnable = isZoomEnable\n    }\n\n\n    fun setOnPullZoomListener(OnPullZoomListener: OnPullZoomListener) {\n        mPullZoomListener = OnPullZoomListener\n    }\n\n    /**\n     * 下拉变焦监听\n     */\n    interface OnPullZoomListener {\n        /**\n         * 正在变焦\n         * @param scrollValue 滑动距离\n         */\n        fun onPullZooming(scrollValue: Int)\n\n        /**\n         * 变焦结束\n         */\n        fun onPullZoomEnd()\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/pull/zoom/PullToZoomRecyclerView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.pull.zoom\n\nimport android.animation.ValueAnimator\nimport android.content.Context\nimport android.content.res.TypedArray\nimport android.util.AttributeSet\nimport android.view.View\nimport android.view.animation.AccelerateInterpolator\nimport android.widget.FrameLayout\nimport android.widget.LinearLayout\nimport androidx.recyclerview.widget.DefaultItemAnimator\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.chad.library.adapter.base.BaseQuickAdapter\nimport kotlin.math.abs\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/11/24 14:00\n * Description:下拉放大recyclerView,只支持竖直拖动\n */\n\nclass PullToZoomRecyclerView : PullToZoomBase<RecyclerView> {\n\n\n    private lateinit var mHeaderContainer: FrameLayout\n    private var mHeaderHeight: Int = 0\n    private var mValueAnimator: ValueAnimator? = null\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)\n\n    /**\n     * 添加头布局在recyclerView中，头布局包括 zoomView与headView\n     */\n    override fun handleStyledAttributes(typedArray: TypedArray) {\n        mHeaderContainer = FrameLayout(context)\n        mZoomView?.let {\n            mHeaderContainer.addView(mZoomView)\n        }\n        mHeadView?.let {\n            mHeaderContainer.addView(mHeadView)\n        }\n        mRootView.adapter?.let {\n            val adapter = mRootView.adapter as BaseQuickAdapter<*, *>\n            adapter.addHeaderView(mHeaderContainer)\n        }\n\n    }\n\n    override fun createRootView(context: Context): RecyclerView = RecyclerView(context)\n\n    /**\n     * 判断当前recyclerView是否滑动到顶部，如果是在顶部就可以进行下拉\n     */\n    override fun isReadyForPullStart(): Boolean {\n        val adapter = mRootView.adapter\n        return if (adapter == null || adapter.itemCount == 0) true else !mRootView.canScrollVertically(-1)\n    }\n\n    /**\n     * 改变头部布局高度\n     */\n    override fun pullHeadToZoom(scrollValue: Int) {\n        mValueAnimator?.let {\n            if (mValueAnimator!!.isStarted) {\n                mValueAnimator?.cancel()\n            }\n        }\n        val lp = mHeaderContainer.layoutParams\n        lp.height = abs(scrollValue) + mHeaderHeight\n        mHeaderContainer.layoutParams = lp\n    }\n\n    /**\n     * 滑动到顶部\n     */\n    override fun smoothScrollToTop() {\n        mValueAnimator = ValueAnimator.ofInt(mHeaderContainer.bottom, mHeaderHeight)\n        mValueAnimator?.duration = 350\n        mValueAnimator?.interpolator = AccelerateInterpolator()\n        mValueAnimator?.addUpdateListener {\n            val lp = mHeaderContainer.layoutParams\n            lp.height = it.animatedValue as Int\n            mHeaderContainer.layoutParams = lp\n            if (lp.height == mHeaderHeight) {\n                mPullZoomListener?.onPullZoomEnd()\n            }\n        }\n        mValueAnimator?.start()\n\n    }\n\n    /**\n     * 设置头布局\n     */\n    override fun setHeaderView(headerView: View?) {\n        headerView?.let {\n            mHeadView = headerView\n            updateZoomAndHeaderView()\n        }\n    }\n\n\n    /**\n     * 设置头布局的高度，与宽度，该方法必须要调用\n     */\n    override fun setHeaderViewLayoutParams(layoutParams: LinearLayout.LayoutParams) {\n        mHeaderContainer.let {\n            mHeaderContainer.layoutParams = layoutParams\n            mHeaderHeight = layoutParams.height\n        }\n    }\n\n    /**\n     * 设置放大布局\n     */\n    override fun setZoomView(zoomView: View?) {\n        zoomView?.let {\n            mZoomView = zoomView\n            updateZoomAndHeaderView()\n        }\n    }\n\n    /**\n     * 更新放大布局与头布局\n     */\n    private fun updateZoomAndHeaderView() {\n        mHeaderContainer?.let {\n            mZoomView?.let {\n                mHeaderContainer.addView(mZoomView)\n            }\n            mHeadView?.let {\n                mHeaderContainer.addView(mHeadView)\n            }\n        }\n    }\n\n    /**\n     * 设置适配器与布局管理器\n     */\n    fun setAdapterAndLayoutManager(adapter: BaseQuickAdapter<*, *>, layoutManager: LinearLayoutManager) {\n        mRootView.adapter = adapter\n        mRootView.layoutManager = layoutManager\n        mRootView.itemAnimator = DefaultItemAnimator()\n        updateView()\n    }\n\n    /**\n     * 更新RecyclerView中的布局，并获取头布局的高度\n     */\n    private fun updateView() {\n        mHeaderContainer?.let {\n            val adapter = mRootView.adapter as BaseQuickAdapter<*, *>\n            adapter.removeHeaderView(mHeaderContainer)\n            mHeaderContainer.removeAllViews()\n            updateZoomAndHeaderView()\n            //获取头布局的高度\n            mHeaderHeight = mHeaderContainer.layoutParams.height\n            adapter.addHeaderView(mHeaderContainer)\n        }\n    }\n\n    fun scrollToTop() {\n        mRootView.smoothScrollToPosition(0)\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/state/MultipleStateView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.state\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.View.OnClickListener\nimport android.view.ViewGroup\nimport android.widget.ImageView\nimport android.widget.RelativeLayout\nimport android.widget.TextView\nimport com.jennifer.andy.simpleeyes.R\n\n\n/**\n * Author:  andy.xwt\n * Date:    2017/8/31 22:40\n * Description:多状态布局。包含加载界面，错误界面，网络异常界面，内容界面，空界面\n */\n\nclass MultipleStateView : RelativeLayout {\n\n\n    private var mEmptyView: View? = null\n    private var mNetErrorView: View? = null\n    private var mLoadingView: View? = null\n    private var mContentViews: MutableList<View> = mutableListOf()\n\n    enum class State {\n        EMPTY, NET_ERROR, LOADING, CONTENT\n    }\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)\n\n    override fun addView(child: View?, params: ViewGroup.LayoutParams?) {\n        super.addView(child, params)\n        child?.let {\n            if (child.tag != State.LOADING && child.tag != State.EMPTY && child.tag != State.NET_ERROR) {\n                mContentViews.add(child)\n            }\n        }\n    }\n\n    fun showLoading() {\n        switchContent(State.LOADING)\n    }\n\n\n    fun showEmpty(onClickListener: OnClickListener) {\n        switchContent(State.EMPTY, onClickListener)\n    }\n\n    fun showNetError(onClickListener: OnClickListener) {\n        switchContent(State.NET_ERROR, onClickListener)\n    }\n\n    fun showContent() {\n        switchContent(State.CONTENT)\n    }\n\n    /**\n     * 设置空数据view\n     */\n    private fun setEmptyView(onClickListener: OnClickListener) {\n        if (mEmptyView == null) {\n            mEmptyView = LayoutInflater.from(context).inflate(R.layout.layout_loading_message, null)\n            mEmptyView?.tag = State.EMPTY\n            val imageView = mEmptyView?.findViewById<ImageView>(R.id.iv_image)\n            val errorText = mEmptyView?.findViewById<TextView>(R.id.tv_message_info)\n            imageView?.setImageResource(R.drawable.ic_eye_black_error)\n            errorText?.setText(R.string.empty_message)\n            mEmptyView?.setOnClickListener(onClickListener)\n            addStateView(mEmptyView)\n        } else {\n            mEmptyView?.visibility = View.VISIBLE\n        }\n    }\n\n    /**\n     * 显示空数据View\n     */\n    private fun showEmptyView(onClickListener: OnClickListener) {\n        setEmptyView(onClickListener)\n        hideLoadingView()\n        hideNetErrorView()\n        setContentViewVisible(false)\n    }\n\n\n    /**\n     * 隐藏空数据view\n     */\n    private fun hideEmptyView() {\n        mEmptyView?.let { it.visibility = View.GONE }\n    }\n\n\n    /**\n     * 设置网络异常view\n     */\n    private fun setNetErrorView(onClickListener: OnClickListener) {\n        if (mNetErrorView == null) {\n            mNetErrorView = LayoutInflater.from(context).inflate(R.layout.layout_loading_message, null)\n            mNetErrorView?.tag = State.NET_ERROR\n            val imageView = mNetErrorView?.findViewById<ImageView>(R.id.iv_image)\n            val errorText = mNetErrorView?.findViewById<TextView>(R.id.tv_message_info)\n            imageView?.setImageResource(R.drawable.ic_eye_black_error)\n            errorText?.setText(R.string.net_error_message)\n            mNetErrorView?.setOnClickListener(onClickListener)\n            addStateView(mNetErrorView)\n        } else {\n            mNetErrorView?.visibility = View.VISIBLE\n        }\n    }\n\n    /**\n     * 显示网络异常view\n     */\n    private fun showNetErrorView(onClickListener: OnClickListener) {\n        setNetErrorView(onClickListener)\n        hideLoadingView()\n        hideEmptyView()\n        setContentViewVisible(false)\n    }\n\n    /**\n     * 隐藏网络异常view\n     */\n    private fun hideNetErrorView() {\n        mNetErrorView?.let { it.visibility = View.GONE }\n    }\n\n    /**\n     * 设置加载中view\n     */\n    private fun setLoadingView(onClickListener: OnClickListener) {\n        if (mLoadingView == null) {\n            mLoadingView = NetLoadingView(context)\n            mLoadingView?.tag = State.LOADING\n            mLoadingView?.setOnClickListener(onClickListener)\n            addStateView(mLoadingView)\n        } else {\n            mLoadingView?.visibility = View.VISIBLE\n        }\n    }\n\n    /**\n     * 显示加载view\n     */\n    private fun showLoadingView(onClickListener: OnClickListener) {\n        setLoadingView(onClickListener)\n        hideEmptyView()\n        hideNetErrorView()\n        setContentViewVisible(false)\n    }\n\n    /**\n     * 隐藏加载view\n     */\n    private fun hideLoadingView() {\n        mLoadingView?.let { it.visibility = View.GONE }\n    }\n\n\n    /**\n     * 设置内容界面是否显示\n     */\n    private fun setContentViewVisible(isVisible: Boolean) {\n        for (mContentView in mContentViews) {\n            mContentView.visibility = if (isVisible) View.VISIBLE else View.GONE\n        }\n\n    }\n\n    /**\n     * 显示内容视图\n     */\n    private fun showContentView() {\n        hideEmptyView()\n        hideNetErrorView()\n        hideLoadingView()\n        setContentViewVisible(true)\n    }\n\n    /**\n     * 添加状态布局\n     */\n    private fun addStateView(view: View?) {\n        val layoutParams = LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)\n        layoutParams.addRule(CENTER_IN_PARENT)\n        addView(view, layoutParams)\n    }\n\n    /**\n     * 切换布局\n     */\n    private fun switchContent(state: State, onClickListener: OnClickListener = OnClickListener { }) {\n        when (state) {\n            State.EMPTY -> showEmptyView(onClickListener)\n            State.LOADING -> showLoadingView(onClickListener)\n            State.NET_ERROR -> showNetErrorView(onClickListener)\n            State.CONTENT -> showContentView()\n        }\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/state/NetLoadingView.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.state\n\nimport android.animation.ObjectAnimator\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.widget.FrameLayout\nimport android.widget.ImageView\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.utils.bindView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/2/11 17:44\n * Description:网络加载view\n */\n\nclass NetLoadingView : FrameLayout {\n\n    private val mHeadInner by bindView<ImageView>(R.id.iv_head_inner)\n    private lateinit var mRotationAnimator: ObjectAnimator\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        LayoutInflater.from(getContext()).inflate(R.layout.layout_loading_view, this)\n        doInnerEyeAnimator()\n    }\n\n\n    /**\n     * 执行内部眼睛动画\n     */\n    private fun doInnerEyeAnimator() {\n        mRotationAnimator = ObjectAnimator.ofFloat(mHeadInner, \"rotation\", 0f, 360f)\n        mRotationAnimator.duration = 1000\n        mRotationAnimator.repeatCount = -1\n        mRotationAnimator.start()\n    }\n\n\n    override fun onDetachedFromWindow() {\n        super.onDetachedFromWindow()\n        mRotationAnimator.cancel()\n\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/tab/AnimationUtils.java",
    "content": "package com.jennifer.andy.simpleeyes.widget.tab;\n\nimport android.view.animation.DecelerateInterpolator;\nimport android.view.animation.Interpolator;\nimport android.view.animation.LinearInterpolator;\n\nimport androidx.interpolator.view.animation.FastOutLinearInInterpolator;\nimport androidx.interpolator.view.animation.FastOutSlowInInterpolator;\nimport androidx.interpolator.view.animation.LinearOutSlowInInterpolator;\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/4 13:47\n * Description:\n */\n\npublic class AnimationUtils {\n\n    static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();\n    static final Interpolator FAST_OUT_SLOW_IN_INTERPOLATOR = new FastOutSlowInInterpolator();\n    static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR = new FastOutLinearInInterpolator();\n    static final Interpolator LINEAR_OUT_SLOW_IN_INTERPOLATOR = new LinearOutSlowInInterpolator();\n    static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();\n\n    /**\n     * Linear interpolation between {@code startValue} and {@code endValue} by {@code fraction}.\n     */\n    static float lerp(float startValue, float endValue, float fraction) {\n        return startValue + (fraction * (endValue - startValue));\n    }\n\n    static int lerp(int startValue, int endValue, float fraction) {\n        return startValue + Math.round(fraction * (endValue - startValue));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/tab/ShortTabLayout.java",
    "content": "package com.jennifer.andy.simpleeyes.widget.tab;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.database.DataSetObserver;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.drawable.Drawable;\nimport android.os.Build;\nimport android.text.Layout;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.SoundEffectConstants;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.view.accessibility.AccessibilityEvent;\nimport android.view.accessibility.AccessibilityNodeInfo;\nimport android.widget.HorizontalScrollView;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.google.android.material.tabs.TabLayout;\nimport com.jennifer.andy.simpleeyes.R;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\nimport java.util.Iterator;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.IntDef;\nimport androidx.annotation.LayoutRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.RestrictTo;\nimport androidx.annotation.StringRes;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.appcompat.content.res.AppCompatResources;\nimport androidx.appcompat.widget.TooltipCompat;\nimport androidx.core.util.Pools;\nimport androidx.core.view.GravityCompat;\nimport androidx.core.view.PointerIconCompat;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.widget.TextViewCompat;\nimport androidx.viewpager.widget.PagerAdapter;\nimport androidx.viewpager.widget.ViewPager;\n\nimport static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;\nimport static androidx.viewpager.widget.ViewPager.SCROLL_STATE_DRAGGING;\nimport static androidx.viewpager.widget.ViewPager.SCROLL_STATE_IDLE;\nimport static androidx.viewpager.widget.ViewPager.SCROLL_STATE_SETTLING;\n\n\n/**\n * TabLayout provides a horizontal layout to display tabs.\n * <p>\n * <p>Population of the tabs to display is\n * done through {@link  Tab} instances. You create tabs via {@link #newTab()}. From there you can\n * change the tab's label or icon via {@link  Tab#setText(int)} and {@link  Tab#setIcon(int)}\n * respectively. To display the tab, you need to add it to the layout via one of the\n * {@link #addTab(Tab)} methods. For example:\n * <pre>\n * TabLayout tabLayout = ...;\n * tabLayout.addTab(tabLayout.newTab().setText(\"Tab 1\"));\n * tabLayout.addTab(tabLayout.newTab().setText(\"Tab 2\"));\n * tabLayout.addTab(tabLayout.newTab().setText(\"Tab 3\"));\n * </pre>\n * You should set a listener via {@link #setOnTabSelectedListener(OnTabSelectedListener)} to be\n * notified when any tab's selection state has been changed.\n * <p>\n * <p>You can also add items to TabLayout in your layout through the use of {@link TabItem}.\n * An example usage is like so:</p>\n * <p>\n * <pre>\n * &lt;TabLayout\n *         android:layout_height=&quot;wrap_content&quot;\n *         android:layout_width=&quot;match_parent&quot;&gt;\n *\n *     &lt;android.support.design.widget.TabItem\n *             android:text=&quot;@string/tab_text&quot;/&gt;\n *\n *     &lt;android.support.design.widget.TabItem\n *             android:icon=&quot;@drawable/ic_android&quot;/&gt;\n *\n * &lt;/TabLayout&gt;\n * </pre>\n * <p>\n * <h3>ViewPager integration</h3>\n * <p>\n * If you're using a {@link ViewPager} together\n * with this layout, you can call {@link #setupWithViewPager(ViewPager)} to link the two together.\n * This layout will be automatically populated from the {@link PagerAdapter}'s page titles.</p>\n * <p>\n * <p>\n * This view also supports being used as part of a ViewPager's decor, and can be added\n * directly to the ViewPager in a layout resource file like so:</p>\n * <p>\n * <pre>\n * &lt;android.support.v4.view.ViewPager\n *     android:layout_width=&quot;match_parent&quot;\n *     android:layout_height=&quot;match_parent&quot;&gt;\n *\n *     &lt;TabLayout\n *         android:layout_width=&quot;match_parent&quot;\n *         android:layout_height=&quot;wrap_content&quot;\n *         android:layout_gravity=&quot;top&quot; /&gt;\n *\n * &lt;/android.support.v4.view.ViewPager&gt;\n * </pre>\n *\n * @attr ref android.support.design.R.styleable#TabLayout_tabPadding\n * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingStart\n * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingTop\n * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingEnd\n * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingBottom\n * @attr ref android.support.design.R.styleable#TabLayout_tabContentStart\n * @attr ref android.support.design.R.styleable#TabLayout_tabBackground\n * @attr ref android.support.design.R.styleable#TabLayout_tabMinWidth\n * @attr ref android.support.design.R.styleable#TabLayout_tabMaxWidth\n * @attr ref android.support.design.R.styleable#TabLayout_tabTextAppearance\n * @see <a href=\"http://www.google.com/design/spec/components/tabs.html\">Tabs</a>\n */\n@ViewPager.DecorView\npublic class ShortTabLayout extends HorizontalScrollView {\n\n    private static final int DEFAULT_HEIGHT_WITH_TEXT_ICON = 72; // dps\n    static final int DEFAULT_GAP_TEXT_ICON = 8; // dps\n    private static final int INVALID_WIDTH = -1;\n    private static final int DEFAULT_HEIGHT = 48; // dps\n    private static final int TAB_MIN_WIDTH_MARGIN = 56; //dps\n    static final int FIXED_WRAP_GUTTER_MIN = 16; //dps\n    static final int MOTION_NON_ADJACENT_OFFSET = 24;\n\n    private static final int ANIMATION_DURATION = 300;\n\n    private static final Pools.Pool<Tab> sTabPool = new Pools.SynchronizedPool<>(16);\n\n    /**\n     * Scrollable tabs display a subset of tabs at any given moment, and can contain longer tab\n     * labels and a larger number of tabs. They are best used for browsing contexts in touch\n     * interfaces when users don’t need to directly compare the tab labels.\n     *\n     * @see #setTabMode(int)\n     * @see #getTabMode()\n     */\n    public static final int MODE_SCROLLABLE = 0;\n\n    /**\n     * Fixed tabs display all tabs concurrently and are best used with content that benefits from\n     * quick pivots between tabs. The maximum number of tabs is limited by the view’s width.\n     * Fixed tabs have equal width, based on the widest tab label.\n     *\n     * @see #setTabMode(int)\n     * @see #getTabMode()\n     */\n    public static final int MODE_FIXED = 1;\n\n    /**\n     * @hide\n     */\n    @RestrictTo(LIBRARY_GROUP)\n    @IntDef(value = {MODE_SCROLLABLE, MODE_FIXED})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Mode {\n    }\n\n    /**\n     * Gravity used to fill the {@link TabLayout} as much as possible. This option only takes effect\n     * when used with {@link #MODE_FIXED}.\n     *\n     * @see #setTabGravity(int)\n     * @see #getTabGravity()\n     */\n    public static final int GRAVITY_FILL = 0;\n\n    /**\n     * Gravity used to lay out the tabs in the center of the {@link TabLayout}.\n     *\n     * @see #setTabGravity(int)\n     * @see #getTabGravity()\n     */\n    public static final int GRAVITY_CENTER = 1;\n\n    /**\n     * @hide\n     */\n    @RestrictTo(LIBRARY_GROUP)\n    @IntDef(flag = true, value = {GRAVITY_FILL, GRAVITY_CENTER})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface TabGravity {\n    }\n\n    /**\n     * Callback interface invoked when a tab's selection state changes.\n     */\n    public interface OnTabSelectedListener {\n\n        /**\n         * Called when a tab enters the selected state.\n         *\n         * @param tab The tab that was selected\n         */\n        public void onTabSelected(Tab tab);\n\n        /**\n         * Called when a tab exits the selected state.\n         *\n         * @param tab The tab that was unselected\n         */\n        public void onTabUnselected(Tab tab);\n\n        /**\n         * Called when a tab that is already selected is chosen again by the user. Some applications\n         * may use this action to return to the top level of a category.\n         *\n         * @param tab The tab that was reselected.\n         */\n        public void onTabReselected(Tab tab);\n    }\n\n    private final ArrayList<Tab> mTabs = new ArrayList<>();\n    private Tab mSelectedTab;\n\n    private final SlidingTabStrip mTabStrip;\n\n    int mTabPaddingStart;\n    int mTabPaddingTop;\n    int mTabPaddingEnd;\n    int mTabPaddingBottom;\n\n    int mTabTextAppearance;\n    ColorStateList mTabTextColors;\n    float mTabTextSize;\n    float mTabTextMultiLineSize;\n\n    final int mTabBackgroundResId;\n\n    int mTabMaxWidth = Integer.MAX_VALUE;\n    private final int mRequestedTabMinWidth;\n    private final int mRequestedTabMaxWidth;\n    private final int mScrollableTabMinWidth;\n\n    private int mContentInsetStart;\n\n    int mTabGravity;\n    int mMode;\n\n    private OnTabSelectedListener mSelectedListener;\n    private final ArrayList<OnTabSelectedListener> mSelectedListeners = new ArrayList<>();\n    private OnTabSelectedListener mCurrentVpSelectedListener;\n\n    private ValueAnimator mScrollAnimator;\n\n    ViewPager mViewPager;\n    private PagerAdapter mPagerAdapter;\n    private DataSetObserver mPagerAdapterObserver;\n    private TabLayoutOnPageChangeListener mPageChangeListener;\n    private AdapterChangeListener mAdapterChangeListener;\n    private boolean mSetupViewPagerImplicitly;\n\n    // Pool we use as a simple RecyclerBin\n    private final Pools.Pool<TabView> mTabViewPool = new Pools.SimplePool<>(12);\n\n    public ShortTabLayout(Context context) {\n        this(context, null);\n    }\n\n    public ShortTabLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public ShortTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n\n        checkAppCompatTheme(context);\n\n        // Disable the Scroll Bar\n        setHorizontalScrollBarEnabled(false);\n\n        // Add the TabStrip\n        mTabStrip = new SlidingTabStrip(context);\n        super.addView(mTabStrip, 0, new HorizontalScrollView.LayoutParams(\n                LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));\n\n        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabLayout,\n                defStyleAttr, R.style.Widget_Design_TabLayout);\n\n        mTabStrip.setSelectedIndicatorHeight(\n                a.getDimensionPixelSize(R.styleable.TabLayout_tabIndicatorHeight, 0));\n        mTabStrip.setSelectedIndicatorColor(a.getColor(R.styleable.TabLayout_tabIndicatorColor, 0));\n\n        mTabPaddingStart = mTabPaddingTop = mTabPaddingEnd = mTabPaddingBottom = a\n                .getDimensionPixelSize(R.styleable.TabLayout_tabPadding, 0);\n        mTabPaddingStart = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingStart,\n                mTabPaddingStart);\n        mTabPaddingTop = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingTop,\n                mTabPaddingTop);\n        mTabPaddingEnd = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingEnd,\n                mTabPaddingEnd);\n        mTabPaddingBottom = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingBottom,\n                mTabPaddingBottom);\n\n        mTabTextAppearance = a.getResourceId(R.styleable.TabLayout_tabTextAppearance,\n                R.style.TextAppearance_Design_Tab);\n\n        // Text colors/sizes come from the text appearance first\n        final TypedArray ta = context.obtainStyledAttributes(mTabTextAppearance,\n                R.styleable.TextAppearance);\n        try {\n            mTabTextSize = ta.getDimensionPixelSize(\n                    R.styleable.TextAppearance_android_textSize, 0);\n            mTabTextColors = ta.getColorStateList(\n                    R.styleable.TextAppearance_android_textColor);\n        } finally {\n            ta.recycle();\n        }\n\n        if (a.hasValue(R.styleable.TabLayout_tabTextColor)) {\n            // If we have an explicit text color set, use it instead\n            mTabTextColors = a.getColorStateList(R.styleable.TabLayout_tabTextColor);\n        }\n\n        if (a.hasValue(R.styleable.TabLayout_tabSelectedTextColor)) {\n            // We have an explicit selected text color set, so we need to make merge it with the\n            // current colors. This is exposed so that developers can use theme attributes to set\n            // this (theme attrs in ColorStateLists are Lollipop+)\n            final int selected = a.getColor(R.styleable.TabLayout_tabSelectedTextColor, 0);\n            mTabTextColors = createColorStateList(mTabTextColors.getDefaultColor(), selected);\n        }\n\n        mRequestedTabMinWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMinWidth,\n                INVALID_WIDTH);\n        mRequestedTabMaxWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMaxWidth,\n                INVALID_WIDTH);\n        mTabBackgroundResId = a.getResourceId(R.styleable.TabLayout_tabBackground, 0);\n        mContentInsetStart = a.getDimensionPixelSize(R.styleable.TabLayout_tabContentStart, 0);\n        mMode = a.getInt(R.styleable.TabLayout_tabMode, MODE_FIXED);\n        mTabGravity = a.getInt(R.styleable.TabLayout_tabGravity, GRAVITY_FILL);\n        a.recycle();\n\n        // TODO add attr for these\n        final Resources res = getResources();\n        mTabTextMultiLineSize = res.getDimensionPixelSize(R.dimen.design_tab_text_size_2line);\n        mScrollableTabMinWidth = res.getDimensionPixelSize(R.dimen.design_tab_scrollable_min_width);\n\n        // Now apply the tab mode and gravity\n        applyModeAndGravity();\n    }\n\n    private static final int[] APPCOMPAT_CHECK_ATTRS = {\n            R.attr.colorPrimary\n    };\n\n    private void checkAppCompatTheme(Context context) {\n        TypedArray a = context.obtainStyledAttributes(APPCOMPAT_CHECK_ATTRS);\n        final boolean failed = !a.hasValue(0);\n        a.recycle();\n        if (failed) {\n            throw new IllegalArgumentException(\"You need to use a Theme.AppCompat theme \"\n                    + \"(or descendant) with the design library.\");\n        }\n    }\n\n    /**\n     * Sets the tab indicator's color for the currently selected tab.\n     *\n     * @param color color to use for the indicator\n     * @attr ref android.support.design.R.styleable#TabLayout_tabIndicatorColor\n     */\n    public void setSelectedTabIndicatorColor(@ColorInt int color) {\n        mTabStrip.setSelectedIndicatorColor(color);\n    }\n\n    /**\n     * Sets the tab indicator's height for the currently selected tab.\n     *\n     * @param height height to use for the indicator in pixels\n     * @attr ref android.support.design.R.styleable#TabLayout_tabIndicatorHeight\n     */\n    public void setSelectedTabIndicatorHeight(int height) {\n        mTabStrip.setSelectedIndicatorHeight(height);\n    }\n\n    /**\n     * Set the scroll position of the tabs. This is useful for when the tabs are being displayed as\n     * part of a scrolling container such as {@link ViewPager}.\n     * <p>\n     * Calling this method does not update the selected tab, it is only used for drawing purposes.\n     *\n     * @param position           current scroll position\n     * @param positionOffset     Value from [0, 1) indicating the offset from {@code position}.\n     * @param updateSelectedText Whether to update the text's selected state.\n     */\n    public void setScrollPosition(int position, float positionOffset, boolean updateSelectedText) {\n        setScrollPosition(position, positionOffset, updateSelectedText, true);\n    }\n\n    void setScrollPosition(int position, float positionOffset, boolean updateSelectedText,\n                           boolean updateIndicatorPosition) {\n        final int roundedPosition = Math.round(position + positionOffset);\n        if (roundedPosition < 0 || roundedPosition >= mTabStrip.getChildCount()) {\n            return;\n        }\n\n        // Set the indicator position, if enabled\n        if (updateIndicatorPosition) {\n            mTabStrip.setIndicatorPositionFromTabPosition(position, positionOffset);\n        }\n\n        // Now update the scroll position, canceling any running animation\n        if (mScrollAnimator != null && mScrollAnimator.isRunning()) {\n            mScrollAnimator.cancel();\n        }\n        scrollTo(calculateScrollXForTab(position, positionOffset), 0);\n\n        // Update the 'selected state' view as we scroll, if enabled\n        if (updateSelectedText) {\n            setSelectedTabView(roundedPosition);\n        }\n    }\n\n    private float getScrollPosition() {\n        return mTabStrip.getIndicatorPosition();\n    }\n\n    /**\n     * Add a tab to this layout. The tab will be added at the end of the list.\n     * If this is the first tab to be added it will become the selected tab.\n     *\n     * @param tab Tab to add\n     */\n    public void addTab(@NonNull Tab tab) {\n        addTab(tab, mTabs.isEmpty());\n    }\n\n    /**\n     * Add a tab to this layout. The tab will be inserted at <code>position</code>.\n     * If this is the first tab to be added it will become the selected tab.\n     *\n     * @param tab      The tab to add\n     * @param position The new position of the tab\n     */\n    public void addTab(@NonNull Tab tab, int position) {\n        addTab(tab, position, mTabs.isEmpty());\n    }\n\n    /**\n     * Add a tab to this layout. The tab will be added at the end of the list.\n     *\n     * @param tab         Tab to add\n     * @param setSelected True if the added tab should become the selected tab.\n     */\n    public void addTab(@NonNull Tab tab, boolean setSelected) {\n        addTab(tab, mTabs.size(), setSelected);\n    }\n\n    /**\n     * Add a tab to this layout. The tab will be inserted at <code>position</code>.\n     *\n     * @param tab         The tab to add\n     * @param position    The new position of the tab\n     * @param setSelected True if the added tab should become the selected tab.\n     */\n    public void addTab(@NonNull Tab tab, int position, boolean setSelected) {\n        if (tab.mParent != this) {\n            throw new IllegalArgumentException(\"Tab belongs to a different TabLayout.\");\n        }\n        configureTab(tab, position);\n        addTabView(tab);\n\n        if (setSelected) {\n            tab.select();\n        }\n    }\n\n    private void addTabFromItemView(@NonNull TabItem item) {\n        final Tab tab = newTab();\n        if (item.mText != null) {\n            tab.setText(item.mText);\n        }\n        if (item.mIcon != null) {\n            tab.setIcon(item.mIcon);\n        }\n        if (item.mCustomLayout != 0) {\n            tab.setCustomView(item.mCustomLayout);\n        }\n        if (!TextUtils.isEmpty(item.getContentDescription())) {\n            tab.setContentDescription(item.getContentDescription());\n        }\n        addTab(tab);\n    }\n\n    /**\n     * @deprecated Use {@link #addOnTabSelectedListener(OnTabSelectedListener)} and\n     * {@link #removeOnTabSelectedListener(OnTabSelectedListener)}.\n     */\n    @Deprecated\n    public void setOnTabSelectedListener(@Nullable OnTabSelectedListener listener) {\n        // The logic in this method emulates what we had before support for multiple\n        // registered listeners.\n        if (mSelectedListener != null) {\n            removeOnTabSelectedListener(mSelectedListener);\n        }\n        // Update the deprecated field so that we can remove the passed listener the next\n        // time we're called\n        mSelectedListener = listener;\n        if (listener != null) {\n            addOnTabSelectedListener(listener);\n        }\n    }\n\n    /**\n     * Add a {@link  OnTabSelectedListener} that will be invoked when tab selection\n     * changes.\n     * <p>\n     * <p>Components that add a listener should take care to remove it when finished via\n     * {@link #removeOnTabSelectedListener(OnTabSelectedListener)}.</p>\n     *\n     * @param listener listener to add\n     */\n    public void addOnTabSelectedListener(@NonNull OnTabSelectedListener listener) {\n        if (!mSelectedListeners.contains(listener)) {\n            mSelectedListeners.add(listener);\n        }\n    }\n\n    /**\n     * Remove the given {@link  OnTabSelectedListener} that was previously added via\n     * {@link #addOnTabSelectedListener(OnTabSelectedListener)}.\n     *\n     * @param listener listener to remove\n     */\n    public void removeOnTabSelectedListener(@NonNull OnTabSelectedListener listener) {\n        mSelectedListeners.remove(listener);\n    }\n\n    /**\n     * Remove all previously added {@link  OnTabSelectedListener}s.\n     */\n    public void clearOnTabSelectedListeners() {\n        mSelectedListeners.clear();\n    }\n\n    /**\n     * Create and return a new {@link  Tab}. You need to manually add this using\n     * {@link #addTab(Tab)} or a related method.\n     *\n     * @return Demo new Tab\n     * @see #addTab(Tab)\n     */\n    @NonNull\n    public Tab newTab() {\n        Tab tab = sTabPool.acquire();\n        if (tab == null) {\n            tab = new Tab();\n        }\n        tab.mParent = this;\n        tab.mView = createTabView(tab);\n        return tab;\n    }\n\n    /**\n     * Returns the number of tabs currently registered with the action bar.\n     *\n     * @return Tab count\n     */\n    public int getTabCount() {\n        return mTabs.size();\n    }\n\n    /**\n     * Returns the tab at the specified index.\n     */\n    @Nullable\n    public Tab getTabAt(int index) {\n        return (index < 0 || index >= getTabCount()) ? null : mTabs.get(index);\n    }\n\n    /**\n     * Returns the position of the current selected tab.\n     *\n     * @return selected tab position, or {@code -1} if there isn't a selected tab.\n     */\n    public int getSelectedTabPosition() {\n        return mSelectedTab != null ? mSelectedTab.getPosition() : -1;\n    }\n\n    /**\n     * Remove a tab from the layout. If the removed tab was selected it will be deselected\n     * and another tab will be selected if present.\n     *\n     * @param tab The tab to remove\n     */\n    public void removeTab(Tab tab) {\n        if (tab.mParent != this) {\n            throw new IllegalArgumentException(\"Tab does not belong to this TabLayout.\");\n        }\n\n        removeTabAt(tab.getPosition());\n    }\n\n    /**\n     * Remove a tab from the layout. If the removed tab was selected it will be deselected\n     * and another tab will be selected if present.\n     *\n     * @param position Position of the tab to remove\n     */\n    public void removeTabAt(int position) {\n        final int selectedTabPosition = mSelectedTab != null ? mSelectedTab.getPosition() : 0;\n        removeTabViewAt(position);\n\n        final Tab removedTab = mTabs.remove(position);\n        if (removedTab != null) {\n            removedTab.reset();\n            sTabPool.release(removedTab);\n        }\n\n        final int newTabCount = mTabs.size();\n        for (int i = position; i < newTabCount; i++) {\n            mTabs.get(i).setPosition(i);\n        }\n\n        if (selectedTabPosition == position) {\n            selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));\n        }\n    }\n\n    /**\n     * Remove all tabs from the action bar and deselect the current tab.\n     */\n    public void removeAllTabs() {\n        // Remove all the views\n        for (int i = mTabStrip.getChildCount() - 1; i >= 0; i--) {\n            removeTabViewAt(i);\n        }\n\n        for (final Iterator<Tab> i = mTabs.iterator(); i.hasNext(); ) {\n            final Tab tab = i.next();\n            i.remove();\n            tab.reset();\n            sTabPool.release(tab);\n        }\n\n        mSelectedTab = null;\n    }\n\n    /**\n     * Set the behavior mode for the Tabs in this layout. The valid input options are:\n     * <ul>\n     * <li>{@link #MODE_FIXED}: Fixed tabs display all tabs concurrently and are best used\n     * with content that benefits from quick pivots between tabs.</li>\n     * <li>{@link #MODE_SCROLLABLE}: Scrollable tabs display a subset of tabs at any given moment,\n     * and can contain longer tab labels and a larger number of tabs. They are best used for\n     * browsing contexts in touch interfaces when users don’t need to directly compare the tab\n     * labels. This mode is commonly used with a {@link ViewPager}.</li>\n     * </ul>\n     *\n     * @param mode one of {@link #MODE_FIXED} or {@link #MODE_SCROLLABLE}.\n     * @attr ref android.support.design.R.styleable#TabLayout_tabMode\n     */\n    public void setTabMode(@Mode int mode) {\n        if (mode != mMode) {\n            mMode = mode;\n            applyModeAndGravity();\n        }\n    }\n\n    /**\n     * Returns the current mode used by this {@link TabLayout}.\n     *\n     * @see #setTabMode(int)\n     */\n    @Mode\n    public int getTabMode() {\n        return mMode;\n    }\n\n    /**\n     * Set the gravity to use when laying out the tabs.\n     *\n     * @param gravity one of {@link #GRAVITY_CENTER} or {@link #GRAVITY_FILL}.\n     * @attr ref android.support.design.R.styleable#TabLayout_tabGravity\n     */\n    public void setTabGravity(@TabGravity int gravity) {\n        if (mTabGravity != gravity) {\n            mTabGravity = gravity;\n            applyModeAndGravity();\n        }\n    }\n\n    /**\n     * The current gravity used for laying out tabs.\n     *\n     * @return one of {@link #GRAVITY_CENTER} or {@link #GRAVITY_FILL}.\n     */\n    @TabGravity\n    public int getTabGravity() {\n        return mTabGravity;\n    }\n\n    /**\n     * Sets the text colors for the different states (normal, selected) used for the tabs.\n     *\n     * @see #getTabTextColors()\n     */\n    public void setTabTextColors(@Nullable ColorStateList textColor) {\n        if (mTabTextColors != textColor) {\n            mTabTextColors = textColor;\n            updateAllTabs();\n        }\n    }\n\n    /**\n     * Gets the text colors for the different states (normal, selected) used for the tabs.\n     */\n    @Nullable\n    public ColorStateList getTabTextColors() {\n        return mTabTextColors;\n    }\n\n    /**\n     * Sets the text colors for the different states (normal, selected) used for the tabs.\n     *\n     * @attr ref android.support.design.R.styleable#TabLayout_tabTextColor\n     * @attr ref android.support.design.R.styleable#TabLayout_tabSelectedTextColor\n     */\n    public void setTabTextColors(int normalColor, int selectedColor) {\n        setTabTextColors(createColorStateList(normalColor, selectedColor));\n    }\n\n    /**\n     * The one-stop shop for setting up this {@link TabLayout} with a {@link ViewPager}.\n     * <p>\n     * <p>This is the same as calling {@link #setupWithViewPager(ViewPager, boolean)} with\n     * auto-refresh enabled.</p>\n     *\n     * @param viewPager the ViewPager to link to, or {@code null} to clear any previous link\n     */\n    public void setupWithViewPager(@Nullable ViewPager viewPager) {\n        setupWithViewPager(viewPager, true);\n    }\n\n    /**\n     * The one-stop shop for setting up this {@link TabLayout} with a {@link ViewPager}.\n     * <p>\n     * <p>This method will link the given ViewPager and this TabLayout together so that\n     * changes in one are automatically reflected in the other. This includes scroll state changes\n     * and clicks. The tabs displayed in this layout will be populated\n     * from the ViewPager adapter's page titles.</p>\n     * <p>\n     * <p>If {@code autoRefresh} is {@code true}, any changes in the {@link PagerAdapter} will\n     * trigger this layout to re-populate itself from the adapter's titles.</p>\n     * <p>\n     * <p>If the given ViewPager is non-null, it needs to already have a\n     * {@link PagerAdapter} set.</p>\n     *\n     * @param viewPager   the ViewPager to link to, or {@code null} to clear any previous link\n     * @param autoRefresh whether this layout should refresh its contents if the given ViewPager's\n     *                    content changes\n     */\n    public void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh) {\n        setupWithViewPager(viewPager, autoRefresh, false);\n    }\n\n    private void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh,\n                                    boolean implicitSetup) {\n        if (mViewPager != null) {\n            // If we've already been setup with a ViewPager, remove us from it\n            if (mPageChangeListener != null) {\n                mViewPager.removeOnPageChangeListener(mPageChangeListener);\n            }\n            if (mAdapterChangeListener != null) {\n                mViewPager.removeOnAdapterChangeListener(mAdapterChangeListener);\n            }\n        }\n\n        if (mCurrentVpSelectedListener != null) {\n            // If we already have a tab selected listener for the ViewPager, remove it\n            removeOnTabSelectedListener(mCurrentVpSelectedListener);\n            mCurrentVpSelectedListener = null;\n        }\n\n        if (viewPager != null) {\n            mViewPager = viewPager;\n\n            // Add our custom OnPageChangeListener to the ViewPager\n            if (mPageChangeListener == null) {\n                mPageChangeListener = new TabLayoutOnPageChangeListener(this);\n            }\n            mPageChangeListener.reset();\n            viewPager.addOnPageChangeListener(mPageChangeListener);\n\n            // Now we'll add a tab selected listener to set ViewPager's current item\n            mCurrentVpSelectedListener = new ViewPagerOnTabSelectedListener(viewPager);\n            addOnTabSelectedListener(mCurrentVpSelectedListener);\n\n            final PagerAdapter adapter = viewPager.getAdapter();\n            if (adapter != null) {\n                // Now we'll populate ourselves from the pager adapter, adding an observer if\n                // autoRefresh is enabled\n                setPagerAdapter(adapter, autoRefresh);\n            }\n\n            // Add a listener so that we're notified of any adapter changes\n            if (mAdapterChangeListener == null) {\n                mAdapterChangeListener = new AdapterChangeListener();\n            }\n            mAdapterChangeListener.setAutoRefresh(autoRefresh);\n            viewPager.addOnAdapterChangeListener(mAdapterChangeListener);\n\n            // Now update the scroll position to match the ViewPager's current item\n            setScrollPosition(viewPager.getCurrentItem(), 0f, true);\n        } else {\n            // We've been given a null ViewPager so we need to clear out the internal state,\n            // listeners and observers\n            mViewPager = null;\n            setPagerAdapter(null, false);\n        }\n\n        mSetupViewPagerImplicitly = implicitSetup;\n    }\n\n    /**\n     * @deprecated Use {@link #setupWithViewPager(ViewPager)} to link a TabLayout with a ViewPager\n     * together. When that method is used, the TabLayout will be automatically updated\n     * when the {@link PagerAdapter} is changed.\n     */\n    @Deprecated\n    public void setTabsFromPagerAdapter(@Nullable final PagerAdapter adapter) {\n        setPagerAdapter(adapter, false);\n    }\n\n    @Override\n    public boolean shouldDelayChildPressedState() {\n        // Only delay the pressed state if the tabs can scroll\n        return getTabScrollRange() > 0;\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n\n        if (mViewPager == null) {\n            // If we don't have a ViewPager already, check if our parent is a ViewPager to\n            // setup with it automatically\n            final ViewParent vp = getParent();\n            if (vp instanceof ViewPager) {\n                // If we have a ViewPager parent and we've been added as part of its decor, let's\n                // assume that we should automatically setup to display any titles\n                setupWithViewPager((ViewPager) vp, true, true);\n            }\n        }\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n\n        if (mSetupViewPagerImplicitly) {\n            // If we've been setup with a ViewPager implicitly, let's clear out any listeners, etc\n            setupWithViewPager(null);\n            mSetupViewPagerImplicitly = false;\n        }\n    }\n\n    private int getTabScrollRange() {\n        return Math.max(0, mTabStrip.getWidth() - getWidth() - getPaddingLeft()\n                - getPaddingRight());\n    }\n\n    void setPagerAdapter(@Nullable final PagerAdapter adapter, final boolean addObserver) {\n        if (mPagerAdapter != null && mPagerAdapterObserver != null) {\n            // If we already have a PagerAdapter, unregister our observer\n            mPagerAdapter.unregisterDataSetObserver(mPagerAdapterObserver);\n        }\n\n        mPagerAdapter = adapter;\n\n        if (addObserver && adapter != null) {\n            // Register our observer on the new adapter\n            if (mPagerAdapterObserver == null) {\n                mPagerAdapterObserver = new PagerAdapterObserver();\n            }\n            adapter.registerDataSetObserver(mPagerAdapterObserver);\n        }\n\n        // Finally make sure we reflect the new adapter\n        populateFromPagerAdapter();\n    }\n\n    void populateFromPagerAdapter() {\n        removeAllTabs();\n\n        if (mPagerAdapter != null) {\n            final int adapterCount = mPagerAdapter.getCount();\n            for (int i = 0; i < adapterCount; i++) {\n                addTab(newTab().setText(mPagerAdapter.getPageTitle(i)), false);\n            }\n\n            // Make sure we reflect the currently set ViewPager item\n            if (mViewPager != null && adapterCount > 0) {\n                final int curItem = mViewPager.getCurrentItem();\n                if (curItem != getSelectedTabPosition() && curItem < getTabCount()) {\n                    selectTab(getTabAt(curItem));\n                }\n            }\n        }\n    }\n\n    private void updateAllTabs() {\n        for (int i = 0, z = mTabs.size(); i < z; i++) {\n            mTabs.get(i).updateView();\n        }\n    }\n\n    private TabView createTabView(@NonNull final Tab tab) {\n        TabView tabView = mTabViewPool != null ? mTabViewPool.acquire() : null;\n        if (tabView == null) {\n            tabView = new TabView(getContext());\n        }\n        tabView.setTab(tab);\n        tabView.setFocusable(true);\n        tabView.setMinimumWidth(getTabMinWidth());\n        return tabView;\n    }\n\n    private void configureTab(Tab tab, int position) {\n        tab.setPosition(position);\n        mTabs.add(position, tab);\n\n        final int count = mTabs.size();\n        for (int i = position + 1; i < count; i++) {\n            mTabs.get(i).setPosition(i);\n        }\n    }\n\n    private void addTabView(Tab tab) {\n        final TabView tabView = tab.mView;\n        mTabStrip.addView(tabView, tab.getPosition(), createLayoutParamsForTabs());\n    }\n\n    @Override\n    public void addView(View child) {\n        addViewInternal(child);\n    }\n\n    @Override\n    public void addView(View child, int index) {\n        addViewInternal(child);\n    }\n\n    @Override\n    public void addView(View child, ViewGroup.LayoutParams params) {\n        addViewInternal(child);\n    }\n\n    @Override\n    public void addView(View child, int index, ViewGroup.LayoutParams params) {\n        addViewInternal(child);\n    }\n\n    private void addViewInternal(final View child) {\n        if (child instanceof TabItem) {\n            addTabFromItemView((TabItem) child);\n        } else {\n            throw new IllegalArgumentException(\"Only TabItem instances can be added to TabLayout\");\n        }\n    }\n\n    private LinearLayout.LayoutParams createLayoutParamsForTabs() {\n        final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(\n                LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);\n        updateTabViewLayoutParams(lp);\n        return lp;\n    }\n\n    private void updateTabViewLayoutParams(LinearLayout.LayoutParams lp) {\n        if (mMode == MODE_FIXED && mTabGravity == GRAVITY_FILL) {\n            lp.width = 0;\n            lp.weight = 1;\n        } else {\n            lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;\n            lp.weight = 0;\n        }\n    }\n\n    int dpToPx(int dps) {\n        return Math.round(getResources().getDisplayMetrics().density * dps);\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        // If we have a MeasureSpec which allows us to decide our height, try and use the default\n        // height\n        final int idealHeight = dpToPx(getDefaultHeight()) + getPaddingTop() + getPaddingBottom();\n        switch (MeasureSpec.getMode(heightMeasureSpec)) {\n            case MeasureSpec.AT_MOST:\n                heightMeasureSpec = MeasureSpec.makeMeasureSpec(\n                        Math.min(idealHeight, MeasureSpec.getSize(heightMeasureSpec)),\n                        MeasureSpec.EXACTLY);\n                break;\n            case MeasureSpec.UNSPECIFIED:\n                heightMeasureSpec = MeasureSpec.makeMeasureSpec(idealHeight, MeasureSpec.EXACTLY);\n                break;\n        }\n\n        final int specWidth = MeasureSpec.getSize(widthMeasureSpec);\n        if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {\n            // If we don't have an unspecified width spec, use the given size to calculate\n            // the max tab width\n            mTabMaxWidth = mRequestedTabMaxWidth > 0\n                    ? mRequestedTabMaxWidth\n                    : specWidth - dpToPx(TAB_MIN_WIDTH_MARGIN);\n        }\n\n        // Now super measure itself using the (possibly) modified height spec\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\n        if (getChildCount() == 1) {\n            // If we're in fixed mode then we need to make the tab strip is the same width as us\n            // so we don't scroll\n            final View child = getChildAt(0);\n            boolean remeasure = false;\n\n            switch (mMode) {\n                case MODE_SCROLLABLE:\n                    // We only need to resize the child if it's smaller than us. This is similar\n                    // to fillViewport\n                    remeasure = child.getMeasuredWidth() < getMeasuredWidth();\n                    break;\n                case MODE_FIXED:\n                    // Resize the child so that it doesn't scroll\n                    remeasure = child.getMeasuredWidth() != getMeasuredWidth();\n                    break;\n            }\n\n            if (remeasure) {\n                // Re-measure the child with a widthSpec set to be exactly our measure width\n                int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop()\n                        + getPaddingBottom(), child.getLayoutParams().height);\n                int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(\n                        getMeasuredWidth(), MeasureSpec.EXACTLY);\n                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);\n            }\n        }\n    }\n\n    private void removeTabViewAt(int position) {\n        final TabView view = (TabView) mTabStrip.getChildAt(position);\n        mTabStrip.removeViewAt(position);\n        if (view != null) {\n            view.reset();\n            mTabViewPool.release(view);\n        }\n        requestLayout();\n    }\n\n    private void animateToTab(int newPosition) {\n        if (newPosition == Tab.INVALID_POSITION) {\n            return;\n        }\n\n        if (getWindowToken() == null || !ViewCompat.isLaidOut(this)\n                || mTabStrip.childrenNeedLayout()) {\n            // If we don't have a window token, or we haven't been laid out yet just draw the new\n            // position now\n            setScrollPosition(newPosition, 0f, true);\n            return;\n        }\n\n        final int startScrollX = getScrollX();\n        final int targetScrollX = calculateScrollXForTab(newPosition, 0);\n\n        if (startScrollX != targetScrollX) {\n            ensureScrollAnimator();\n\n            mScrollAnimator.setIntValues(startScrollX, targetScrollX);\n            mScrollAnimator.start();\n        }\n\n        // Now animate the indicator\n        mTabStrip.animateIndicatorToPosition(newPosition, ANIMATION_DURATION);\n    }\n\n    private void ensureScrollAnimator() {\n        if (mScrollAnimator == null) {\n            mScrollAnimator = new ValueAnimator();\n            mScrollAnimator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);\n            mScrollAnimator.setDuration(ANIMATION_DURATION);\n            mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                @Override\n                public void onAnimationUpdate(ValueAnimator animator) {\n                    scrollTo((int) animator.getAnimatedValue(), 0);\n                }\n            });\n        }\n    }\n\n    void setScrollAnimatorListener(Animator.AnimatorListener listener) {\n        ensureScrollAnimator();\n        mScrollAnimator.addListener(listener);\n    }\n\n    private void setSelectedTabView(int position) {\n        final int tabCount = mTabStrip.getChildCount();\n        if (position < tabCount) {\n            for (int i = 0; i < tabCount; i++) {\n                final View child = mTabStrip.getChildAt(i);\n                child.setSelected(i == position);\n            }\n        }\n    }\n\n    void selectTab(Tab tab) {\n        selectTab(tab, true);\n    }\n\n    void selectTab(final Tab tab, boolean updateIndicator) {\n        final Tab currentTab = mSelectedTab;\n\n        if (currentTab == tab) {\n            if (currentTab != null) {\n                dispatchTabReselected(tab);\n                animateToTab(tab.getPosition());\n            }\n        } else {\n            final int newPosition = tab != null ? tab.getPosition() : Tab.INVALID_POSITION;\n            if (updateIndicator) {\n                if ((currentTab == null || currentTab.getPosition() == Tab.INVALID_POSITION)\n                        && newPosition != Tab.INVALID_POSITION) {\n                    // If we don't currently have a tab, just draw the indicator\n                    setScrollPosition(newPosition, 0f, true);\n                } else {\n                    animateToTab(newPosition);\n                }\n                if (newPosition != Tab.INVALID_POSITION) {\n                    setSelectedTabView(newPosition);\n                }\n            }\n            if (currentTab != null) {\n                dispatchTabUnselected(currentTab);\n            }\n            mSelectedTab = tab;\n            if (tab != null) {\n                dispatchTabSelected(tab);\n            }\n        }\n    }\n\n    private void dispatchTabSelected(@NonNull final Tab tab) {\n        for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {\n            mSelectedListeners.get(i).onTabSelected(tab);\n        }\n    }\n\n    private void dispatchTabUnselected(@NonNull final Tab tab) {\n        for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {\n            mSelectedListeners.get(i).onTabUnselected(tab);\n        }\n    }\n\n    private void dispatchTabReselected(@NonNull final Tab tab) {\n        for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {\n            mSelectedListeners.get(i).onTabReselected(tab);\n        }\n    }\n\n    private int calculateScrollXForTab(int position, float positionOffset) {\n        if (mMode == MODE_SCROLLABLE) {\n            final View selectedChild = mTabStrip.getChildAt(position);\n            final View nextChild = position + 1 < mTabStrip.getChildCount()\n                    ? mTabStrip.getChildAt(position + 1)\n                    : null;\n            final int selectedWidth = selectedChild != null ? selectedChild.getWidth() : 0;\n            final int nextWidth = nextChild != null ? nextChild.getWidth() : 0;\n\n            // base scroll amount: places center of tab in center of parent\n            int scrollBase = selectedChild.getLeft() + (selectedWidth / 2) - (getWidth() / 2);\n            // offset amount: fraction of the distance between centers of tabs\n            int scrollOffset = (int) ((selectedWidth + nextWidth) * 0.5f * positionOffset);\n\n            return (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_LTR)\n                    ? scrollBase + scrollOffset\n                    : scrollBase - scrollOffset;\n        }\n        return 0;\n    }\n\n    private void applyModeAndGravity() {\n        int paddingStart = 0;\n        if (mMode == MODE_SCROLLABLE) {\n            // If we're scrollable, or fixed at start, inset using padding\n            paddingStart = Math.max(0, mContentInsetStart - mTabPaddingStart);\n        }\n        ViewCompat.setPaddingRelative(mTabStrip, paddingStart, 0, 0, 0);\n\n        switch (mMode) {\n            case MODE_FIXED:\n                mTabStrip.setGravity(Gravity.CENTER_HORIZONTAL);\n                break;\n            case MODE_SCROLLABLE:\n                mTabStrip.setGravity(GravityCompat.START);\n                break;\n        }\n\n        updateTabViews(true);\n    }\n\n    void updateTabViews(final boolean requestLayout) {\n        for (int i = 0; i < mTabStrip.getChildCount(); i++) {\n            View child = mTabStrip.getChildAt(i);\n            child.setMinimumWidth(getTabMinWidth());\n            updateTabViewLayoutParams((LinearLayout.LayoutParams) child.getLayoutParams());\n            if (requestLayout) {\n                child.requestLayout();\n            }\n        }\n    }\n\n    /**\n     * Demo tab in this layout. Instances can be created via {@link #newTab()}.\n     */\n    public static final class Tab {\n\n        /**\n         * An invalid position for a tab.\n         *\n         * @see #getPosition()\n         */\n        public static final int INVALID_POSITION = -1;\n\n        private Object mTag;\n        private Drawable mIcon;\n        private CharSequence mText;\n        private CharSequence mContentDesc;\n        private int mPosition = INVALID_POSITION;\n        private View mCustomView;\n\n        ShortTabLayout mParent;\n        TabView mView;\n\n        Tab() {\n            // Private constructor\n        }\n\n        /**\n         * @return This Tab's tag object.\n         */\n        @Nullable\n        public Object getTag() {\n            return mTag;\n        }\n\n        /**\n         * Give this Tab an arbitrary object to hold for later use.\n         *\n         * @param tag Object to store\n         * @return The current instance for call chaining\n         */\n        @NonNull\n        public Tab setTag(@Nullable Object tag) {\n            mTag = tag;\n            return this;\n        }\n\n\n        /**\n         * Returns the custom view used for this tab.\n         *\n         * @see #setCustomView(View)\n         * @see #setCustomView(int)\n         */\n        @Nullable\n        public View getCustomView() {\n            return mCustomView;\n        }\n\n        /**\n         * Set a custom view to be used for this tab.\n         * <p>\n         * If the provided view contains a {@link TextView} with an ID of\n         * {@link android.R.id#text1} then that will be updated with the value given\n         * to {@link #setText(CharSequence)}. Similarly, if this layout contains an\n         * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with\n         * the value given to {@link #setIcon(Drawable)}.\n         * </p>\n         *\n         * @param view Custom view to be used as a tab.\n         * @return The current instance for call chaining\n         */\n        @NonNull\n        public Tab setCustomView(@Nullable View view) {\n            mCustomView = view;\n            updateView();\n            return this;\n        }\n\n        /**\n         * Set a custom view to be used for this tab.\n         * <p>\n         * If the inflated layout contains a {@link TextView} with an ID of\n         * {@link android.R.id#text1} then that will be updated with the value given\n         * to {@link #setText(CharSequence)}. Similarly, if this layout contains an\n         * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with\n         * the value given to {@link #setIcon(Drawable)}.\n         * </p>\n         *\n         * @param resId Demo layout resource to inflate and use as a custom tab view\n         * @return The current instance for call chaining\n         */\n        @NonNull\n        public Tab setCustomView(@LayoutRes int resId) {\n            final LayoutInflater inflater = LayoutInflater.from(mView.getContext());\n            return setCustomView(inflater.inflate(resId, mView, false));\n        }\n\n        /**\n         * Return the icon associated with this tab.\n         *\n         * @return The tab's icon\n         */\n        @Nullable\n        public Drawable getIcon() {\n            return mIcon;\n        }\n\n        /**\n         * Return the current position of this tab in the action bar.\n         *\n         * @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in\n         * the action bar.\n         */\n        public int getPosition() {\n            return mPosition;\n        }\n\n        void setPosition(int position) {\n            mPosition = position;\n        }\n\n        /**\n         * Return the text of this tab.\n         *\n         * @return The tab's text\n         */\n        @Nullable\n        public CharSequence getText() {\n            return mText;\n        }\n\n        /**\n         * Set the icon displayed on this tab.\n         *\n         * @param icon The drawable to use as an icon\n         * @return The current instance for call chaining\n         */\n        @NonNull\n        public Tab setIcon(@Nullable Drawable icon) {\n            mIcon = icon;\n            updateView();\n            return this;\n        }\n\n        /**\n         * Set the icon displayed on this tab.\n         *\n         * @param resId Demo resource ID referring to the icon that should be displayed\n         * @return The current instance for call chaining\n         */\n        @NonNull\n        public Tab setIcon(@DrawableRes int resId) {\n            if (mParent == null) {\n                throw new IllegalArgumentException(\"Tab not attached to a TabLayout\");\n            }\n            return setIcon(AppCompatResources.getDrawable(mParent.getContext(), resId));\n        }\n\n        /**\n         * Set the text displayed on this tab. Text may be truncated if there is not room to display\n         * the entire string.\n         *\n         * @param text The text to display\n         * @return The current instance for call chaining\n         */\n        @NonNull\n        public Tab setText(@Nullable CharSequence text) {\n            mText = text;\n            updateView();\n            return this;\n        }\n\n        /**\n         * Set the text displayed on this tab. Text may be truncated if there is not room to display\n         * the entire string.\n         *\n         * @param resId Demo resource ID referring to the text that should be displayed\n         * @return The current instance for call chaining\n         */\n        @NonNull\n        public Tab setText(@StringRes int resId) {\n            if (mParent == null) {\n                throw new IllegalArgumentException(\"Tab not attached to a TabLayout\");\n            }\n            return setText(mParent.getResources().getText(resId));\n        }\n\n        /**\n         * Select this tab. Only valid if the tab has been added to the action bar.\n         */\n        public void select() {\n            if (mParent == null) {\n                throw new IllegalArgumentException(\"Tab not attached to a TabLayout\");\n            }\n            mParent.selectTab(this);\n        }\n\n        /**\n         * Returns true if this tab is currently selected.\n         */\n        public boolean isSelected() {\n            if (mParent == null) {\n                throw new IllegalArgumentException(\"Tab not attached to a TabLayout\");\n            }\n            return mParent.getSelectedTabPosition() == mPosition;\n        }\n\n        /**\n         * Set a description of this tab's content for use in accessibility support. If no content\n         * description is provided the title will be used.\n         *\n         * @param resId Demo resource ID referring to the description text\n         * @return The current instance for call chaining\n         * @see #setContentDescription(CharSequence)\n         * @see #getContentDescription()\n         */\n        @NonNull\n        public Tab setContentDescription(@StringRes int resId) {\n            if (mParent == null) {\n                throw new IllegalArgumentException(\"Tab not attached to a TabLayout\");\n            }\n            return setContentDescription(mParent.getResources().getText(resId));\n        }\n\n        /**\n         * Set a description of this tab's content for use in accessibility support. If no content\n         * description is provided the title will be used.\n         *\n         * @param contentDesc Description of this tab's content\n         * @return The current instance for call chaining\n         * @see #setContentDescription(int)\n         * @see #getContentDescription()\n         */\n        @NonNull\n        public Tab setContentDescription(@Nullable CharSequence contentDesc) {\n            mContentDesc = contentDesc;\n            updateView();\n            return this;\n        }\n\n        /**\n         * Gets a brief description of this tab's content for use in accessibility support.\n         *\n         * @return Description of this tab's content\n         * @see #setContentDescription(CharSequence)\n         * @see #setContentDescription(int)\n         */\n        @Nullable\n        public CharSequence getContentDescription() {\n            return mContentDesc;\n        }\n\n        void updateView() {\n            if (mView != null) {\n                mView.update();\n            }\n        }\n\n        void reset() {\n            mParent = null;\n            mView = null;\n            mTag = null;\n            mIcon = null;\n            mText = null;\n            mContentDesc = null;\n            mPosition = INVALID_POSITION;\n            mCustomView = null;\n        }\n    }\n\n    class TabView extends LinearLayout {\n        private Tab mTab;\n        private TextView mTextView;\n        private ImageView mIconView;\n\n        private View mCustomView;\n        private TextView mCustomTextView;\n        private ImageView mCustomIconView;\n\n        private int mDefaultMaxLines = 2;\n\n        public TabView(Context context) {\n            super(context);\n            if (mTabBackgroundResId != 0) {\n                ViewCompat.setBackground(\n                        this, AppCompatResources.getDrawable(context, mTabBackgroundResId));\n            }\n            ViewCompat.setPaddingRelative(this, mTabPaddingStart, mTabPaddingTop,\n                    mTabPaddingEnd, mTabPaddingBottom);\n            setGravity(Gravity.CENTER);\n            setOrientation(VERTICAL);\n            setClickable(true);\n            ViewCompat.setPointerIcon(this,\n                    PointerIconCompat.getSystemIcon(getContext(), PointerIconCompat.TYPE_HAND));\n        }\n\n        @Override\n        public boolean performClick() {\n            final boolean handled = super.performClick();\n\n            if (mTab != null) {\n                if (!handled) {\n                    playSoundEffect(SoundEffectConstants.CLICK);\n                }\n                mTab.select();\n                return true;\n            } else {\n                return handled;\n            }\n        }\n\n        @Override\n        public void setSelected(final boolean selected) {\n            final boolean changed = isSelected() != selected;\n\n            super.setSelected(selected);\n\n            if (changed && selected && Build.VERSION.SDK_INT < 16) {\n                // Pre-JB we need to manually send the TYPE_VIEW_SELECTED event\n                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);\n            }\n\n            // Always dispatch this to the child views, regardless of whether the value has\n            // changed\n            if (mTextView != null) {\n                mTextView.setSelected(selected);\n            }\n            if (mIconView != null) {\n                mIconView.setSelected(selected);\n            }\n            if (mCustomView != null) {\n                mCustomView.setSelected(selected);\n            }\n        }\n\n        @Override\n        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {\n            super.onInitializeAccessibilityEvent(event);\n            // This view masquerades as an action bar tab.\n            event.setClassName(ActionBar.Tab.class.getName());\n        }\n\n        @Override\n        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {\n            super.onInitializeAccessibilityNodeInfo(info);\n            // This view masquerades as an action bar tab.\n            info.setClassName(ActionBar.Tab.class.getName());\n        }\n\n        @Override\n        public void onMeasure(final int origWidthMeasureSpec, final int origHeightMeasureSpec) {\n            final int specWidthSize = MeasureSpec.getSize(origWidthMeasureSpec);\n            final int specWidthMode = MeasureSpec.getMode(origWidthMeasureSpec);\n            final int maxWidth = getTabMaxWidth();\n\n            final int widthMeasureSpec;\n            final int heightMeasureSpec = origHeightMeasureSpec;\n\n            if (maxWidth > 0 && (specWidthMode == MeasureSpec.UNSPECIFIED\n                    || specWidthSize > maxWidth)) {\n                // If we have a max width and a given spec which is either unspecified or\n                // larger than the max width, update the width spec using the same mode\n                widthMeasureSpec = MeasureSpec.makeMeasureSpec(mTabMaxWidth, MeasureSpec.AT_MOST);\n            } else {\n                // Else, use the original width spec\n                widthMeasureSpec = origWidthMeasureSpec;\n            }\n\n            // Now lets measure\n            super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\n            // We need to switch the text size based on whether the text is spanning 2 lines or not\n            if (mTextView != null) {\n                final Resources res = getResources();\n                float textSize = mTabTextSize;\n                int maxLines = mDefaultMaxLines;\n\n                if (mIconView != null && mIconView.getVisibility() == VISIBLE) {\n                    // If the icon view is being displayed, we limit the text to 1 line\n                    maxLines = 1;\n                } else if (mTextView != null && mTextView.getLineCount() > 1) {\n                    // Otherwise when we have text which wraps we reduce the text size\n                    textSize = mTabTextMultiLineSize;\n                }\n\n                final float curTextSize = mTextView.getTextSize();\n                final int curLineCount = mTextView.getLineCount();\n                final int curMaxLines = TextViewCompat.getMaxLines(mTextView);\n\n                if (textSize != curTextSize || (curMaxLines >= 0 && maxLines != curMaxLines)) {\n                    // We've got a new text size and/or max lines...\n                    boolean updateTextView = true;\n\n                    if (mMode == MODE_FIXED && textSize > curTextSize && curLineCount == 1) {\n                        // If we're in fixed mode, going up in text size and currently have 1 line\n                        // then it's very easy to get into an infinite recursion.\n                        // To combat that we check to see if the change in text size\n                        // will cause a line count change. If so, abort the size change and stick\n                        // to the smaller size.\n                        final Layout layout = mTextView.getLayout();\n                        if (layout == null || approximateLineWidth(layout, 0, textSize)\n                                > getMeasuredWidth() - getPaddingLeft() - getPaddingRight()) {\n                            updateTextView = false;\n                        }\n                    }\n\n                    if (updateTextView) {\n                        mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);\n                        mTextView.setMaxLines(maxLines);\n                        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n                    }\n                }\n            }\n        }\n\n        void setTab(@Nullable final Tab tab) {\n            if (tab != mTab) {\n                mTab = tab;\n                update();\n            }\n        }\n\n        void reset() {\n            setTab(null);\n            setSelected(false);\n        }\n\n        final void update() {\n            final Tab tab = mTab;\n            final View custom = tab != null ? tab.getCustomView() : null;\n            if (custom != null) {\n                final ViewParent customParent = custom.getParent();\n                if (customParent != this) {\n                    if (customParent != null) {\n                        ((ViewGroup) customParent).removeView(custom);\n                    }\n                    addView(custom);\n                }\n                mCustomView = custom;\n                if (mTextView != null) {\n                    mTextView.setVisibility(GONE);\n                }\n                if (mIconView != null) {\n                    mIconView.setVisibility(GONE);\n                    mIconView.setImageDrawable(null);\n                }\n\n                mCustomTextView = (TextView) custom.findViewById(android.R.id.text1);\n                if (mCustomTextView != null) {\n                    mDefaultMaxLines = TextViewCompat.getMaxLines(mCustomTextView);\n                }\n                mCustomIconView = (ImageView) custom.findViewById(android.R.id.icon);\n            } else {\n                // We do not have a custom view. Remove one if it already exists\n                if (mCustomView != null) {\n                    removeView(mCustomView);\n                    mCustomView = null;\n                }\n                mCustomTextView = null;\n                mCustomIconView = null;\n            }\n\n            if (mCustomView == null) {\n                // If there isn't a custom view, we'll us our own in-built layouts\n                if (mIconView == null) {\n                    ImageView iconView = (ImageView) LayoutInflater.from(getContext())\n                            .inflate(R.layout.design_layout_tab_icon, this, false);\n                    addView(iconView, 0);\n                    mIconView = iconView;\n                }\n                if (mTextView == null) {\n                    TextView textView = (TextView) LayoutInflater.from(getContext())\n                            .inflate(R.layout.design_layout_tab_text, this, false);\n                    addView(textView);\n                    mTextView = textView;\n                    mDefaultMaxLines = TextViewCompat.getMaxLines(mTextView);\n                }\n                TextViewCompat.setTextAppearance(mTextView, mTabTextAppearance);\n                if (mTabTextColors != null) {\n                    mTextView.setTextColor(mTabTextColors);\n                }\n                updateTextAndIcon(mTextView, mIconView);\n            } else {\n                // Else, we'll see if there is a TextView or ImageView present and update them\n                if (mCustomTextView != null || mCustomIconView != null) {\n                    updateTextAndIcon(mCustomTextView, mCustomIconView);\n                }\n            }\n\n            // Finally update our selected state\n            setSelected(tab != null && tab.isSelected());\n        }\n\n        private void updateTextAndIcon(@Nullable final TextView textView,\n                                       @Nullable final ImageView iconView) {\n            final Drawable icon = mTab != null ? mTab.getIcon() : null;\n            final CharSequence text = mTab != null ? mTab.getText() : null;\n            final CharSequence contentDesc = mTab != null ? mTab.getContentDescription() : null;\n\n            if (iconView != null) {\n                if (icon != null) {\n                    iconView.setImageDrawable(icon);\n                    iconView.setVisibility(VISIBLE);\n                    setVisibility(VISIBLE);\n                } else {\n                    iconView.setVisibility(GONE);\n                    iconView.setImageDrawable(null);\n                }\n                iconView.setContentDescription(contentDesc);\n            }\n\n            final boolean hasText = !TextUtils.isEmpty(text);\n            if (textView != null) {\n                if (hasText) {\n                    textView.setText(text);\n                    textView.setVisibility(VISIBLE);\n                    setVisibility(VISIBLE);\n                } else {\n                    textView.setVisibility(GONE);\n                    textView.setText(null);\n                }\n                textView.setContentDescription(contentDesc);\n            }\n\n            if (iconView != null) {\n                MarginLayoutParams lp = ((MarginLayoutParams) iconView.getLayoutParams());\n                int bottomMargin = 0;\n                if (hasText && iconView.getVisibility() == VISIBLE) {\n                    // If we're showing both text and icon, add some margin bottom to the icon\n                    bottomMargin = dpToPx(DEFAULT_GAP_TEXT_ICON);\n                }\n                if (bottomMargin != lp.bottomMargin) {\n                    lp.bottomMargin = bottomMargin;\n                    iconView.requestLayout();\n                }\n            }\n            TooltipCompat.setTooltipText(this, hasText ? null : contentDesc);\n        }\n\n        public Tab getTab() {\n            return mTab;\n        }\n\n        /**\n         * Approximates a given lines width with the new provided text size.\n         */\n        private float approximateLineWidth(Layout layout, int line, float textSize) {\n            return layout.getLineWidth(line) * (textSize / layout.getPaint().getTextSize());\n        }\n    }\n\n    private class SlidingTabStrip extends LinearLayout {\n        private int mSelectedIndicatorHeight;\n        private final Paint mSelectedIndicatorPaint;\n\n        int mSelectedPosition = -1;\n        float mSelectionOffset;\n\n        private int mLayoutDirection = -1;\n\n        private int mIndicatorLeft = -1;\n        private int mIndicatorRight = -1;\n\n        private ValueAnimator mIndicatorAnimator;\n\n        SlidingTabStrip(Context context) {\n            super(context);\n            setWillNotDraw(false);\n            mSelectedIndicatorPaint = new Paint();\n        }\n\n        void setSelectedIndicatorColor(int color) {\n            if (mSelectedIndicatorPaint.getColor() != color) {\n                mSelectedIndicatorPaint.setColor(color);\n                ViewCompat.postInvalidateOnAnimation(this);\n            }\n        }\n\n        void setSelectedIndicatorHeight(int height) {\n            if (mSelectedIndicatorHeight != height) {\n                mSelectedIndicatorHeight = height;\n                ViewCompat.postInvalidateOnAnimation(this);\n            }\n        }\n\n        boolean childrenNeedLayout() {\n            for (int i = 0, z = getChildCount(); i < z; i++) {\n                final View child = getChildAt(i);\n                if (child.getWidth() <= 0) {\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        void setIndicatorPositionFromTabPosition(int position, float positionOffset) {\n            if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) {\n                mIndicatorAnimator.cancel();\n            }\n\n            mSelectedPosition = position;\n            mSelectionOffset = positionOffset;\n            updateIndicatorPosition();\n        }\n\n        float getIndicatorPosition() {\n            return mSelectedPosition + mSelectionOffset;\n        }\n\n        @Override\n        public void onRtlPropertiesChanged(int layoutDirection) {\n            super.onRtlPropertiesChanged(layoutDirection);\n\n            // Workaround for a bug before Android M where LinearLayout did not relayout itself when\n            // layout direction changed.\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n                //noinspection WrongConstant\n                if (mLayoutDirection != layoutDirection) {\n                    requestLayout();\n                    mLayoutDirection = layoutDirection;\n                }\n            }\n        }\n\n        @Override\n        protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {\n            super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\n            if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) {\n                // HorizontalScrollView will first measure use with UNSPECIFIED, and then with\n                // EXACTLY. Ignore the first call since anything we do will be overwritten anyway\n                return;\n            }\n\n            if (mMode == MODE_FIXED && mTabGravity == GRAVITY_CENTER) {\n                final int count = getChildCount();\n\n                // First we'll find the widest tab\n                int largestTabWidth = 0;\n                for (int i = 0, z = count; i < z; i++) {\n                    View child = getChildAt(i);\n                    if (child.getVisibility() == VISIBLE) {\n                        largestTabWidth = Math.max(largestTabWidth, child.getMeasuredWidth());\n                    }\n                }\n\n                if (largestTabWidth <= 0) {\n                    // If we don't have a largest child yet, skip until the next measure pass\n                    return;\n                }\n\n                final int gutter = dpToPx(FIXED_WRAP_GUTTER_MIN);\n                boolean remeasure = false;\n\n                if (largestTabWidth * count <= getMeasuredWidth() - gutter * 2) {\n                    // If the tabs fit within our width minus gutters, we will set all tabs to have\n                    // the same width\n                    for (int i = 0; i < count; i++) {\n                        final LinearLayout.LayoutParams lp =\n                                (LayoutParams) getChildAt(i).getLayoutParams();\n                        if (lp.width != largestTabWidth || lp.weight != 0) {\n                            lp.width = largestTabWidth;\n                            lp.weight = 0;\n                            remeasure = true;\n                        }\n                    }\n                } else {\n                    // If the tabs will wrap to be larger than the width minus gutters, we need\n                    // to switch to GRAVITY_FILL\n                    mTabGravity = GRAVITY_FILL;\n                    updateTabViews(false);\n                    remeasure = true;\n                }\n\n                if (remeasure) {\n                    // Now re-measure after our changes\n                    super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n                }\n            }\n        }\n\n        @Override\n        protected void onLayout(boolean changed, int l, int t, int r, int b) {\n            super.onLayout(changed, l, t, r, b);\n\n            if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) {\n                // If we're currently running an animation, lets cancel it and start a\n                // new animation with the remaining duration\n                mIndicatorAnimator.cancel();\n                final long duration = mIndicatorAnimator.getDuration();\n                animateIndicatorToPosition(mSelectedPosition,\n                        Math.round((1f - mIndicatorAnimator.getAnimatedFraction()) * duration));\n            } else {\n                // If we've been layed out, update the indicator position\n                updateIndicatorPosition();\n            }\n        }\n\n        private void updateIndicatorPosition() {\n            final View selectedTitle = getChildAt(mSelectedPosition);\n            int left, right;\n\n            if (selectedTitle != null && selectedTitle.getWidth() > 0) {\n                left = selectedTitle.getLeft();\n                right = selectedTitle.getRight();\n\n                if (mSelectionOffset > 0f && mSelectedPosition < getChildCount() - 1) {\n                    // Draw the selection partway between the tabs\n                    View nextTitle = getChildAt(mSelectedPosition + 1);\n                    left = (int) (mSelectionOffset * nextTitle.getLeft() +\n                            (1.0f - mSelectionOffset) * left);\n                    right = (int) (mSelectionOffset * nextTitle.getRight() +\n                            (1.0f - mSelectionOffset) * right);\n                }\n            } else {\n                left = right = -1;\n            }\n\n            setIndicatorPosition(left, right);\n        }\n\n        void setIndicatorPosition(int left, int right) {\n            if (left != mIndicatorLeft || right != mIndicatorRight) {\n                // If the indicator's left/right has changed, invalidate\n                mIndicatorLeft = left;\n                mIndicatorRight = right;\n                ViewCompat.postInvalidateOnAnimation(this);\n            }\n        }\n\n        void animateIndicatorToPosition(final int position, int duration) {\n            if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) {\n                mIndicatorAnimator.cancel();\n            }\n\n            final boolean isRtl = ViewCompat.getLayoutDirection(this)\n                    == ViewCompat.LAYOUT_DIRECTION_RTL;\n\n            final View targetView = getChildAt(position);\n            if (targetView == null) {\n                // If we don't have a view, just update the position now and return\n                updateIndicatorPosition();\n                return;\n            }\n\n            final int targetLeft = targetView.getLeft();\n            final int targetRight = targetView.getRight();\n            final int startLeft;\n            final int startRight;\n\n            if (Math.abs(position - mSelectedPosition) <= 1) {\n                // If the views are adjacent, we'll animate from edge-to-edge\n                startLeft = mIndicatorLeft;\n                startRight = mIndicatorRight;\n            } else {\n                // Else, we'll just grow from the nearest edge\n                final int offset = dpToPx(MOTION_NON_ADJACENT_OFFSET);\n                if (position < mSelectedPosition) {\n                    // We're going end-to-start\n                    if (isRtl) {\n                        startLeft = startRight = targetLeft - offset;\n                    } else {\n                        startLeft = startRight = targetRight + offset;\n                    }\n                } else {\n                    // We're going start-to-end\n                    if (isRtl) {\n                        startLeft = startRight = targetRight + offset;\n                    } else {\n                        startLeft = startRight = targetLeft - offset;\n                    }\n                }\n            }\n\n            if (startLeft != targetLeft || startRight != targetRight) {\n                ValueAnimator animator = mIndicatorAnimator = new ValueAnimator();\n                animator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);\n                animator.setDuration(duration);\n                animator.setFloatValues(0, 1);\n                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                    @Override\n                    public void onAnimationUpdate(ValueAnimator animator) {\n                        final float fraction = animator.getAnimatedFraction();\n                        setIndicatorPosition(\n                                AnimationUtils.lerp(startLeft, targetLeft, fraction),\n                                AnimationUtils.lerp(startRight, targetRight, fraction));\n                    }\n                });\n                animator.addListener(new AnimatorListenerAdapter() {\n                    @Override\n                    public void onAnimationEnd(Animator animator) {\n                        mSelectedPosition = position;\n                        mSelectionOffset = 0f;\n                    }\n                });\n                animator.start();\n            }\n        }\n\n        @Override\n        public void draw(Canvas canvas) {\n            super.draw(canvas);\n\n            // 选中的位置添加下划线 下划线宽度为其总宽度的8分之一\n            int width = mIndicatorRight - mIndicatorLeft;\n            int indicatorWidth = width / 8;\n            int indicatorLeft = mIndicatorLeft + (width - indicatorWidth) / 2;\n            int indicatorRight = mIndicatorRight - (width / 2) + indicatorWidth / 2;\n            if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {\n                canvas.drawRect(indicatorLeft, getHeight() - mSelectedIndicatorHeight,\n                        indicatorRight, getHeight(), mSelectedIndicatorPaint);\n            }\n        }\n    }\n\n    private static ColorStateList createColorStateList(int defaultColor, int selectedColor) {\n        final int[][] states = new int[2][];\n        final int[] colors = new int[2];\n        int i = 0;\n\n        states[i] = SELECTED_STATE_SET;\n        colors[i] = selectedColor;\n        i++;\n\n        // Default enabled state\n        states[i] = EMPTY_STATE_SET;\n        colors[i] = defaultColor;\n        i++;\n\n        return new ColorStateList(states, colors);\n    }\n\n    private int getDefaultHeight() {\n        boolean hasIconAndText = false;\n        for (int i = 0, count = mTabs.size(); i < count; i++) {\n            Tab tab = mTabs.get(i);\n            if (tab != null && tab.getIcon() != null && !TextUtils.isEmpty(tab.getText())) {\n                hasIconAndText = true;\n                break;\n            }\n        }\n        return hasIconAndText ? DEFAULT_HEIGHT_WITH_TEXT_ICON : DEFAULT_HEIGHT;\n    }\n\n    private int getTabMinWidth() {\n        if (mRequestedTabMinWidth != INVALID_WIDTH) {\n            // If we have been given a min width, use it\n            return mRequestedTabMinWidth;\n        }\n        // Else, we'll use the default value\n        return mMode == MODE_SCROLLABLE ? mScrollableTabMinWidth : 0;\n    }\n\n    @Override\n    public LayoutParams generateLayoutParams(AttributeSet attrs) {\n        // We don't care about the layout params of any views added to us, since we don't actually\n        // add them. The only view we add is the SlidingTabStrip, which is done manually.\n        // We return the default layout params so that we don't blow up if we're given a TabItem\n        // without android:layout_* values.\n        return generateDefaultLayoutParams();\n    }\n\n    int getTabMaxWidth() {\n        return mTabMaxWidth;\n    }\n\n    /**\n     * Demo {@link ViewPager.OnPageChangeListener} class which contains the\n     * necessary calls back to the provided {@link TabLayout} so that the tab position is\n     * kept in sync.\n     * <p>\n     * <p>This class stores the provided TabLayout weakly, meaning that you can use\n     * {@link ViewPager#addOnPageChangeListener(ViewPager.OnPageChangeListener)\n     * addOnPageChangeListener(OnPageChangeListener)} without removing the listener and\n     * not cause a leak.\n     */\n    public static class TabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener {\n        private final WeakReference<ShortTabLayout> mTabLayoutRef;\n        private int mPreviousScrollState;\n        private int mScrollState;\n\n        public TabLayoutOnPageChangeListener(ShortTabLayout tabLayout) {\n            mTabLayoutRef = new WeakReference<>(tabLayout);\n        }\n\n        @Override\n        public void onPageScrollStateChanged(final int state) {\n            mPreviousScrollState = mScrollState;\n            mScrollState = state;\n        }\n\n        @Override\n        public void onPageScrolled(final int position, final float positionOffset,\n                                   final int positionOffsetPixels) {\n            final ShortTabLayout tabLayout = mTabLayoutRef.get();\n            if (tabLayout != null) {\n                // Only update the text selection if we're not settling, or we are settling after\n                // being dragged\n                final boolean updateText = mScrollState != SCROLL_STATE_SETTLING ||\n                        mPreviousScrollState == SCROLL_STATE_DRAGGING;\n                // Update the indicator if we're not settling after being idle. This is caused\n                // from a setCurrentItem() call and will be handled by an animation from\n                // onPageSelected() instead.\n                final boolean updateIndicator = !(mScrollState == SCROLL_STATE_SETTLING\n                        && mPreviousScrollState == SCROLL_STATE_IDLE);\n                tabLayout.setScrollPosition(position, positionOffset, updateText, updateIndicator);\n            }\n        }\n\n        @Override\n        public void onPageSelected(final int position) {\n            final ShortTabLayout tabLayout = mTabLayoutRef.get();\n            if (tabLayout != null && tabLayout.getSelectedTabPosition() != position\n                    && position < tabLayout.getTabCount()) {\n                // Select the tab, only updating the indicator if we're not being dragged/settled\n                // (since onPageScrolled will handle that).\n                final boolean updateIndicator = mScrollState == SCROLL_STATE_IDLE\n                        || (mScrollState == SCROLL_STATE_SETTLING\n                        && mPreviousScrollState == SCROLL_STATE_IDLE);\n                tabLayout.selectTab(tabLayout.getTabAt(position), updateIndicator);\n            }\n        }\n\n        void reset() {\n            mPreviousScrollState = mScrollState = SCROLL_STATE_IDLE;\n        }\n    }\n\n    /**\n     * Demo {@link  OnTabSelectedListener} class which contains the necessary calls back\n     * to the provided {@link ViewPager} so that the tab position is kept in sync.\n     */\n    public static class ViewPagerOnTabSelectedListener implements OnTabSelectedListener {\n        private final ViewPager mViewPager;\n\n        public ViewPagerOnTabSelectedListener(ViewPager viewPager) {\n            mViewPager = viewPager;\n        }\n\n        @Override\n        public void onTabSelected(Tab tab) {\n            mViewPager.setCurrentItem(tab.getPosition());\n        }\n\n        @Override\n        public void onTabUnselected(Tab tab) {\n            // No-op\n        }\n\n        @Override\n        public void onTabReselected(Tab tab) {\n            // No-op\n        }\n    }\n\n    private class PagerAdapterObserver extends DataSetObserver {\n        PagerAdapterObserver() {\n        }\n\n        @Override\n        public void onChanged() {\n            populateFromPagerAdapter();\n        }\n\n        @Override\n        public void onInvalidated() {\n            populateFromPagerAdapter();\n        }\n    }\n\n    private class AdapterChangeListener implements ViewPager.OnAdapterChangeListener {\n        private boolean mAutoRefresh;\n\n        AdapterChangeListener() {\n        }\n\n        @Override\n        public void onAdapterChanged(@NonNull ViewPager viewPager,\n                                     @Nullable PagerAdapter oldAdapter, @Nullable PagerAdapter newAdapter) {\n            if (mViewPager == viewPager) {\n                setPagerAdapter(newAdapter, mAutoRefresh);\n            }\n        }\n\n        void setAutoRefresh(boolean autoRefresh) {\n            mAutoRefresh = autoRefresh;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/tab/TabItem.java",
    "content": "package com.jennifer.andy.simpleeyes.widget.tab;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport androidx.appcompat.widget.TintTypedArray;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport com.jennifer.andy.simpleeyes.R;\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/4 13:49\n * Description:\n */\n\npublic final class TabItem extends View {\n    final CharSequence mText;\n    final Drawable mIcon;\n    final int mCustomLayout;\n\n    public TabItem(Context context) {\n        this(context, null);\n    }\n\n    @SuppressLint(\"RestrictedApi\")\n    public TabItem(Context context, AttributeSet attrs) {\n        super(context, attrs);\n\n        @SuppressLint(\"RestrictedApi\") final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,\n                R.styleable.TabItem);\n        mText = a.getText(R.styleable.TabItem_android_text);\n        mIcon = a.getDrawable(R.styleable.TabItem_android_icon);\n        mCustomLayout = a.getResourceId(R.styleable.TabItem_android_layout, 0);\n        a.recycle();\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/viewpager/InterceptVerticalViewPager.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.viewpager\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.MotionEvent\nimport android.view.ViewConfiguration\nimport androidx.viewpager.widget.ViewPager\nimport kotlin.math.abs\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/6/14 16:03\n * Description:\n * 该ViewPager主要拦截两个情况：\n * 1.拦截向上的滑动\n * 2.拦截固定角标的向左滑动\n *\n */\nclass InterceptVerticalViewPager : ViewPager {\n\n    private var mLastMotionX: Float = 0f\n    private var mLastMotionY: Float = 0f\n\n    lateinit var verticalListener: () -> Unit\n    lateinit var horizontalListener: (Int) -> Unit\n    private var isDoListener: Boolean = false\n\n    var mDisMissIndex = -1\n\n    private val scaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop\n\n    constructor (context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)\n\n\n    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {\n        val action = ev.action and MotionEvent.ACTION_MASK\n        val y = ev.y\n        val x = ev.x\n        when (action) {\n            MotionEvent.ACTION_DOWN -> {\n                mLastMotionX = ev.x\n                mLastMotionY = ev.y\n            }\n            MotionEvent.ACTION_MOVE -> {\n                val dx = mLastMotionX - x\n                val dy = mLastMotionY - y\n                val absX = abs(dx)\n                val absY = abs(dy)\n\n                //向上滑动，且y轴坐标的距离大于x轴滑动距离的一半\n                if (dy > 0 && absY > absX * 0.5f && absY > scaledTouchSlop) {\n                    if (!isDoListener) {\n                        verticalListener()\n                        isDoListener = true\n                        return false\n                    }\n                }\n                //像左滑动，且x轴坐标的距离大于y轴滑动距离的一半\n                if (dx > 0 && absX > absY * 0.5f && absX > scaledTouchSlop) {\n                    if (!isDoListener && mDisMissIndex == currentItem) {\n                        horizontalListener(mDisMissIndex)\n                        isDoListener = true\n                        return false\n                    }\n                }\n                mLastMotionX = x\n                mLastMotionY = y\n            }\n        }\n\n        return super.dispatchTouchEvent(ev)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/jennifer/andy/simpleeyes/widget/viewpager/MarginWithIndicatorViewPager.kt",
    "content": "package com.jennifer.andy.simpleeyes.widget.viewpager\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.FrameLayout\nimport androidx.viewpager.widget.PagerAdapter\nimport androidx.viewpager.widget.ViewPager\nimport com.jennifer.andy.simpleeyes.R\nimport com.jennifer.andy.simpleeyes.entity.Content\nimport com.jennifer.andy.simpleeyes.utils.bindView\nimport com.jennifer.andy.simpleeyes.utils.dip2px\nimport com.jennifer.andy.simpleeyes.widget.CollectionOfHorizontalScrollCardView\nimport com.rd.PageIndicatorView\n\n\n/**\n * Author:  andy.xwt\n * Date:    2018/7/9 17:11\n * Description: 带分割（可以看见前面视图的）与指示器的ViewPager\n */\n\nclass MarginWithIndicatorViewPager : FrameLayout {\n\n    private val mViewPager: ViewPager by bindView(R.id.vp_indicator_pager)\n    private val mIndicator: PageIndicatorView by bindView(R.id.pageIndicatorView)\n\n    private lateinit var mItemList: MutableList<Content>\n    lateinit var pageViewClickListener: (position: Int) -> Unit\n\n    constructor(context: Context) : this(context, null)\n\n    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {\n        LayoutInflater.from(context).inflate(R.layout.layout_margin_with_indicator_pager, this, true)\n    }\n\n    /**\n     * 设置数据\n     */\n    fun setData(itemList: MutableList<Content>) {\n        mItemList = itemList\n        setAdapter()\n    }\n\n    private fun setAdapter() {\n        mViewPager.adapter = MarginWithViewPagerAdapter()\n        mViewPager.offscreenPageLimit = 3\n        mViewPager.pageMargin = context.dip2px(10f)\n        mViewPager.currentItem = 10000 * mItemList.size\n        mIndicator.count = mItemList.size\n        mViewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {\n            override fun onPageScrollStateChanged(state: Int) {\n\n            }\n\n            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {\n                mIndicator.setSelected(position % mItemList.size)\n            }\n\n            override fun onPageSelected(position: Int) {\n            }\n        })\n\n    }\n\n    /**\n     * 设置无限滚动适配器\n     */\n    inner class MarginWithViewPagerAdapter : PagerAdapter() {\n\n        override fun getCount() = Int.MAX_VALUE\n\n        override fun instantiateItem(container: ViewGroup, position: Int): Any {\n            val cardView = CollectionOfHorizontalScrollCardView(this@MarginWithIndicatorViewPager.context)\n            val newPosition = position % mItemList.size\n            cardView.setOnClickListener { pageViewClickListener(newPosition) }//点击响应事件\n            cardView.setData(mItemList[newPosition])\n            container.addView(cardView)\n            return cardView\n        }\n\n        override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {\n            container.removeView(`object` as View?)\n        }\n\n        override fun isViewFromObject(view: View, `object`: Any): Boolean {\n            return view == `object`\n        }\n\n    }\n}"
  },
  {
    "path": "app/src/main/res/anim/bottom_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromYDelta=\"100%p\"\n    android:toYDelta=\"0\"/>"
  },
  {
    "path": "app/src/main/res/anim/bottom_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromYDelta=\"0\"\n    android:toYDelta=\"100%p\"/>"
  },
  {
    "path": "app/src/main/res/anim/fade_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<alpha\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromAlpha=\"0.0\"\n    android:toAlpha=\"1.0\"/>"
  },
  {
    "path": "app/src/main/res/anim/fade_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<alpha\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromAlpha=\"1.0\"\n    android:toAlpha=\"0.0\"/>"
  },
  {
    "path": "app/src/main/res/anim/left_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromXDelta=\"-100%p\"\n    android:toXDelta=\"0\"/>\n"
  },
  {
    "path": "app/src/main/res/anim/left_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromXDelta=\"0\"\n    android:toXDelta=\"-100%p\"/>\n"
  },
  {
    "path": "app/src/main/res/anim/no_anim.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<alpha\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"200\"\n    android:fromAlpha=\"1\"\n    android:toAlpha=\"1\"/>"
  },
  {
    "path": "app/src/main/res/anim/right_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromXDelta=\"100%p\"\n    android:toXDelta=\"0\"/>"
  },
  {
    "path": "app/src/main/res/anim/right_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromXDelta=\"0\"\n    android:toXDelta=\"100%p\"/>"
  },
  {
    "path": "app/src/main/res/anim/scale_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<scale\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromXScale=\"0.0\"\n    android:fromYScale=\"0.0\"\n    android:toXScale=\"1.0\"\n    android:toYScale=\"1.0\"/>"
  },
  {
    "path": "app/src/main/res/anim/scale_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<scale\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromXScale=\"1\"\n    android:fromYScale=\"1\"\n    android:toXScale=\"0\"\n    android:toYScale=\"0\"/>"
  },
  {
    "path": "app/src/main/res/anim/top_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromYDelta=\"-100%p\"\n    android:toYDelta=\"0\"/>"
  },
  {
    "path": "app/src/main/res/anim/top_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"500\"\n    android:fromYDelta=\"0\"\n    android:toYDelta=\"-100%p\"/>"
  },
  {
    "path": "app/src/main/res/color/selector_item_square_text.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:color=\"@color/transparent\"\n        android:state_pressed=\"true\"/>\n\n    <item\n        android:color=\"@color/transparent\"\n        android:state_selected=\"true\"/>\n\n    <item\n        android:color=\"@color/white\"/>\n</selector>"
  },
  {
    "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:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportHeight=\"108.0\"\n    android:viewportWidth=\"108.0\">\n    <path android:fillColor=\"#26A69A\"\n          android:pathData=\"M0,0h108v108h-108z\"\n          android:strokeColor=\"#66FFFFFF\" 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=\"M9,0L9,108\"\n          android:strokeColor=\"#66FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M39,0L39,108\"\n          android:strokeColor=\"#66FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M29,0L29,108\"\n          android:strokeColor=\"#66FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M59,0L59,108\"\n          android:strokeColor=\"#66FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M49,0L49,108\"\n          android:strokeColor=\"#66FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M79,0L79,108\"\n          android:strokeColor=\"#66FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M69,0L69,108\"\n          android:strokeColor=\"#66FFFFFF\" 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=\"#66FFFFFF\" 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=\"#66FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,69L108,69\"\n          android:strokeColor=\"#66FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,79L108,79\"\n          android:strokeColor=\"#66FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,49L108,49\"\n          android:strokeColor=\"#66FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,59L108,59\"\n          android:strokeColor=\"#66FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,29L108,29\"\n          android:strokeColor=\"#66FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,39L108,39\"\n          android:strokeColor=\"#66FFFFFF\" 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,9L108,9\"\n          android:strokeColor=\"#66FFFFFF\" android:strokeWidth=\"0.8\"/>\n</vector>\n\n"
  },
  {
    "path": "app/src/main/res/drawable/seek_bar_layer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <!--背景进度条-->\n    <item android:id=\"@android:id/background\">\n        <shape>\n            <solid android:color=\"#73ffffff\"/>\n        </shape>\n    </item>\n    <!--第二进度条-->\n    <item android:id=\"@android:id/secondaryProgress\">\n        <clip>\n            <shape>\n                <solid android:color=\"#f2ffffff\"/>\n            </shape>\n        </clip>\n    </item>\n\n    <!--第一进度条-->\n    <item android:id=\"@android:id/progress\">\n        <clip>\n            <shape>\n                <solid android:color=\"#4B86C6\"/>\n            </shape>\n        </clip>\n    </item>\n</layer-list>\n\n"
  },
  {
    "path": "app/src/main/res/drawable/selector_checkbox_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_selected=\"true\" android:drawable=\"@drawable/checkbox_bg_checked\"/>\n    <item android:state_checked=\"true\" android:drawable=\"@drawable/checkbox_bg_unchecked\"/>\n    <item android:drawable=\"@drawable/checkbox_bg_unchecked\"/>\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/selector_item_square_foreground.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:drawable=\"@color/transparent\"\n        android:state_pressed=\"true\"/>\n\n    <item\n        android:drawable=\"@color/transparent\"\n        android:state_selected=\"true\"/>\n\n    <item\n        android:drawable=\"@color/black_alpha_33\"/>\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/shape_black_border.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <stroke android:width=\"1dp\" android:color=\"@color/black\"/>\n    <corners android:radius=\"3dp\"/>\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_border_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item>\n\n        <shape>\n            <solid android:color=\"@color/divider\"/>\n        </shape>\n    </item>\n\n    <item\n        android:bottom=\"1px\">\n        <shape>\n            <solid android:color=\"@color/white\"/>\n        </shape>\n    </item>\n</layer-list>"
  },
  {
    "path": "app/src/main/res/drawable/shape_border_bottom_top.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item>\n\n        <shape>\n            <solid android:color=\"@color/divider\"/>\n        </shape>\n    </item>\n\n    <item\n        android:bottom=\"1px\"\n        android:top=\"1px\">\n        <shape>\n            <solid android:color=\"@color/white\"/>\n        </shape>\n    </item>\n</layer-list>"
  },
  {
    "path": "app/src/main/res/drawable/shape_hot_search_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners android:radius=\"10dp\"/>\n    <solid android:color=\"@color/gray_EFF0EF\"/>\n\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_indicator_selected.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"oval\">\n\n    <solid\n        android:color=\"@color/white\"/>\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_indicator_unselected.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"oval\">\n\n    <solid\n        android:color=\"@color/gray_BBBBBB\"/>\n\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_share_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners android:radius=\"5dp\"/>\n    <solid android:color=\"@color/black_translucent_70\"/>\n\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_show_all_border.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <solid\n        android:color=\"@color/white_FEFFFE\"/>\n\n    <stroke\n        android:width=\"4dp\"\n        android:color=\"@color/gray_EDEEEC\"/>\n\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_translate_border.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <solid\n        android:color=\"@color/black_alpha_4D\"/>\n\n    <stroke\n        android:width=\"1dp\"\n        android:color=\"@color/gray_828885\"/>\n\n    <corners\n        android:radius=\"4dp\"/>\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_video_detail_placeholder.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n    <solid\n        android:color=\"@color/black_1E1F1D\"/>\n\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_webview_scrollbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid android:color=\"@color/white_translucent_25\"/>\n    <size android:width=\"5dp\" android:height=\"25dp\"/>\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_white_border.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <stroke android:width=\"1dp\" android:color=\"@color/white\"/>\n    <corners android:radius=\"3dp\"/>\n</shape>"
  },
  {
    "path": "app/src/main/res/layout/activity_all_author.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    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/layout_common_toolbar\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n        android:id=\"@+id/multiple_state_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/rv_recycler\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"/>\n\n    </com.jennifer.andy.simpleeyes.widget.state.MultipleStateView>\n\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_all_category.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    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/layout_common_toolbar\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n        android:id=\"@+id/multiple_state_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/rv_recycler\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"/>\n\n    </com.jennifer.andy.simpleeyes.widget.state.MultipleStateView>\n\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_author_tag_detail.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/white\">\n\n    <com.jennifer.andy.simpleeyes.widget.StickyNavLayout\n        android:id=\"@+id/stick_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <include\n            android:id=\"@+id/id_sticky_nav_layout_top_view\"\n            layout=\"@layout/layout_author_tag_detail_header\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.tab.ShortTabLayout\n            android:id=\"@+id/id_sticky_nav_layout_nav_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@color/white\"\n            app:tabIndicatorColor=\"@color/black\"\n            app:tabIndicatorHeight=\"3dp\"\n            app:tabSelectedTextColor=\"@color/black\"\n            app:tabTextAppearance=\"@style/TabAppearance\"\n            app:tabTextColor=\"@color/black_alpha_4D\"/>\n\n        <androidx.viewpager.widget.ViewPager\n            android:id=\"@+id/id_sticky_nav_layout_viewpager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"/>\n\n    </com.jennifer.andy.simpleeyes.widget.StickyNavLayout>\n\n    <include layout=\"@layout/layout_left_title_share_toolbar\"/>\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_category_tab.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"280dp\">\n\n        <com.facebook.drawee.view.SimpleDraweeView\n            android:id=\"@+id/iv_image\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            app:actualImageScaleType=\"centerCrop\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_sub_title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginTop=\"80dp\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"20sp\"\n            app:font_name=\"bold\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_desc\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/tv_sub_title\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginTop=\"10dp\"\n            android:letterSpacing=\"0.4\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"12sp\"\n            app:font_name=\"bold\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_follow\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/tv_desc\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginTop=\"50dp\"\n            android:background=\"@drawable/shape_white_border\"\n            android:gravity=\"center\"\n            android:letterSpacing=\"0.2\"\n            android:paddingLeft=\"10dp\"\n            android:paddingTop=\"5dp\"\n            android:paddingRight=\"10dp\"\n            android:paddingBottom=\"5dp\"\n            android:text=\"@string/add_follow\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"12sp\"\n            app:font_name=\"normal\"/>\n    </RelativeLayout>\n\n    <include layout=\"@layout/layout_category_tab_toolbar\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.StickyNavLayout\n        android:id=\"@+id/stick_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <RelativeLayout\n            android:id=\"@+id/id_sticky_nav_layout_top_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"280dp\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.tab.ShortTabLayout\n            android:id=\"@+id/id_sticky_nav_layout_nav_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@color/white\"\n            app:tabIndicatorColor=\"@color/black\"\n            app:tabIndicatorHeight=\"3dp\"\n            app:tabSelectedTextColor=\"@color/black\"\n            app:tabTextAppearance=\"@style/TabAppearance\"\n            app:tabTextColor=\"@color/black_alpha_4D\"/>\n\n        <androidx.viewpager.widget.ViewPager\n            android:id=\"@+id/id_sticky_nav_layout_viewpager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"/>\n\n    </com.jennifer.andy.simpleeyes.widget.StickyNavLayout>\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_common_recyclerview.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:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <include\n        layout=\"@layout/layout_common_toolbar\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"/>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rv_recycler\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintHorizontal_bias=\"0.0\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/tool_bar\"\n        app:layout_constraintVertical_bias=\"0.0\"/>\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_daily_elite.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/normal_title_height\"\n        android:background=\"@drawable/shape_border_bottom_top\">\n\n        <ImageView\n            android:id=\"@+id/iv_back\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:src=\"@drawable/ic_action_back_black\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:text=\"@string/daily_editor_elite\"\n            android:textSize=\"16sp\"\n            app:font_name=\"bold\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_date\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginEnd=\"16dp\"\n            android:drawableEnd=\"@drawable/ic_action_pull_down_indicator\"\n            android:text=\"Today\"\n            android:textSize=\"14sp\"\n            app:font_name=\"lobster\"/>\n    </RelativeLayout>\n\n    <com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n        android:id=\"@+id/multiple_state_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n\n        <com.jennifer.andy.simpleeyes.widget.pull.refresh.PullToRefreshRecyclerView\n            android:id=\"@+id/rv_recycler\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"/>\n\n    </com.jennifer.andy.simpleeyes.widget.state.MultipleStateView>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_landing.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/fl_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"/>\n\n"
  },
  {
    "path": "app/src/main/res/layout/activity_login.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/bg_account_activity\">\n\n    <ImageView\n        android:id=\"@+id/iv_close\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:src=\"@drawable/action_close_white\"/>\n\n    <ImageView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_above=\"@id/tv_notice\"\n        android:layout_centerHorizontal=\"true\"\n        android:src=\"@drawable/ic_account_login_header\"/>\n\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_notice\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_above=\"@id/ll_login_container\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_marginTop=\"20dp\"\n        android:layout_marginBottom=\"20dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/login_notice\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"12sp\"\n        app:font_name=\"normal\"/>\n\n    <LinearLayout\n        android:id=\"@+id/ll_login_container\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\">\n\n        <ImageView\n            android:id=\"@+id/iv_weibo\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"20dp\"\n            android:layout_marginRight=\"20dp\"\n            android:src=\"@drawable/ic_account_login_weibo\"/>\n        <ImageView\n            android:id=\"@+id/iv_wechat\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"20dp\"\n            android:layout_marginRight=\"20dp\"\n            android:src=\"@drawable/ic_account_login_wechat\"/>\n        <ImageView\n            android:id=\"@+id/iv_qq\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"20dp\"\n            android:layout_marginRight=\"20dp\"\n            android:src=\"@drawable/ic_account_login_qq\"/>\n    </LinearLayout>\n\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:background=\"@color/black_translucent_90\"\n        android:gravity=\"center\"\n        android:text=\"@string/user_agreement\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"12sp\"\n        app:font_name=\"normal\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    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    <FrameLayout\n        android:id=\"@+id/fl_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"/>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"@color/divider\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.BottomBar\n        android:id=\"@+id/bottom_navigation_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n    />\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_rank.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n\n    <include layout=\"@layout/layout_common_toolbar\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n        android:id=\"@+id/multiple_state_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <com.jennifer.andy.simpleeyes.widget.tab.ShortTabLayout\n            android:id=\"@+id/tab_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@color/white\"\n            android:visibility=\"gone\"\n            app:tabIndicatorColor=\"@color/black\"\n            app:tabIndicatorHeight=\"3dp\"\n            app:tabSelectedTextColor=\"@color/black\"\n            app:tabTextAppearance=\"@style/TabAppearance\"\n            app:tabTextColor=\"@color/black_alpha_4D\"/>\n\n        <androidx.viewpager.widget.ViewPager\n            android:id=\"@+id/view_pager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_below=\"@id/tab_layout\"/>\n\n\n    </com.jennifer.andy.simpleeyes.widget.state.MultipleStateView>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_tag.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <include layout=\"@layout/layout_center_title_share_toolbar\"/>\n\n\n    <com.jennifer.andy.simpleeyes.widget.tab.ShortTabLayout\n        android:id=\"@+id/tab_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/shape_border_bottom\"\n        app:tabIndicatorColor=\"@color/black\"\n        app:tabIndicatorHeight=\"3dp\"\n        app:tabSelectedTextColor=\"@color/black\"\n        app:tabTextAppearance=\"@style/TabAppearance\"\n        app:tabTextColor=\"@color/black_alpha_4D\">\n    </com.jennifer.andy.simpleeyes.widget.tab.ShortTabLayout>\n\n\n    <androidx.viewpager.widget.ViewPager\n        android:id=\"@+id/view_pager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_below=\"@id/tab_layout\"/>\n\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_topic.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    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/layout_common_toolbar\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n        android:id=\"@+id/multiple_state_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/rv_recycler\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"/>\n    </com.jennifer.andy.simpleeyes.widget.state.MultipleStateView>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_video_detail.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\">\n\n            <RelativeLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:layout_marginTop=\"200dp\">\n\n                <com.facebook.drawee.view.SimpleDraweeView\n                    android:id=\"@+id/iv_blurred\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"match_parent\"\n                    app:actualImageScaleType=\"focusCrop\"\n                    app:failureImage=\"@drawable/shape_video_detail_placeholder\"\n                    app:placeholderImage=\"@drawable/shape_video_detail_placeholder\"\n                    app:placeholderImageScaleType=\"focusCrop\" />\n\n                <ImageView\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"match_parent\"\n                    android:background=\"@drawable/video_player_bg_color\" />\n\n                <androidx.recyclerview.widget.RecyclerView\n                    android:id=\"@+id/rv_video_recycler\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"match_parent\" />\n\n            </RelativeLayout>\n\n        </RelativeLayout>\n\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"220dp\">\n\n\n            <RelativeLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"200dp\">\n\n                <com.jennifer.andy.simpleeyes.player.IjkVideoViewWrapper\n                    android:id=\"@+id/video_view_wrapper\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"match_parent\" />\n            </RelativeLayout>\n\n            <FrameLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"20dp\"\n                android:layout_alignParentBottom=\"true\"\n                android:layout_marginBottom=\"11dp\">\n\n                <SeekBar\n                    android:id=\"@+id/seek_bar\"\n                    style=\"@style/ProgressBarStyle\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"center_vertical\"\n                    android:background=\"@color/black_translucent_80\"\n                    android:max=\"1000\"\n                    android:paddingStart=\"0dp\"\n                    android:paddingEnd=\"0dp\" />\n            </FrameLayout>\n\n        </RelativeLayout>\n\n\n    </FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_videoinfo_by_id.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/black\"\n    android:orientation=\"vertical\">\n\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_webview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    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/layout_center_title_share_toolbar\"/>\n\n    <WebView\n        android:id=\"@+id/web_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:scrollbarThumbVertical=\"@drawable/shape_webview_scrollbar\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_light_controller.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/ll_light_container\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:layout_alignParentEnd=\"true\"\n    android:layout_centerVertical=\"true\"\n    android:orientation=\"vertical\">\n\n    <ImageView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:src=\"@drawable/action_light_up\"/>\n\n\n    <com.jennifer.andy.simpleeyes.widget.VerticalProgressBar\n        android:id=\"@+id/pb_light_progress\"\n        style=\"@style/ProgressBarStyle\"\n        android:layout_width=\"100dp\"\n        android:layout_height=\"3dp\"\n        android:layout_gravity=\"center_horizontal\"/>\n\n    <ImageView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:src=\"@drawable/action_light_down\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_volume_controller.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/ll_volume_container\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:layout_alignParentStart=\"true\"\n    android:layout_centerVertical=\"true\"\n    android:orientation=\"vertical\">\n\n    <ImageView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:src=\"@drawable/action_volume_up\"/>\n\n\n    <com.jennifer.andy.simpleeyes.widget.VerticalProgressBar\n        android:id=\"@+id/pb_volume_progress\"\n        style=\"@style/ProgressBarStyle\"\n        android:layout_width=\"120dp\"\n        android:layout_height=\"3dp\"\n        android:layout_gravity=\"center_horizontal\"/>\n\n    <ImageView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:src=\"@drawable/action_volume_down\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/empty_search_word.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_no_find\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:text=\"@string/not_find_match_content\"\n        android:textSize=\"12sp\"\n        app:font_name=\"normal\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/tv_no_find\"\n        android:layout_centerInParent=\"true\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/remind_find_in_group\"\n        android:textColor=\"@color/SecondaryText\"\n        android:textSize=\"12sp\"\n        app:font_name=\"normal\"/>\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_cache.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <ImageView\n        android:id=\"@+id/iv_back\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentStart=\"true\"\n        android:src=\"@drawable/ic_action_back_black\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignTop=\"@id/iv_back\"\n        android:layout_alignBottom=\"@id/iv_back\"\n        android:layout_toStartOf=\"@id/tv_editor\"\n        android:layout_toEndOf=\"@id/iv_back\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center\"\n        android:maxLines=\"1\"\n        android:text=\"@string/mine_cache\"\n        android:textSize=\"16sp\"\n        app:font_name=\"bold\"/>\n\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_editor\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignTop=\"@id/iv_back\"\n        android:layout_alignBottom=\"@id/iv_back\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_marginRight=\"16dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/edit\"\n        android:textColor=\"@color/SecondaryText\"\n        android:textSize=\"12sp\"\n        app:font_name=\"normal\"/>\n\n    <View\n        android:id=\"@+id/line1\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:layout_below=\"@id/iv_back\"\n        android:background=\"@color/divider\"/>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rv_cache_recycler\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_below=\"@+id/line1\"/>\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_feed.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/white\"\n    android:orientation=\"vertical\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/normal_title_height\">\n\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_all_category\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginStart=\"16dp\"\n            android:src=\"@drawable/ic_action_back_black\"\n            android:text=\"@string/all_category\"\n            app:font_name=\"normal\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:text=\"@string/discover_en\"\n            android:textSize=\"20sp\"\n            app:font_name=\"lobster\"/>\n\n\n        <ImageView\n            android:id=\"@+id/iv_search\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:src=\"@drawable/ic_action_search_black\"/>\n\n    </RelativeLayout>\n\n\n    <com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n        android:id=\"@+id/multiple_state_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <com.jennifer.andy.simpleeyes.widget.tab.ShortTabLayout\n            android:id=\"@+id/tab_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/shape_border_bottom\"\n            app:tabIndicatorColor=\"@color/black\"\n            app:tabIndicatorHeight=\"3dp\"\n            app:tabSelectedTextColor=\"@color/black\"\n            app:tabTextAppearance=\"@style/TabAppearance\"\n            app:tabTextColor=\"@color/black_alpha_4D\">\n        </com.jennifer.andy.simpleeyes.widget.tab.ShortTabLayout>\n\n\n        <androidx.viewpager.widget.ViewPager\n            android:id=\"@+id/view_pager\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_below=\"@id/tab_layout\"/>\n\n\n    </com.jennifer.andy.simpleeyes.widget.state.MultipleStateView>\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_follow.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/normal_title_height\"\n        android:background=\"@color/white\">\n\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_all_author\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginStart=\"16dp\"\n            android:src=\"@drawable/ic_action_back_black\"\n            android:text=\"@string/all_author\"\n            app:font_name=\"normal\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:text=\"@string/Subscription_en\"\n            android:textSize=\"20sp\"\n            app:font_name=\"lobster\"/>\n\n\n        <ImageView\n            android:id=\"@+id/iv_search\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:src=\"@drawable/ic_action_search_black\"/>\n\n    </RelativeLayout>\n\n\n    <com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n        android:id=\"@+id/multiple_state_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <com.jennifer.andy.simpleeyes.widget.pull.refresh.PullToRefreshRecyclerView\n            android:id=\"@+id/rv_follow_recycler\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"/>\n\n\n    </com.jennifer.andy.simpleeyes.widget.state.MultipleStateView>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_home.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    <com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n        android:id=\"@+id/multiple_state_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n\n        <com.jennifer.andy.simpleeyes.widget.pull.zoom.PullToZoomRecyclerView\n            android:id=\"@+id/rv_home_recycler\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"/>\n\n    </com.jennifer.andy.simpleeyes.widget.state.MultipleStateView>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_local_coomon_landing.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/iv_background\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:backgroundImage=\"@drawable/landing_background\"/>\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_above=\"@+id/ll_ad_container\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginBottom=\"100dp\"\n            android:letterSpacing=\"0.3\"\n            android:text=\"@string/open_eyes_chinese\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"20sp\"\n            app:font_name=\"normal\"/>\n\n\n        <LinearLayout\n            android:id=\"@+id/ll_ad_container\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentBottom=\"true\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginBottom=\"40dp\"\n            android:orientation=\"vertical\">\n\n            <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n                android:id=\"@+id/tv_ad_english\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/ad_english_message\"\n                android:textColor=\"@color/gray_B7B9B8\"\n                android:textSize=\"14sp\"\n                app:font_name=\"lobster\"/>\n\n\n            <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_horizontal\"\n                android:layout_marginTop=\"10dp\"\n                android:text=\"@string/ad_chinese_message\"\n                android:textColor=\"@color/gray_B7B9B8\"\n                android:textSize=\"14sp\"\n                app:font_name=\"normal\"/>\n\n        </LinearLayout>\n\n    </RelativeLayout>\n\n    <RelativeLayout\n        android:id=\"@+id/rl_loading_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <RelativeLayout\n            android:id=\"@+id/ll_move_container\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\">\n\n            <RelativeLayout\n                android:id=\"@+id/ll_eye_container\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\">\n\n                <ImageView\n                    android:id=\"@+id/iv_head_outer\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_centerInParent=\"true\"\n                    android:src=\"@drawable/ic_eye_white\"/>\n\n                <ImageView\n                    android:id=\"@+id/iv_head_inner\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_centerInParent=\"true\"/>\n\n            </RelativeLayout>\n\n            <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n                android:id=\"@+id/tv_name\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_below=\"@id/ll_eye_container\"\n                android:layout_centerHorizontal=\"true\"\n                android:text=\"@string/open_eyes_english\"\n                android:textColor=\"@color/white\"\n                android:textSize=\"24sp\"\n                app:font_name=\"lobster\"/>\n\n        </RelativeLayout>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_today\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:gravity=\"center\"\n            android:text=\"@string/for_today\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"24sp\"\n            android:visibility=\"invisible\"\n            app:font_name=\"lobster\"/>\n\n        <LinearLayout\n            android:id=\"@+id/ll_today\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentBottom=\"true\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginBottom=\"40dp\"\n            android:orientation=\"vertical\">\n\n            <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n                android:id=\"@+id/tv_date\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginBottom=\"20dp\"\n                android:textColor=\"@color/gray_B7B9B8\"\n                android:textSize=\"14sp\"\n                android:visibility=\"invisible\"\n                app:font_name=\"normal\"/>\n\n\n            <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n                android:id=\"@+id/tv_today_chose\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_horizontal\"\n                android:layout_marginTop=\"10dp\"\n                android:letterSpacing=\"0.3\"\n                android:text=\"@string/today_chose\"\n                android:textColor=\"@color/gray_B7B9B8\"\n                android:textSize=\"14sp\"\n                android:visibility=\"invisible\"\n                app:font_name=\"normal\"/>\n\n        </LinearLayout>\n\n\n    </RelativeLayout>\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_profile.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/white\"\n    android:orientation=\"vertical\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <ImageView\n            android:id=\"@+id/ic_more\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:src=\"@drawable/ic_menu_more\"/>\n\n        <com.facebook.drawee.view.SimpleDraweeView\n            android:id=\"@+id/iv_avatar\"\n            android:layout_width=\"70dp\"\n            android:layout_height=\"70dp\"\n            android:layout_below=\"@+id/ic_more\"\n            android:layout_centerHorizontal=\"true\"\n            app:placeholderImage=\"@drawable/ic_default_avatar\"\n            app:roundAsCircle=\"true\"\n            app:roundingBorderColor=\"@color/gray_BBBBBB\"\n            app:roundingBorderWidth=\"1dp\"/>\n    </RelativeLayout>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_click_login\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"20dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/click_login\"\n        android:textColor=\"@color/black_444444\"\n    />\n\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"50dp\"\n        android:layout_marginTop=\"20dp\"\n        android:background=\"@drawable/shape_border_bottom\"\n        android:orientation=\"horizontal\">\n\n        <FrameLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"1\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:drawableStart=\"@drawable/ic_action_favorites_grey_without_padding\"\n                android:drawablePadding=\"10dp\"\n                android:text=\"@string/favorites\"/>\n\n\n        </FrameLayout>\n\n        <View\n            android:layout_width=\"1dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginTop=\"10dp\"\n            android:layout_marginBottom=\"10dp\"\n            android:background=\"@color/divider\"/>\n\n        <FrameLayout\n            android:id=\"@+id/fl_comment_container\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"1\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:drawableStart=\"@drawable/ic_action_reply_grey_without_padding\"\n                android:drawablePadding=\"10dp\"\n                android:text=\"@string/commit\"/>\n\n        </FrameLayout>\n\n    </LinearLayout>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rv_profile_recycler\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_marginTop=\"10dp\"\n        android:layout_weight=\"1\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"@dimen/default_margin\"\n        android:gravity=\"center\"\n        android:text=\"@string/version\"\n        android:textColor=\"@color/gray_BBBBBB\"\n        android:textSize=\"11sp\"\n        app:font_name=\"normal\"/>\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_search_hot.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/white\"\n    android:orientation=\"vertical\">\n\n    <LinearLayout\n        android:id=\"@+id/rl_search_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\"\n        android:padding=\"10dp\">\n\n        <androidx.appcompat.widget.SearchView\n            android:id=\"@+id/searchView\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"30dp\"\n            android:layout_weight=\"1\"\n            android:background=\"@drawable/shape_hot_search_bg\"\n            app:closeIcon=\"@drawable/ic_action_cancel_grey\"\n            app:queryBackground=\"@color/transparent\"\n            app:queryHint=\"@string/find_interesting_video\"\n            app:searchHintIcon=\"@null\"\n            app:searchIcon=\"@null\"\n            app:submitBackground=\"@color/transparent\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_cancel\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_vertical\"\n            android:layout_marginStart=\"10dp\"\n            android:text=\"@string/cancel\"\n            app:font_name=\"normal\"/>\n\n    </LinearLayout>\n\n    <com.jennifer.andy.simpleeyes.widget.state.MultipleStateView\n        android:id=\"@+id/multiple_state_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <com.jennifer.andy.simpleeyes.widget.SearchHotRemindView\n            android:id=\"@+id/rl_search_remind\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"/>\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/rv_search_recycler\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_below=\"@+id/rl_search_remind\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginTop=\"10dp\"\n        />\n\n    </com.jennifer.andy.simpleeyes.widget.state.MultipleStateView>\n\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_slogan.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_tag_detail_info.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rv_recycler\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"/>\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_video_landing.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.jennifer.andy.simpleeyes.widget.FullScreenVideoView\n        android:id=\"@+id/video_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"/>\n\n    <ImageView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_marginTop=\"220dp\"\n        android:src=\"@drawable/ic_account_login_header\"/>\n\n\n    <com.jennifer.andy.simpleeyes.widget.viewpager.InterceptVerticalViewPager\n        android:id=\"@+id/view_pager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"/>\n\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_marginBottom=\"40dp\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"vertical\">\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTypeWriterTextView\n            android:id=\"@+id/tv_slogan_zh\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:gravity=\"center_horizontal\"\n            android:textColor=\"@color/white\"\n            app:font_name=\"bold\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTypeWriterTextView\n            android:id=\"@+id/tv_slogan_en\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:gravity=\"center_horizontal\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"16sp\"\n            app:font_name=\"lobster\"/>\n\n        <com.rd.PageIndicatorView\n            android:id=\"@+id/pageIndicatorView\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"20dp\"\n            android:layout_marginTop=\"30dp\"\n            app:piv_animationType=\"color\"\n            app:piv_interactiveAnimation=\"true\"\n            app:piv_padding=\"10dp\"\n            app:piv_radius=\"3dp\"\n            app:piv_selectedColor=\"@color/white\"\n            app:piv_unselectedColor=\"@color/white_translucent_65\"/>\n\n        <ImageView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:src=\"@drawable/ic_top_arrow_double\"/>\n    </LinearLayout>\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_collection_brief.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    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_marginStart=\"5dp\"\n    android:orientation=\"vertical\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/iv_image\"\n        android:layout_width=\"250dp\"\n        android:layout_height=\"150dp\"\n        app:actualImageScaleType=\"centerCrop\"\n    />\n\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"10dp\"\n        android:ellipsize=\"end\"\n        android:maxLines=\"1\"\n        android:maxWidth=\"250dp\"\n        android:textColor=\"@color/black\"\n        app:font_name=\"bold\"/>\n\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_desc\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"5dp\"\n        android:ellipsize=\"end\"\n        android:maxLines=\"1\"\n        android:textColor=\"@color/SecondaryText\"\n        android:textSize=\"12sp\"\n        app:font_name=\"normal\"/>\n\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_collection_card_cover.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    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_marginLeft=\"2dp\"\n    android:layout_marginRight=\"2dp\"\n    android:background=\"@color/white\"\n    android:orientation=\"vertical\">\n\n\n    <RelativeLayout\n        android:id=\"@+id/ll_container\"\n        android:layout_width=\"250dp\"\n        android:layout_height=\"wrap_content\">\n\n        <com.facebook.drawee.view.SimpleDraweeView\n            android:id=\"@+id/iv_image\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"150dp\"\n        />\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_translate\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_marginEnd=\"@dimen/default_margin_right\"\n            android:layout_marginTop=\"@dimen/default_margin_top\"\n            android:background=\"@drawable/shape_translate_border\"\n            android:padding=\"3dp\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"10sp\"\n            android:textStyle=\"bold\"\n            android:visibility=\"gone\"\n            app:font_name=\"bold\"/>\n\n\n        <ImageView\n            android:id=\"@+id/iv_daily\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignBottom=\"@+id/iv_image\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_marginBottom=\"12dp\"\n            android:layout_marginEnd=\"15dp\"\n            android:src=\"@drawable/daily_label\"\n            android:visibility=\"gone\"/>\n\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/iv_image\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginTop=\"10dp\"\n            android:orientation=\"vertical\">\n\n            <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n                android:id=\"@+id/tv_title\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:ellipsize=\"end\"\n                android:maxLines=\"1\"\n                android:textColor=\"@color/black\"\n                app:font_name=\"bold\"/>\n\n\n            <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n                android:id=\"@+id/tv_desc\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"5dp\"\n                android:textColor=\"@color/SecondaryText\"\n                android:textSize=\"12sp\"\n                app:font_name=\"normal\"/>\n        </LinearLayout>\n    </RelativeLayout>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_show_all\"\n        android:layout_width=\"150dp\"\n        android:layout_height=\"150dp\"\n        android:background=\"@drawable/shape_show_all_border\"\n        android:gravity=\"center\"\n        android:text=\"@string/show_all\"\n        android:visibility=\"gone\"\n        app:font_name=\"normal\"/>\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_collection_of_horizontal_scroll_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <com.jennifer.andy.simpleeyes.widget.ItemHeaderView\n        android:id=\"@+id/item_header_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.viewpager.MarginWithIndicatorViewPager\n        android:id=\"@+id/view_pager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_hot_search.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/tv_text\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:layout_margin=\"5dp\"\n    android:background=\"@color/gray_B7B9B8\"\n    android:letterSpacing=\"0.3\"\n    android:padding=\"5dp\"\n    android:textColor=\"@color/white\"\n    android:textSize=\"13sp\"\n    app:font_name=\"normal\"/>\n\n"
  },
  {
    "path": "app/src/main/res/layout/item_profile_setting.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"80dp\"\n        android:gravity=\"center\"\n        android:textColor=\"@color/black_444444\"/>\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_square_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n\n    <FrameLayout\n        android:id=\"@+id/fl_cover\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <com.facebook.drawee.view.SimpleDraweeView\n            android:id=\"@+id/iv_simple_image\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"180dp\"\n            app:actualImageScaleType=\"focusCrop\"\n            android:foreground=\"@drawable/selector_item_square_foreground\"\n            app:viewAspectRatio=\"1.33\"/>\n\n    </FrameLayout>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:textColor=\"@color/selector_item_square_text\"\n        android:textSize=\"14sp\"\n        app:font_name=\"bold\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_square_collection.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    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_marginEnd=\"2dp\"\n    android:layout_marginStart=\"2dp\">\n\n\n    <FrameLayout\n        android:id=\"@+id/fl_cover\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <com.facebook.drawee.view.SimpleDraweeView\n            android:id=\"@+id/iv_simple_image\"\n            android:layout_width=\"160dp\"\n            android:layout_height=\"160dp\"\n            app:actualImageScaleType=\"focusCrop\"\n            app:viewAspectRatio=\"1.33\"/>\n\n    </FrameLayout>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:textColor=\"@color/selector_item_square_text\"\n        android:textSize=\"14sp\"\n        app:font_name=\"bold\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_the_end.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/rl_load_end_view\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"100dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/no_more_string\"\n        android:textColor=\"@color/white\"\n        app:font_name=\"lobster\"/>\n\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/item_video_samll_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"100dp\"\n    android:paddingLeft=\"@dimen/default_padding\"\n    android:paddingRight=\"@dimen/default_padding\"\n    android:paddingTop=\"@dimen/default_padding\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/iv_image\"\n        android:layout_width=\"130dp\"\n        android:layout_height=\"70dp\"\n        app:actualImageScaleType=\"centerCrop\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"10dp\"\n        android:layout_marginTop=\"10dp\"\n        android:layout_toEndOf=\"@+id/iv_image\"\n        android:textColor=\"@color/white\"\n        app:font_name=\"bold\"/>\n\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_time\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignStart=\"@+id/tv_title\"\n        android:layout_below=\"@+id/tv_title\"\n        android:layout_marginTop=\"15dp\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"11sp\"/>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_alignParentBottom=\"true\"\n        android:background=\"@color/divider\"/>\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_video_text_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center_vertical\"\n    android:paddingStart=\"@dimen/default_padding\"\n    android:paddingTop=\"@dimen/default_padding\">\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_text\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:drawableEnd=\"@drawable/ic_action_right_arrow\"\n        android:gravity=\"center_vertical\"\n        android:textColor=\"@color/white\"\n    />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_author_tag_detail_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\"\n    android:padding=\"20dp\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/iv_head\"\n        android:layout_width=\"80dp\"\n        android:layout_height=\"80dp\"\n        android:layout_gravity=\"center_horizontal\"\n        app:roundAsCircle=\"true\"\n        app:roundingBorderColor=\"@color/gray_B7B9B8\"\n        app:roundingBorderWidth=\"2dp\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_name\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center\"\n        android:textSize=\"14sp\"\n        app:font_name=\"bold\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_brief\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center\"\n        android:textColor=\"@color/SecondaryText\"\n        android:textSize=\"12sp\"\n        app:font_name=\"normal\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_focus\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_marginTop=\"10dp\"\n        android:background=\"@drawable/shape_black_border\"\n        android:gravity=\"center\"\n        android:letterSpacing=\"0.2\"\n        android:paddingLeft=\"15dp\"\n        android:paddingTop=\"5dp\"\n        android:paddingRight=\"15dp\"\n        android:paddingBottom=\"5dp\"\n        android:text=\"@string/add_follow\"\n        android:textSize=\"12sp\"\n        app:font_name=\"bold\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_desc\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"20dp\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center\"\n        android:maxLines=\"2\"\n        android:textColor=\"@color/SecondaryText\"\n        android:textSize=\"11sp\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_blank_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<View\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/view\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@color/white\">\n\n</View>"
  },
  {
    "path": "app/src/main/res/layout/layout_bottom_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    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:orientation=\"vertical\"\n    android:padding=\"2dp\">\n\n    <ImageView\n        android:id=\"@+id/iv_image\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:src=\"@drawable/ic_tab_strip_icon_feed\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"5dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/discover\"\n        android:textSize=\"8sp\"\n        app:font_name=\"normal\"/>\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_brife_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@color/white\"\n    android:padding=\"@dimen/default_margin_left\">\n\n    <com.jennifer.andy.simpleeyes.widget.ItemHeaderView\n        android:id=\"@+id/item_header_view\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"/>\n\n    <View\n        android:id=\"@+id/view_line\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_below=\"@id/item_header_view\"\n        android:layout_marginTop=\"20dp\"\n        android:background=\"@color/divider\"/>\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_card_banner.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.facebook.drawee.view.SimpleDraweeView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/iv_image\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/default_card_height\"\n/>\n"
  },
  {
    "path": "app/src/main/res/layout/layout_category_head_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:layout_marginBottom=\"5dp\">\n\n\n    <!--这里加前景是为了处理，有些图片背景是白色的情况-->\n    <com.youth.banner.Banner\n        android:id=\"@+id/head_banner\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_above=\"@id/rl_more_container\"\n        android:foreground=\"@color/black_alpha_33\"\n        app:image_scale_type=\"center_crop\"\n        app:indicator_drawable_selected=\"@drawable/shape_indicator_selected\"\n        app:indicator_drawable_unselected=\"@drawable/shape_indicator_unselected\"\n        app:indicator_height=\"6dp\"\n        app:indicator_margin=\"3dp\"\n        app:indicator_width=\"6dp\"/>\n\n\n    <LinearLayout\n        android:id=\"@+id/ll_text_container\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_above=\"@+id/rl_more_container\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_marginBottom=\"20dp\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"vertical\">\n\n        <ImageView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:src=\"@drawable/ic_category_header\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTypeWriterTextView\n            android:id=\"@+id/tv_title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center_horizontal\"\n            android:maxLines=\"1\"\n            android:textColor=\"@color/white\"\n            app:font_name=\"bold\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTypeWriterTextView\n            android:id=\"@+id/tv_text\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center_horizontal\"\n            android:maxLines=\"1\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"12sp\"\n            app:font_name=\"normal\"/>\n    </LinearLayout>\n\n    <com.jennifer.andy.simpleeyes.widget.pull.head.HeaderRefreshView\n        android:id=\"@+id/head_refresh\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_above=\"@id/rl_more_container\"/>\n\n\n    <ImageView\n        android:id=\"@+id/iv_search\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_marginTop=\"5dp\"\n        android:src=\"@drawable/ic_action_search_white\"/>\n\n\n    <RelativeLayout\n        android:id=\"@+id/rl_more_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"50dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:background=\"@drawable/shape_border_bottom_top\">\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"match_parent\"\n            android:layout_centerInParent=\"true\"\n            android:drawableRight=\"@drawable/ic_action_more_arrow_dark\"\n            android:gravity=\"center_vertical\"\n            android:letterSpacing=\"0.5\"\n            android:text=\"@string/more_andy_elite\"\n            android:textSize=\"12sp\"\n            app:font_name=\"normal\"/>\n\n    </RelativeLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_category_tab_toolbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/tool_bar\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/normal_title_height\">\n\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_centerInParent=\"true\"\n        android:background=\"@color/white\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center\"\n        android:maxLines=\"1\"\n        android:textSize=\"17sp\"\n        app:font_name=\"bold\"/>\n\n    <ImageView\n        android:id=\"@+id/iv_back\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentStart=\"true\"\n        android:src=\"@drawable/ic_action_back_white\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_center_title_share_toolbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/tool_bar\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/normal_title_height\"\n    android:background=\"@drawable/shape_border_bottom\"\n    android:padding=\"1px\">\n\n    <ImageView\n        android:id=\"@+id/iv_back\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentStart=\"true\"\n        android:src=\"@drawable/ic_action_back_black\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"match_parent\"\n        android:layout_centerInParent=\"true\"\n        android:layout_toEndOf=\"@id/iv_back\"\n        android:layout_toStartOf=\"@id/iv_share\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center\"\n        android:maxLines=\"1\"\n        android:textSize=\"16sp\"\n        app:font_name=\"bold\"/>\n\n\n    <ImageView\n        android:id=\"@+id/iv_share\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentEnd=\"true\"\n        android:src=\"@drawable/ic_action_share_grey\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_choiceness.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/default_card_height\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/iv_elite_image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n    />\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_translate\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_marginEnd=\"@dimen/default_margin_right\"\n        android:layout_marginTop=\"@dimen/default_margin_top\"\n        android:background=\"@drawable/shape_translate_border\"\n        android:paddingBottom=\"3dp\"\n        android:paddingLeft=\"6dp\"\n        android:paddingRight=\"6dp\"\n        android:paddingTop=\"3dp\"\n        android:text=\"机器翻译\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"10sp\"\n        android:textStyle=\"bold\"\n        android:visibility=\"gone\"\n        app:font_name=\"bold\"/>\n\n\n    <ImageView\n        android:id=\"@+id/iv_arrow\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:src=\"@drawable/ic_video_collection_with_picture_arrow\"\n        android:visibility=\"gone\"/>\n\n    <ImageView\n        android:id=\"@+id/iv_daily\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_marginBottom=\"@dimen/default_margin_bottom\"\n        android:layout_marginEnd=\"@dimen/default_margin_right\"\n        android:src=\"@drawable/daily_label\"\n    />\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_collection_of_horizontal_scroll_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\"\n>\n\n    <com.jennifer.andy.simpleeyes.widget.EliteImageView\n        android:id=\"@+id/scroll_elite_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/scroll_tv_title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_marginTop=\"15dp\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center\"\n        android:maxLines=\"1\"\n        android:textColor=\"@color/black\"\n        android:textSize=\"14sp\"\n        app:font_name=\"bold\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/scroll_tv_desc\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_marginTop=\"8dp\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center\"\n        android:maxLines=\"1\"\n        android:textColor=\"@color/black_alpha_4D\"\n        android:textSize=\"13sp\"\n        app:font_name=\"normal\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_collection_with_brief.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@color/white\"\n    android:padding=\"5dp\">\n\n\n    <com.jennifer.andy.simpleeyes.widget.ItemHeaderView\n        android:id=\"@+id/item_header_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"/>\n\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rv_recycler\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/item_header_view\"/>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_below=\"@id/rv_recycler\"\n        android:layout_marginTop=\"10dp\"\n        android:background=\"@color/divider\"/>\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_collection_with_cover.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_marginBottom=\"5dp\"\n    android:layout_marginTop=\"5dp\"\n    android:background=\"@color/white\"\n    android:orientation=\"vertical\">\n\n    <com.jennifer.andy.simpleeyes.widget.EliteImageView\n        android:id=\"@+id/iv_image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"/>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rv_collection_cover_recycler\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"10dp\"\n        android:layout_marginTop=\"10dp\"/>\n\n    <include layout=\"@layout/layout_division_line\"/>\n\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_common_text.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/rl_head_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@color/white\"\n    android:minHeight=\"40dp\"\n    android:padding=\"10dp\">\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/iv_source\"\n        android:layout_width=\"@dimen/default_card_source_icon_width\"\n        android:layout_height=\"@dimen/default_card_source_icon_height\"\n        android:layout_centerVertical=\"true\"\n        android:visibility=\"gone\"/>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:layout_toStartOf=\"@id/fl_right_container\"\n        android:layout_toEndOf=\"@id/iv_source\"\n        android:orientation=\"vertical\">\n\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_title\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginEnd=\"10dp\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\"\n            android:textSize=\"15sp\"\n            android:visibility=\"gone\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_desc\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginTop=\"5dp\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\"\n            android:textColor=\"@color/SecondaryText\"\n            android:textSize=\"12sp\"\n            android:visibility=\"gone\"\n            app:font_name=\"normal\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_sub_title\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginTop=\"5dp\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center\"\n            android:maxWidth=\"100dp\"\n            android:maxLines=\"1\"\n            android:textColor=\"@color/SecondaryText\"\n            android:textSize=\"13sp\"\n            android:visibility=\"gone\"\n\n        />\n    </LinearLayout>\n\n    <FrameLayout\n        android:id=\"@+id/fl_right_container\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:layout_marginStart=\"@dimen/default_margin\"\n        android:layout_marginEnd=\"@dimen/default_margin\">\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_focus\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/shape_black_border\"\n            android:gravity=\"center\"\n            android:letterSpacing=\"0.2\"\n            android:padding=\"5dp\"\n            android:text=\"@string/add_follow\"\n            android:textSize=\"11sp\"\n            android:visibility=\"gone\"\n            app:font_name=\"normal\"\n        />\n\n\n        <ImageView\n            android:id=\"@+id/iv_more\"\n            android:layout_width=\"@dimen/default_card_source_icon_width\"\n            android:layout_height=\"@dimen/default_card_source_icon_height\"\n            android:layout_gravity=\"center\"\n            android:src=\"@drawable/ic_action_more_arrow_dark\"\n            android:visibility=\"gone\"/>\n\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_date\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:gravity=\"center\"\n            android:textColor=\"@color/SecondaryText\"\n            android:textSize=\"10sp\"\n            android:visibility=\"gone\"\n            app:font_name=\"normal\"/>\n\n    </FrameLayout>\n\n\n</RelativeLayout>\n\n"
  },
  {
    "path": "app/src/main/res/layout/layout_common_toolbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/tool_bar\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/normal_title_height\"\n    android:background=\"@drawable/shape_border_bottom\"\n    android:padding=\"1px\">\n\n\n    <ImageView\n        android:id=\"@+id/iv_back\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentStart=\"true\"\n        android:src=\"@drawable/ic_action_back_black\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"match_parent\"\n        android:layout_centerInParent=\"true\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center\"\n        android:maxLines=\"1\"\n        android:textSize=\"17sp\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_division_line.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"1px\"\n      android:background=\"@color/divider\"/>\n\n\n"
  },
  {
    "path": "app/src/main/res/layout/layout_follow_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@color/white\"\n    android:orientation=\"vertical\">\n\n    <com.jennifer.andy.simpleeyes.widget.EliteImageView\n        android:id=\"@+id/elite_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:actualImageScaleType=\"focusCrop\"\n        app:viewAspectRatio=\"1.33\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.ItemHeaderView\n        android:id=\"@+id/item_header_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"/>\n\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_margin=\"5dp\"\n        android:background=\"@color/divider\"/>\n\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_horizontal_scroll_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.youth.banner.Banner\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/banner\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/default_card_height\"\n    android:layout_marginBottom=\"8dp\"\n    android:orientation=\"vertical\"/>\n"
  },
  {
    "path": "app/src/main/res/layout/layout_ijk_wrapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <com.jennifer.andy.simpleeyes.player.IjkVideoView\n        android:id=\"@+id/video_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/iv_place_image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n\n    <ProgressBar\n        android:id=\"@+id/progress\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\" />\n\n</merge>"
  },
  {
    "path": "app/src/main/res/layout/layout_left_title_share_toolbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/tool_bar\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/normal_title_height\">\n\n    <ImageView\n        android:id=\"@+id/iv_back\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentStart=\"true\"\n        android:src=\"@drawable/ic_action_back_black\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"match_parent\"\n        android:layout_centerInParent=\"true\"\n        android:layout_toEndOf=\"@id/iv_back\"\n        android:layout_toStartOf=\"@id/iv_share\"\n        android:ellipsize=\"end\"\n        android:gravity=\"left|center_vertical\"\n        android:maxLines=\"1\"\n        android:textSize=\"16sp\"\n        app:font_name=\"bold\"/>\n\n\n    <ImageView\n        android:id=\"@+id/iv_share\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentEnd=\"true\"\n        android:src=\"@drawable/ic_action_share_grey\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_load_more_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"100dp\"\n    android:background=\"@color/white\"\n    android:gravity=\"center\"\n    android:orientation=\"vertical\">\n\n    <LinearLayout\n        android:id=\"@+id/ll_load_more_loading_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:gravity=\"center\"\n        android:orientation=\"horizontal\">\n\n        <ProgressBar\n            android:id=\"@+id/loading_progress\"\n            style=\"?android:attr/progressBarStyleSmall\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginEnd=\"@dimen/dp_4\"/>\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"10dp\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"2\"\n            android:text=\"@string/loading_message\"\n            android:textColor=\"@color/black\"\n            android:textSize=\"12sp\"/>\n\n    </LinearLayout>\n\n\n    <FrameLayout\n        android:id=\"@+id/fl_load_more_load_fail_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n        <TextView\n            android:id=\"@+id/tv_prompt\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:text=\"@string/load_failed\"\n            android:textColor=\"@color/black\"\n            android:textSize=\"12sp\"/>\n\n    </FrameLayout>\n\n    <RelativeLayout\n        android:id=\"@+id/rl_load_end_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:gravity=\"center\"\n            android:text=\"@string/no_more_string\"\n            app:font_name=\"lobster\"/>\n\n    </RelativeLayout>\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_loading_message.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/white\"\n>\n\n    <ImageView\n        android:id=\"@+id/iv_image\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentTop=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_marginTop=\"200dp\"\n        android:background=\"@drawable/ic_eye_black_error\"/>\n\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_message_info\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/iv_image\"\n        android:layout_centerHorizontal=\"true\"\n        android:gravity=\"center\"\n        android:textSize=\"12sp\"\n        app:font_name=\"normal\"\n    />\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_loading_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    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\n    <ImageView\n        android:id=\"@+id/iv_head_outer\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:src=\"@drawable/ic_eye_black_outer\"/>\n\n    <ImageView\n        android:id=\"@+id/iv_head_inner\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:src=\"@drawable/ic_eye_black_inner\"/>\n\n\n    <TextView\n        android:id=\"@+id/tv_loading_msg\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/iv_head_outer\"\n        android:layout_centerInParent=\"true\"\n        android:layout_marginTop=\"10dp\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_margin_with_indicator_pager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@color/white\"\n    android:clipChildren=\"false\"\n    android:orientation=\"vertical\">\n\n\n    <androidx.viewpager.widget.ViewPager\n        android:id=\"@+id/vp_indicator_pager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"280dp\"\n        android:layout_marginLeft=\"20dp\"\n        android:layout_marginRight=\"20dp\"\n        android:clipChildren=\"false\"/>\n\n    <com.rd.PageIndicatorView\n        android:id=\"@+id/pageIndicatorView\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_marginBottom=\"15dp\"\n        app:piv_animationType=\"color\"\n        app:piv_interactiveAnimation=\"true\"\n        app:piv_padding=\"10dp\"\n        app:piv_radius=\"3dp\"\n        app:piv_selectedColor=\"@color/black\"\n        app:piv_unselectedColor=\"@color/black_alpha_33\"/>\n\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_media_controller_full_screen.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/black_translucent_60\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:padding=\"5dp\">\n\n        <ImageView\n            android:id=\"@+id/iv_min_screen\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:src=\"@drawable/ic_action_min_screen\"/>\n\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"40dp\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_toEndOf=\"@+id/iv_min_screen\"\n            android:gravity=\"center_vertical\"\n            android:maxLines=\"1\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"16sp\"\n            app:font_name=\"normal\"/>\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:gravity=\"center_vertical\">\n\n            <ImageView\n                android:id=\"@+id/iv_favorites\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/ic_action_favorites\"/>\n            <ImageView\n                android:id=\"@+id/iv_share\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/ic_action_share\"/>\n            <ImageView\n                android:id=\"@+id/iv_more\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/ic_menu_more_white\"/>\n        </LinearLayout>\n\n\n    </RelativeLayout>\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentTop=\"true\"\n        android:layout_centerHorizontal=\"true\">\n\n        <ImageView\n            android:id=\"@+id/iv_arrow\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"/>\n\n        <TextView\n            android:id=\"@+id/tv_drag_currentTime\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:textColor=\"@color/white\"/>\n\n        <TextView\n            android:id=\"@+id/tv_drag_end_time\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:textColor=\"@color/white\"/>\n\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/iv_previous\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:layout_toStartOf=\"@id/iv_pause\"\n        android:src=\"@drawable/ic_player_previous\"\n        android:visibility=\"gone\"/>\n\n    <ImageView\n        android:id=\"@+id/iv_pause\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:background=\"@null\"\n        android:src=\"@drawable/ic_player_pause\"/>\n\n    <ImageView\n        android:id=\"@+id/iv_next\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:layout_toEndOf=\"@id/iv_pause\"\n        android:src=\"@drawable/ic_player_next\"/>\n\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_above=\"@id/rl_bottom\"\n        android:layout_centerHorizontal=\"true\"\n        android:background=\"@drawable/shape_share_bg\"\n        android:padding=\"10dp\">\n\n\n        <ImageView\n            android:id=\"@+id/iv_share_wechat\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginEnd=\"10dp\"\n            android:src=\"@drawable/ic_action_share_wechat\"/>\n        <ImageView\n            android:id=\"@+id/iv_share_moment\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginEnd=\"10dp\"\n            android:src=\"@drawable/ic_action_share_moment\"/>\n        <ImageView\n            android:id=\"@+id/iv_share_weibo\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginEnd=\"10dp\"\n            android:src=\"@drawable/ic_action_share_weibo\"/>\n        <ImageView\n            android:id=\"@+id/iv_share_qq\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:src=\"@drawable/ic_action_share_qq\"/>\n    </LinearLayout>\n\n    <RelativeLayout\n        android:id=\"@+id/rl_bottom\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_marginBottom=\"20dp\"\n        android:layout_marginLeft=\"@dimen/default_margin\"\n        android:layout_marginRight=\"@dimen/default_margin\"\n    >\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_currentTime\"\n            android:layout_width=\"50dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentStart=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"12sp\"\n            app:font_name=\"normal\"/>\n\n        <SeekBar\n            android:id=\"@+id/sb_progress\"\n            style=\"@style/ProgressBarStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginEnd=\"10dp\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_toEndOf=\"@id/tv_currentTime\"\n            android:layout_toStartOf=\"@id/tv_end_time\"\n            android:background=\"@color/transparent\"\n            android:thumb=\"@drawable/ic_player_progress_handle\"\n            android:thumbOffset=\"0dp\"\n        />\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_end_time\"\n            android:layout_width=\"40dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"12sp\"\n            app:font_name=\"normal\"/>\n\n    </RelativeLayout>\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_media_controller_tiny.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginBottom=\"8dp\"\n        android:background=\"@color/black_translucent_60\">\n\n        <ImageView\n            android:id=\"@+id/iv_back\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:src=\"@drawable/ic_action_detail_back\" />\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:gravity=\"center_vertical\">\n\n            <ImageView\n                android:id=\"@+id/iv_favorites\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/ic_action_favorites\" />\n\n            <ImageView\n                android:id=\"@+id/iv_share\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/ic_action_share\" />\n\n            <ImageView\n                android:id=\"@+id/iv_more\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:src=\"@drawable/ic_menu_more_white\" />\n        </LinearLayout>\n\n\n        <ImageView\n            android:id=\"@+id/iv_previous\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toStartOf=\"@id/iv_pause\"\n            android:src=\"@drawable/ic_player_previous\"\n            android:visibility=\"gone\" />\n\n        <ImageView\n            android:id=\"@+id/iv_pause\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:background=\"@null\"\n            android:src=\"@drawable/ic_player_pause\" />\n\n        <ImageView\n            android:id=\"@+id/iv_next\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toEndOf=\"@id/iv_pause\"\n            android:src=\"@drawable/ic_player_next\" />\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/iv_pause\"\n            android:layout_centerInParent=\"true\">\n\n            <TextView\n                android:id=\"@+id/tv_currentTime\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"@color/white\"\n                android:textSize=\"12sp\" />\n\n            <TextView\n                android:id=\"@+id/tv_end_time\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"@color/white\"\n                android:textSize=\"12sp\" />\n\n        </LinearLayout>\n\n        <ImageView\n            android:id=\"@+id/iv_full_screen\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_alignParentBottom=\"true\"\n            android:layout_marginBottom=\"11.8dp\"\n            android:background=\"@null\"\n            android:src=\"@drawable/ic_action_full_screen\" />\n    </RelativeLayout>\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_search_hot_remind_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_marginBottom=\"10dp\"\n        android:layout_marginTop=\"20dp\"\n        android:text=\"@string/search_hot_remind\"\n        android:textColor=\"@color/gray_B7B9B8\"\n        android:textSize=\"12sp\"\n        app:font_name=\"normal\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_result\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/tv_title\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_marginBottom=\"10dp\"\n        android:layout_marginTop=\"10dp\"\n        android:text=\"@string/hot_search_word\"\n        android:textColor=\"@color/black\"\n        android:textSize=\"13sp\"\n        app:font_name=\"normal\"/>\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_single_text.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@color/white\">\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:letterSpacing=\"0.5\"\n        android:padding=\"10dp\"\n        android:textSize=\"14sp\"/>\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_single_video.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <!--这里加前景是为了处理，有些图片背景是白色的情况-->\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/iv_image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"200dp\"\n        android:foreground=\"@color/black_alpha_33\"\n    />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"vertical\">\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_single_title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center\"\n            android:maxLines=\"1\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"15sp\"\n            app:font_name=\"bold\"/>\n\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_single_desc\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dp\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center\"\n            android:maxLines=\"1\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"13sp\"\n            app:font_name=\"normal\"/>\n\n    </LinearLayout>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_label\"\n        android:layout_width=\"60dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_marginEnd=\"@dimen/default_margin\"\n        android:layout_marginTop=\"@dimen/default_margin\"\n        android:background=\"@drawable/shape_translate_border\"\n        android:gravity=\"center\"\n        android:padding=\"3dp\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"10sp\"\n        android:visibility=\"gone\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_square_collection.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_marginTop=\"5dp\"\n    android:layout_marginBottom=\"5dp\"\n    android:background=\"@color/white\"\n    android:paddingTop=\"@dimen/default_padding_top\">\n\n    <com.jennifer.andy.simpleeyes.widget.ItemHeaderView\n        android:id=\"@+id/item_header_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"/>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rv_square_recycler\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/item_header_view\"/>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_below=\"@id/rv_square_recycler\"\n        android:layout_marginTop=\"10dp\"\n        android:background=\"@color/divider\"/>\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_video_author_head.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"80dp\">\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_alignParentTop=\"true\"\n        android:background=\"@color/divider\"/>\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:padding=\"@dimen/default_padding\">\n\n        <com.facebook.drawee.view.SimpleDraweeView\n            android:id=\"@+id/iv_image\"\n            android:layout_width=\"@dimen/default_card_source_icon_width\"\n            android:layout_height=\"@dimen/default_card_source_icon_height\"\n            android:layout_centerVertical=\"true\"\n            app:roundAsCircle=\"true\"/>\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignTop=\"@id/iv_image\"\n            android:layout_marginStart=\"15dp\"\n            android:layout_toEndOf=\"@id/iv_image\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"12sp\"\n            app:font_name=\"bold\"/>\n\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_desc\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@+id/tv_title\"\n            android:layout_marginEnd=\"10dp\"\n            android:layout_marginStart=\"15dp\"\n            android:layout_marginTop=\"5dp\"\n            android:layout_toEndOf=\"@id/iv_image\"\n            android:layout_toStartOf=\"@+id/tv_follow\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"11sp\"\n            app:font_name=\"normal\"/>\n\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_follow\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:background=\"@drawable/shape_white_border\"\n            android:gravity=\"center\"\n            android:padding=\"5dp\"\n            android:text=\"@string/add_follow\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"11sp\"\n            app:font_name=\"bold\"\n        />\n    </RelativeLayout>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_alignParentBottom=\"true\"\n        android:background=\"@color/divider\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_video_detail_head.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@color/black_alpha_4D\"\n    android:minHeight=\"100dp\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/default_padding\">\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTypeWriterTextView\n        android:id=\"@+id/tv_title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textColor=\"@color/white\"\n        app:font_name=\"bold\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTypeWriterTextView\n        android:id=\"@+id/tv_time\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/tv_title\"\n        android:layout_marginTop=\"10dp\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"12sp\"\n        app:font_name=\"normal\"/>\n\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTypeWriterTextView\n        android:id=\"@+id/tv_desc\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/tv_time\"\n        android:layout_marginTop=\"10dp\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"10sp\"/>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/tv_desc\"\n        android:layout_marginTop=\"10dp\"\n        android:orientation=\"horizontal\">\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_favorite\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:drawablePadding=\"10dp\"\n            android:drawableStart=\"@drawable/ic_action_favorites_without_padding\"\n            android:gravity=\"center_vertical\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"11sp\"\n            app:font_name=\"normal\"\n        />\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_share\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:drawablePadding=\"10dp\"\n            android:drawableStart=\"@drawable/ic_action_share_without_padding\"\n            android:gravity=\"center_vertical\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"11sp\"\n            app:font_name=\"normal\"\n        />\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_reply\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:drawablePadding=\"10dp\"\n            android:drawableStart=\"@drawable/ic_action_reply_without_padding\"\n            android:gravity=\"center_vertical\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"11sp\"\n            app:font_name=\"normal\"\n        />\n\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_download\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:drawablePadding=\"10dp\"\n            android:drawableStart=\"@drawable/ic_action_offline_without_padding\"\n            android:gravity=\"center_vertical\"\n            android:text=\"@string/cache\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"11sp\"\n            app:font_name=\"normal\"/>\n\n\n    </LinearLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_video_error.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/black_translucent_65\">\n\n\n    <ImageView\n        android:id=\"@+id/iv_reload\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:src=\"@drawable/ic_player_reload\"/>\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/iv_reload\"\n        android:layout_centerHorizontal=\"true\"\n        android:text=\"@string/video_error_notice\"\n        android:textColor=\"@color/white\"/>\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/layout_video_small_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@color/white\"\n    android:padding=\"5dp\">\n\n\n    <RelativeLayout\n        android:id=\"@+id/rl_head_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\"\n        android:padding=\"10dp\">\n\n        <com.facebook.drawee.view.SimpleDraweeView\n            android:id=\"@+id/iv_image\"\n            android:layout_width=\"130dp\"\n            android:layout_height=\"80dp\"\n            android:layout_centerVertical=\"true\"\n        />\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignTop=\"@id/iv_image\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_toEndOf=\"@id/iv_image\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"2\"\n            android:textSize=\"15sp\"\n            app:font_name=\"bold\"/>\n\n\n        <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n            android:id=\"@+id/tv_desc\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/tv_title\"\n            android:layout_margin=\"15dp\"\n            android:layout_marginStart=\"10dp\"\n            android:layout_marginTop=\"5dp\"\n            android:layout_toEndOf=\"@id/iv_image\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center\"\n            android:maxWidth=\"150dp\"\n            android:maxLines=\"1\"\n            android:textColor=\"@color/SecondaryText\"\n            android:textSize=\"13sp\"\n            app:font_name=\"normal\"/>\n\n\n    </RelativeLayout>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_below=\"@id/rl_head_container\"\n        android:layout_marginTop=\"10dp\"\n        android:background=\"@color/divider\"/>\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/refresh_category_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/rl_refresh_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:layout_above=\"@+id/rl_more_container\"\n    android:background=\"@color/black_alpha_4D\">\n\n\n    <ImageView\n        android:id=\"@+id/iv_refresh\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:src=\"@drawable/ic_homepage_header_progress\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/refresh_daily_elite_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"85dp\"\n    android:background=\"@color/white\"\n    android:orientation=\"vertical\">\n\n    <com.jennifer.andy.simpleeyes.widget.font.CustomFontTextView\n        android:id=\"@+id/tv_loading_msg\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_above=\"@id/iv_head_outer\"\n        android:layout_centerHorizontal=\"true\"\n        android:text=\"@string/daily_elite_update\"\n        android:textSize=\"12sp\"\n        app:font_name=\"normal\"\n    />\n\n    <ImageView\n        android:id=\"@+id/iv_head_outer\"\n        android:layout_width=\"50dp\"\n        android:layout_height=\"50dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:src=\"@drawable/ic_eye_black_outer\"/>\n\n    <ImageView\n        android:id=\"@+id/iv_head_inner\"\n        android:layout_width=\"50dp\"\n        android:layout_height=\"50dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:src=\"@drawable/ic_eye_black_inner\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout-v21/layout_square_collection.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_marginTop=\"5dp\"\n    android:layout_marginBottom=\"5dp\"\n    android:background=\"@color/white\"\n    android:paddingTop=\"@dimen/default_padding_top\">\n\n    <com.jennifer.andy.simpleeyes.widget.ItemHeaderView\n        android:id=\"@+id/item_header_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"/>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/rv_square_recycler\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/item_header_view\"/>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_below=\"@id/rv_square_recycler\"\n        android:layout_marginTop=\"10dp\"\n        android:background=\"@color/divider\"/>\n</RelativeLayout>"
  },
  {
    "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=\"@mipmap/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=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n\n    <declare-styleable name=\"CustomFontTextView\">\n        <attr name=\"font_name\">\n            <enum name=\"normal\" value=\"0\"/>\n            <enum name=\"bold\" value=\"1\"/>\n            <enum name=\"future\" value=\"2\"/>\n            <enum name=\"lobster\" value=\"3\"/>\n        </attr>\n    </declare-styleable>\n\n\n    <declare-styleable name=\"MultipleStateView\">\n        <attr name=\"empty_view\" format=\"reference\"/>\n        <attr name=\"error_view\" format=\"reference\"/>\n        <attr name=\"content_view\" format=\"reference\"/>\n        <attr name=\"net_error_view\" format=\"reference\"/>\n        <attr name=\"loading_view\" format=\"reference\"/>\n    </declare-styleable>\n\n    <declare-styleable name=\"PullToZoomBase\">\n        <attr name=\"zoomView\" format=\"reference\"/>\n        <attr name=\"headView\" format=\"reference\"/>\n        <attr name=\"isHeaderParallax\" format=\"boolean\"/>\n    </declare-styleable>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#FEFFFE</color>\n    <color name=\"colorPrimaryDark\">#000100</color>\n    <color name=\"colorAccent\">#61EECB</color>\n    <color name=\"SecondaryText\">#757575</color>\n\n    <color name=\"white\">#FFFFFF</color>\n    <color name=\"white_FEFFFE\">#FEFFFE</color>\n    <color name=\"white_translucent_05\">#f2ffffff</color>\n    <color name=\"white_translucent_10\">#e6ffffff</color>\n    <color name=\"white_translucent_15\">#d9ffffff</color>\n    <color name=\"white_translucent_20\">#ccffffff</color>\n    <color name=\"white_translucent_25\">#bfffffff</color>\n    <color name=\"white_translucent_30\">#b3ffffff</color>\n    <color name=\"white_translucent_35\">#a6ffffff</color>\n    <color name=\"white_translucent_40\">#99ffffff</color>\n    <color name=\"white_translucent_45\">#8cffffff</color>\n    <color name=\"white_translucent_50\">#80ffffff</color>\n    <color name=\"white_translucent_55\">#73ffffff</color>\n    <color name=\"white_translucent_60\">#66ffffff</color>\n    <color name=\"white_translucent_65\">#59ffffff</color>\n    <color name=\"white_translucent_70\">#4dffffff</color>\n    <color name=\"white_translucent_75\">#40ffffff</color>\n    <color name=\"white_translucent_80\">#33ffffff</color>\n    <color name=\"white_translucent_90\">#26ffffff</color>\n    <color name=\"white_translucent_95\">#0dffffff</color>\n\n\n    <color name=\"black\">#000000</color>\n    <color name=\"black_alpha_4D\">#4D000000</color>\n    <color name=\"black_alpha_33\">#33000000</color>\n    <color name=\"black_translucent_05\">#f2000000</color>\n    <color name=\"black_translucent_10\">#e6000000</color>\n    <color name=\"black_translucent_15\">#d9000000</color>\n    <color name=\"black_translucent_20\">#cc000000</color>\n    <color name=\"black_translucent_25\">#bf000000</color>\n    <color name=\"black_translucent_30\">#b3000000</color>\n    <color name=\"black_translucent_35\">#a6000000</color>\n    <color name=\"black_translucent_40\">#99000000</color>\n    <color name=\"black_translucent_45\">#8c000000</color>\n    <color name=\"black_translucent_50\">#80000000</color>\n    <color name=\"black_translucent_55\">#73000000</color>\n    <color name=\"black_translucent_60\">#66000000</color>\n    <color name=\"black_translucent_65\">#59000000</color>\n    <color name=\"black_translucent_70\">#4d000000</color>\n    <color name=\"black_translucent_75\">#40000000</color>\n    <color name=\"black_translucent_80\">#33000000</color>\n    <color name=\"black_translucent_90\">#26000000</color>\n    <color name=\"black_translucent_95\">#0d000000</color>\n    <color name=\"black_444444\">#444444</color>\n    <color name=\"black_1E1F1D\">#1E1F1D</color>\n\n    <color name=\"divider\">#66A2A2A2</color>\n    <color name=\"transparent\">#00000000</color>\n\n    <color name=\"gray_BBBBBB\">#BBBBBB</color>\n    <color name=\"gray_828885\">#828885</color>\n    <color name=\"gray_66A2A2A2\">#66A2A2A2</color>\n    <color name=\"gray_EDEEEC\">#EDEEEC</color>\n    <color name=\"gray_B7B9B8\">#B7B9B8</color>\n    <color name=\"gray_EFF0EF\">#EFF0EF</color>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"default_margin\">15dp</dimen>\n    <dimen name=\"default_margin_left\">15dp</dimen>\n    <dimen name=\"default_margin_right\">15dp</dimen>\n    <dimen name=\"default_margin_top\">15dp</dimen>\n    <dimen name=\"default_margin_bottom\">15dp</dimen>\n\n    <dimen name=\"default_padding\">15dp</dimen>\n    <dimen name=\"default_padding_top\">15dp</dimen>\n\n    <dimen name=\"default_card_height\">200dp</dimen>\n    <dimen name=\"default_card_source_icon_width\">40dp</dimen>\n    <dimen name=\"default_card_source_icon_height\">40dp</dimen>\n    <dimen name=\"normal_title_height\">48dp</dimen>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/ids_sticky_nav_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <item name=\"id_sticky_nav_layout_top_view\" type=\"id\"/>\n    <item name=\"id_sticky_nav_layout_nav_view\" type=\"id\"/>\n    <item name=\"id_sticky_nav_layout_viewpager\" type=\"id\"/>\n    <item name=\"id_sticky_nav_layout_scrollview\" type=\"id\"/>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">SimpleEyes</string>\n    <string name=\"home\">首页</string>\n    <string name=\"discover\">发现</string>\n    <string name=\"focus\">关注</string>\n    <string name=\"mine\">我的</string>\n    <string name=\"net_error_message\">网络错误\\n点击屏幕重试</string>\n    <string name=\"empty_message\">没有数据\\n点击屏幕重试</string>\n    <string name=\"show_all\">查看全部</string>\n    <string name=\"all_category\">全部分类</string>\n    <string name=\"all_author\">全部作者</string>\n    <string name=\"all_range\">全部排行</string>\n    <string name=\"advert\">广告</string>\n    <string name=\"elite\">Andy精选</string>\n    <string name=\"more_andy_elite\">更多开眼编辑精选</string>\n    <string name=\"no_more_string\">-The End-</string>\n    <string name=\"loading_message\">正在加载中…</string>\n\n    <string name=\"VideoView_error_text_invalid_progressive_playback\">Invalid progressive playback\n    </string>\n    <string name=\"VideoView_error_text_unknown\">Unknown</string>\n    <string name=\"VideoView_error_button\">OK</string>\n\n    <string name=\"VideoView_ar_aspect_fit_parent\">Aspect / Fit parent</string>\n    <string name=\"VideoView_ar_aspect_fill_parent\">Aspect / Fill parent</string>\n    <string name=\"VideoView_ar_aspect_wrap_content\">Aspect / Wrap item</string>\n    <string name=\"VideoView_ar_match_parent\">Free / Fill parent</string>\n    <string name=\"VideoView_ar_16_9_fit_parent\">16:9 / Fit parent</string>\n    <string name=\"VideoView_ar_4_3_fit_parent\">4:3 / Fit parent</string>\n    <string name=\"N_A\">N/A</string>\n    <string name=\"open_eyes_chinese\">开眼</string>\n    <string name=\"open_eyes_english\">Eyepetizer</string>\n    <string name=\"today_chose\"><![CDATA[今日精选>>]]></string>\n    <string name=\"for_today\">for \\n Today</string>\n    <string name=\"ad_english_message\">Daily appetizes for your eyes.Bon eyepetit.</string>\n    <string name=\"ad_chinese_message\">每日精选视频推介，让你大开眼界。</string>\n    <string name=\"cache\">缓存</string>\n    <string name=\"add_follow\">+ 关注</string>\n    <string name=\"video_error_notice\">网络错误或无连接，点击屏幕重试</string>\n    <string name=\"find_interesting_video\">帮你找到感兴趣的视频</string>\n    <string name=\"cancel\">取消</string>\n    <string name=\"search_hot_remind\">输入标题或描述中的关键字找到更多视频</string>\n    <string name=\"hot_search_word\">- 热门搜索词 -</string>\n    <string name=\"remind_find_in_group\">可以通过所属分类\\n以及标题或描述中的关键词来查找</string>\n    <string name=\"not_find_match_content\">很抱歉没有找到相匹配的内容</string>\n    <string name=\"daily_editor_elite\">每日编辑精选</string>\n    <string name=\"load_failed\">加载更多失败</string>\n    <string name=\"daily_elite_update\">今日日报已更新</string>\n    <string name=\"discover_en\">Discover</string>\n    <string name=\"Subscription_en\">Subscription</string>\n    <string name=\"login_notice\">登录后即可关注作者、\\n发表评论、同步收藏视频和播放记录</string>\n    <string name=\"click_login\">点击登录即可发表评论及同步已收藏视频</string>\n    <string name=\"commit\">评论</string>\n    <string name=\"favorites\">收藏</string>\n    <string name=\"version\">Version 3.8.1.2.216</string>\n    <string name=\"user_agreement\">登录或注册及同意开眼用户服务协议</string>\n    <string name=\"mine_cache\">我的缓存</string>\n    <string name=\"edit\">编辑</string>\n\n    <string-array name=\"slogan_array_zh\">\n        <item>每日编辑精选，一如既往</item>\n        <item>关注越多，发现越多</item>\n        <item>离线自动缓存，精彩永不下线</item>\n        <item>登录即可订阅、评论和同步已收藏视频</item>\n    </string-array>\n\n    <string-array name=\"profile_setting\">\n        <item>我的消息</item>\n        <item>我的关注</item>\n        <item>我的缓存</item>\n        <item>观看记录</item>\n        <item>意见反馈</item>\n        <item>我要投稿</item>\n    </string-array>\n    <string-array name=\"slogan_array_en\">\n        <item>Daily appetizers for your eyes,as always</item>\n        <item>Subscribe more,discover a whole lot more</item>\n        <item>Enjoy your daily eyepetit,even without connectivity</item>\n        <item>Sign in to comment &amp;collect videos,to subscript also</item>\n    </string-array>\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.NoActionBar\">\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\n    <!--闪屏页-->\n    <style name=\"SplashTheme\" parent=\"AppTheme\">\n        <item name=\"android:windowBackground\">@color/black</item>\n        <item name=\"android:windowFullscreen\">true</item>\n    </style>\n\n    <!--进度条-->\n    <style name=\"ProgressBarStyle\">\n        <item name=\"android:indeterminateOnly\">false</item>\n        <item name=\"android:progressDrawable\">@drawable/seek_bar_layer</item>\n        <item name=\"android:minHeight\">2dp</item>\n        <item name=\"android:maxHeight\">2dp</item>\n        <item name=\"android:mirrorForRtl\">true</item>\n    </style>\n\n    <style name=\"VideoProgress\" parent=\"@android:style/Theme.Dialog\">\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:backgroundDimEnabled\">false</item>\n    </style>\n\n    <!--Tab文字样式-->\n    <style name=\"TabAppearance\">\n        <item name=\"android:textSize\">13sp</item>\n        <item name=\"android:textStyle\">bold</item>\n    </style>\n\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/xml/network_security_config.xml",
    "content": "<network-security-config>\n    <base-config cleartextTrafficPermitted=\"true\">\n        <trust-anchors>\n            <certificates src=\"system\" />\n            <certificates src=\"user\" />\n        </trust-anchors>\n    </base-config>\n</network-security-config>"
  },
  {
    "path": "app/src/test/java/com/jennifer/andy/simpleeyes/ExampleUnitTest.kt",
    "content": "package com.jennifer.andy.simpleeyes\n\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\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": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    ext.kotlin_version = '1.3.11'\n    repositories {\n        google()\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.4.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\n    repositories {\n        google()\n        jcenter()\n        maven { url \"https://jitpack.io\" }\n\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Mar 11 21:37:12 CST 2020\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-5.6.4-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\nandroid.enableJetifier=true\nandroid.useAndroidX=true\norg.gradle.jvmargs=-Xmx1536m\n\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"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\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\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\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\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\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\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\" ] ; 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# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\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\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\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\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 initData\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 initData\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:initData\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_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=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\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": "settings.gradle",
    "content": "include ':app'\n"
  }
]