Procházet zdrojové kódy

幼儿园项目创想天地幼儿课件 初始版本

infinite.likelins před 5 roky
revize
0209773657
100 změnil soubory, kde provedl 2057 přidání a 0 odebrání
  1. 14 0
      .gitignore
  2. 14 0
      README.MD
  3. 1 0
      app/.gitignore
  4. 161 0
      app/build.gradle
  5. 21 0
      app/proguard-rules.pro
  6. 140 0
      app/schemas/com.bearya.kids.AppDatabase/1.json
  7. 48 0
      app/src/main/AndroidManifest.xml
  8. binární
      app/src/main/assets/chapter/先导一《我是小贝的好朋友》/先导一《我是小贝的好朋友》.webp
  9. binární
      app/src/main/assets/chapter/先导一《机器人的世界》/先导一《机器人的世界》.webp
  10. binární
      app/src/main/assets/chapter/先导三《一起去游玩》/先导三《一起去游玩》.webp
  11. binární
      app/src/main/assets/chapter/先导三《爱心小天使》/先导三《爱心小天使》.webp
  12. binární
      app/src/main/assets/chapter/先导二《跳跳镇》/先导二《跳跳镇》.webp
  13. binární
      app/src/main/assets/chapter/先导二《送小贝去游玩》/先导二《送小贝去游玩》.webp
  14. binární
      app/src/main/assets/chapter/先导五《场景模块》/先导五《场景模块》.webp
  15. binární
      app/src/main/assets/chapter/先导四《一起去游乐园》/先导四《一起去游乐园》.webp
  16. binární
      app/src/main/assets/chapter/先导四《遗失的金币》/先导四《遗失的金币》.webp
  17. binární
      app/src/main/assets/chapter/第一单元《我会对“机器人”发出指令》/第一单元《我会对“机器人”发出指令》.webp
  18. binární
      app/src/main/assets/chapter/第一单元《我是小贝的好朋友》/第一单元《我是小贝的好朋友》.webp
  19. binární
      app/src/main/assets/chapter/第一单元《我是小贝的好朋友》/第十三单元《创想天地》.webp
  20. binární
      app/src/main/assets/chapter/第一单元《敬业的公交车司机》/第一单元《敬业的公交车司机》.webp
  21. binární
      app/src/main/assets/chapter/第七单元《乐于助人好宝宝》/第七单元《乐于助人好宝宝》.webp
  22. binární
      app/src/main/assets/chapter/第七单元《最短的路》/第七单元《最短的路》.webp
  23. binární
      app/src/main/assets/chapter/第七单元《火山》/第七单元《火山》.webp
  24. binární
      app/src/main/assets/chapter/第三单元《奇幻寻宝》/第三单元《奇幻寻宝》.webp
  25. binární
      app/src/main/assets/chapter/第三单元《这里危险,不能走》/第三单元《这里危险,不能走》.webp
  26. binární
      app/src/main/assets/chapter/第三单元《送小贝去游玩》/第三单元《送小贝去游玩》.webp
  27. binární
      app/src/main/assets/chapter/第九单元《创想天地》/第九单元《创想天地》.webp
  28. binární
      app/src/main/assets/chapter/第九单元《梦幻舞会》/第九单元《梦幻舞会》.webp
  29. binární
      app/src/main/assets/chapter/第九单元《谁的步数少?》/第九单元《谁的步数少?》.webp
  30. binární
      app/src/main/assets/chapter/第二单元《小小道路工程师》/第二单元《小小道路工程师》.webp
  31. binární
      app/src/main/assets/chapter/第二单元《小贝的好朋友》/第二单元《小贝的好朋友》.webp
  32. binární
      app/src/main/assets/chapter/第二单元《我会输入指令》/第二单元《我会输入指令》.webp
  33. binární
      app/src/main/assets/chapter/第五单元《一起去游乐园》/第五单元《一起去游乐园》.webp
  34. binární
      app/src/main/assets/chapter/第五单元《寻找秘密安全通道》/第五单元《寻找秘密安全通道》.webp
  35. binární
      app/src/main/assets/chapter/第五单元《英雄无敌》/第五单元《英雄无敌》.webp
  36. binární
      app/src/main/assets/chapter/第八单元《冲破障碍》/第八单元《冲破障碍》.webp
  37. binární
      app/src/main/assets/chapter/第八单元《教练与玩家》/第八单元《教练与玩家》.webp
  38. binární
      app/src/main/assets/chapter/第八单元《解决问题小能手》/第八单元《解决问题小能手》.webp
  39. binární
      app/src/main/assets/chapter/第六单元《丢失的金币》/第六单元《丢失的金币》.webp
  40. binární
      app/src/main/assets/chapter/第六单元《危险重重》/第六单元《危险重重》.webp
  41. binární
      app/src/main/assets/chapter/第六单元《弯弯的路》/第六单元《弯弯的路》.webp
  42. binární
      app/src/main/assets/chapter/第十一单元《哪条路更短?》/第十一单元《哪条路更短?》.webp
  43. binární
      app/src/main/assets/chapter/第十一单元《我能读懂指令》/第十一单元《我能读懂指令》.webp
  44. binární
      app/src/main/assets/chapter/第十三单元《创想天地》/第十三单元《创想天地》.webp
  45. binární
      app/src/main/assets/chapter/第十二单元《教练与玩家》/第十二单元《教练与玩家》.webp
  46. binární
      app/src/main/assets/chapter/第十单元《循环》/第十单元《循环》.webp
  47. binární
      app/src/main/assets/chapter/第十单元《规划路线我最行》/第十单元《规划路线我最行》.webp
  48. binární
      app/src/main/assets/chapter/第四单元《修理跳跳镇的路》/第四单元《修理跳跳镇的路》.webp
  49. binární
      app/src/main/assets/chapter/第四单元《我会调试》/第四单元《我会调试》.webp
  50. binární
      app/src/main/assets/chapter/第四单元《高级道路工程师》/第四单元《高级道路工程师》.webp
  51. binární
      app/src/main/assets/database/kids.db
  52. 14 0
      app/src/main/java/com/bearya/data/dao/AnalysisDao.kt
  53. 14 0
      app/src/main/java/com/bearya/data/dao/ChapterDao.kt
  54. 14 0
      app/src/main/java/com/bearya/data/dao/SectionDao.kt
  55. 10 0
      app/src/main/java/com/bearya/data/entity/Analysis.kt
  56. 13 0
      app/src/main/java/com/bearya/data/entity/Chapter.kt
  57. 13 0
      app/src/main/java/com/bearya/data/entity/Section.kt
  58. 12 0
      app/src/main/java/com/bearya/data/repository/AnalysisRepository.kt
  59. 13 0
      app/src/main/java/com/bearya/data/repository/ChapterRepository.kt
  60. 11 0
      app/src/main/java/com/bearya/data/repository/SectionRepository.kt
  61. 22 0
      app/src/main/java/com/bearya/kids/App.kt
  62. 42 0
      app/src/main/java/com/bearya/kids/AppDatabase.kt
  63. 99 0
      app/src/main/java/com/bearya/kids/analysis/AnalysisAdapter.kt
  64. 106 0
      app/src/main/java/com/bearya/kids/analysis/AnalysisFragment.kt
  65. 24 0
      app/src/main/java/com/bearya/kids/analysis/AnalysisViewModel.kt
  66. 47 0
      app/src/main/java/com/bearya/kids/chapter/ChapterAdapter.kt
  67. 71 0
      app/src/main/java/com/bearya/kids/chapter/ChapterFragment.kt
  68. 21 0
      app/src/main/java/com/bearya/kids/chapter/ChapterViewModel.kt
  69. 46 0
      app/src/main/java/com/bearya/kids/grade/GradeFragment.kt
  70. 49 0
      app/src/main/java/com/bearya/kids/main/MainActivity.kt
  71. 102 0
      app/src/main/java/com/bearya/kids/section/SectionAdapter.kt
  72. 123 0
      app/src/main/java/com/bearya/kids/section/SectionFragment.kt
  73. 21 0
      app/src/main/java/com/bearya/kids/section/SectionViewModel.kt
  74. 38 0
      app/src/main/java/com/bearya/kids/splash/SplashFragment.kt
  75. 113 0
      app/src/main/java/library/BindingComponent.kt
  76. 18 0
      app/src/main/java/library/BuglyComponent.kt
  77. 7 0
      app/src/main/java/library/ListenerComponent.kt
  78. 41 0
      app/src/main/java/library/LoggerComponent.kt
  79. 10 0
      app/src/main/java/library/ext/IntExt.kt
  80. 22 0
      app/src/main/java/library/ext/LiveDataExt.kt
  81. 14 0
      app/src/main/java/library/ext/PagedListExt.kt
  82. 12 0
      app/src/main/java/library/ext/StringExt.kt
  83. 58 0
      app/src/main/java/library/ext/ViewExt.kt
  84. binární
      app/src/main/res/drawable/analysis.webp
  85. binární
      app/src/main/res/drawable/back.webp
  86. binární
      app/src/main/res/drawable/chapters_bg.webp
  87. binární
      app/src/main/res/drawable/grade_bg.webp
  88. binární
      app/src/main/res/drawable/grade_large.webp
  89. binární
      app/src/main/res/drawable/grade_middle.webp
  90. binární
      app/src/main/res/drawable/grade_primary.webp
  91. binární
      app/src/main/res/drawable/launcher.webp
  92. binární
      app/src/main/res/drawable/row_list_bg.webp
  93. 25 0
      app/src/main/res/layout/activity_main.xml
  94. 57 0
      app/src/main/res/layout/fragment_analysis.xml
  95. 60 0
      app/src/main/res/layout/fragment_chapter.xml
  96. 134 0
      app/src/main/res/layout/fragment_grade.xml
  97. 70 0
      app/src/main/res/layout/fragment_section.xml
  98. 17 0
      app/src/main/res/layout/fragment_splash.xml
  99. 75 0
      app/src/main/res/layout/item_chapter.xml
  100. 0 0
      app/src/main/res/layout/item_section.xml

+ 14 - 0
.gitignore

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

+ 14 - 0
README.MD

@@ -0,0 +1,14 @@
+# 幼儿园项目创想天地幼儿课件
+
+![Version](https://img.shields.io/badge/Version-1.0.0-brightgreen.svg?style=flat)
+![VersionCode](https://img.shields.io/badge/VersionCode-1-brightgreen.svg?style=flat)
+![数据库版本](https://img.shields.io/badge/DatabaseVersion-1-brightgreen.svg?style=flat)
+![AndroidAPI](https://img.shields.io/badge/AndroidAPI-21+-brightgreen.svg?style=flat)
+
+[Apk版本更新日志]()
+
+#### 应用包名 :com.bearya.kids
+
+#### 工程构建模块 : include ':app'
+
+#### v-X.Y.Z.B : X 主版本号 , Y 小版本号 , Z bug修复版本号 , B 编译版本号

+ 1 - 0
app/.gitignore

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

+ 161 - 0
app/build.gradle

@@ -0,0 +1,161 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+apply plugin: "androidx.navigation.safeargs.kotlin"
+
+android {
+    compileSdkVersion 29
+    buildToolsVersion "29.0.3"
+
+    def currentVersionCode = getVersionCode()
+    def currentVersionName = getVersionName()
+
+    defaultConfig {
+        applicationId "com.bearya.kids"
+        minSdkVersion 21
+        targetSdkVersion 29
+        versionCode currentVersionCode
+        versionName currentVersionName
+        multiDexEnabled true
+        vectorDrawables.useSupportLibrary true
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments = ["room.schemaLocation": "$projectDir/schemas".toString(),
+                             "room.expandProjection":"true"]
+            }
+        }
+    }
+
+    dataBinding {
+        enabled true
+    }
+
+    aaptOptions {
+        noCompress "webp"  //表示不让aapt压缩的文件后缀
+    }
+
+    signingConfigs {
+        config {
+            keyAlias 'BeiYa'
+            storePassword 'BeiYa123'
+            storeFile file('../bearya_keystore.jks')
+            keyPassword 'BeiYa!@#'
+        }
+    }
+
+    buildTypes {
+        debug {
+            debuggable true
+            minifyEnabled false
+            buildConfigField "String", "BuglyAppKey", BUGLY_APP_KEY
+            signingConfig signingConfigs.config
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+        release {
+            debuggable false
+            minifyEnabled false
+            buildConfigField "String", "BuglyAppKey", BUGLY_APP_KEY
+            signingConfig signingConfigs.config
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    android.applicationVariants.all { variant ->
+        variant.outputs.each { output ->
+            if ("release" == buildType.name)
+                output.outputFileName = "Kids_${defaultConfig.versionCode}_v${defaultConfig.versionName}_release.apk"
+            else
+                output.outputFileName = "Kids_v${defaultConfig.versionName}.apk"
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    kotlinOptions {
+        jvmTarget = 1.8
+    }
+
+
+}
+
+kapt {
+    generateStubs = true
+}
+
+// 获取版本号
+def getVersionCode() {
+    def versionFile = file('version.properties')// 读取第一步新建的文件
+    if (versionFile.canRead()) {// 判断文件读取异常
+        Properties versionProps = new Properties()
+        versionProps.load(new FileInputStream(versionFile))
+        def versionCode = versionProps['VERSION_CODE'].toInteger()// 读取文件里面的版本号
+        versionProps['VERSION_CODE'] = (++versionCode).toString()
+        versionProps.store(versionFile.newWriter(), null)
+        return versionCode // 返回自增之后的版本号
+    } else {
+        throw new Exception("Could not find version.properties!")
+    }
+}
+
+def getVersionName() {
+    def versionFile = file('version.properties')// 读取第一步新建的文件
+    if (versionFile.canRead()) {// 判断文件读取异常
+        Properties versionProps = new Properties()
+        versionProps.load(new FileInputStream(versionFile))
+        def versionName = versionProps['VERSION_NAME'].toString()// 读取文件里面的版本号
+        return versionName // 返回版本
+    } else {
+        throw new Exception("Could not find version.properties!")
+    }
+}
+
+
+dependencies {
+
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
+    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
+
+    implementation 'androidx.appcompat:appcompat:1.1.0'
+    implementation 'androidx.core:core-ktx:1.2.0'
+    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+    implementation 'androidx.viewpager2:viewpager2:1.0.0'
+    implementation 'androidx.paging:paging-runtime:2.1.2'
+    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
+    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
+    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
+    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
+    implementation 'androidx.lifecycle:lifecycle-reactivestreams:2.2.0'
+    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0-alpha05'
+    implementation 'androidx.navigation:navigation-ui-ktx:2.3.0-alpha05'
+    implementation 'androidx.room:room-runtime:2.2.5'
+    implementation 'androidx.room:room-ktx:2.2.5'
+    implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
+    implementation 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
+    implementation 'androidx.cardview:cardview:1.0.0'
+    implementation 'androidx.recyclerview:recyclerview:1.1.0'
+    implementation 'androidx.multidex:multidex:2.0.1'
+    implementation 'androidx.viewpager2:viewpager2:1.0.0'
+
+    implementation 'com.google.code.gson:gson:2.8.6'
+
+    implementation 'com.tencent.bugly:nativecrashreport:3.7.1'
+    implementation 'com.tencent.tinker:tinker-android-lib:1.9.14.5'
+    implementation 'com.tencent.bugly:crashreport_upgrade:1.4.5'
+
+    implementation 'com.github.bumptech.glide:glide:4.11.0'
+    implementation 'com.github.SheHuan:NiceImageView:1.0.5'
+    implementation 'jp.wasabeef:glide-transformations:4.0.1'
+    implementation 'com.orhanobut:logger:2.2.0'
+    implementation 'com.kaopiz:kprogresshud:1.2.0'
+
+    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
+    kapt 'androidx.annotation:annotation:1.1.0'
+    kapt 'androidx.room:room-compiler:2.2.5'
+}

+ 21 - 0
app/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 140 - 0
app/schemas/com.bearya.kids.AppDatabase/1.json

@@ -0,0 +1,140 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 1,
+    "identityHash": "5400eea03707e786cba5f4ca0ec469af",
+    "entities": [
+      {
+        "tableName": "Chapter",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT, `level` INTEGER, `enable` INTEGER, `lock` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "level",
+            "columnName": "level",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "enable",
+            "columnName": "enable",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lock",
+            "columnName": "lock",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Section",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `chapterId` INTEGER, `name` TEXT, `hasAnalysis` INTEGER, `enable` INTEGER, `lock` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "chapterId",
+            "columnName": "chapterId",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "hasAnalysis",
+            "columnName": "hasAnalysis",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "enable",
+            "columnName": "enable",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lock",
+            "columnName": "lock",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Analysis",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT, `sectionId` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sectionId",
+            "columnName": "sectionId",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      }
+    ],
+    "views": [],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5400eea03707e786cba5f4ca0ec469af')"
+    ]
+  }
+}

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

@@ -0,0 +1,48 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.bearya.kids">
+
+    <uses-feature
+        android:name="android.hardware.microphone"
+        android:required="false" />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+
+    <application
+        android:name=".App"
+        android:allowBackup="false"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme"
+        tools:ignore="GoogleAppIndexingWarning"
+        tools:replace="android:allowBackup"
+        tools:targetApi="q">
+
+        <activity
+            android:name=".main.MainActivity"
+            android:screenOrientation="fullSensor">
+
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <nav-graph android:value="@navigation/main_navigation" />
+
+        </activity>
+
+    </application>
+
+</manifest>

binární
app/src/main/assets/chapter/先导一《我是小贝的好朋友》/先导一《我是小贝的好朋友》.webp


binární
app/src/main/assets/chapter/先导一《机器人的世界》/先导一《机器人的世界》.webp


binární
app/src/main/assets/chapter/先导三《一起去游玩》/先导三《一起去游玩》.webp


binární
app/src/main/assets/chapter/先导三《爱心小天使》/先导三《爱心小天使》.webp


binární
app/src/main/assets/chapter/先导二《跳跳镇》/先导二《跳跳镇》.webp


binární
app/src/main/assets/chapter/先导二《送小贝去游玩》/先导二《送小贝去游玩》.webp


binární
app/src/main/assets/chapter/先导五《场景模块》/先导五《场景模块》.webp


binární
app/src/main/assets/chapter/先导四《一起去游乐园》/先导四《一起去游乐园》.webp


binární
app/src/main/assets/chapter/先导四《遗失的金币》/先导四《遗失的金币》.webp


binární
app/src/main/assets/chapter/第一单元《我会对“机器人”发出指令》/第一单元《我会对“机器人”发出指令》.webp


binární
app/src/main/assets/chapter/第一单元《我是小贝的好朋友》/第一单元《我是小贝的好朋友》.webp


binární
app/src/main/assets/chapter/第一单元《我是小贝的好朋友》/第十三单元《创想天地》.webp


binární
app/src/main/assets/chapter/第一单元《敬业的公交车司机》/第一单元《敬业的公交车司机》.webp


binární
app/src/main/assets/chapter/第七单元《乐于助人好宝宝》/第七单元《乐于助人好宝宝》.webp


binární
app/src/main/assets/chapter/第七单元《最短的路》/第七单元《最短的路》.webp


binární
app/src/main/assets/chapter/第七单元《火山》/第七单元《火山》.webp


binární
app/src/main/assets/chapter/第三单元《奇幻寻宝》/第三单元《奇幻寻宝》.webp


binární
app/src/main/assets/chapter/第三单元《这里危险,不能走》/第三单元《这里危险,不能走》.webp


binární
app/src/main/assets/chapter/第三单元《送小贝去游玩》/第三单元《送小贝去游玩》.webp


binární
app/src/main/assets/chapter/第九单元《创想天地》/第九单元《创想天地》.webp


binární
app/src/main/assets/chapter/第九单元《梦幻舞会》/第九单元《梦幻舞会》.webp


binární
app/src/main/assets/chapter/第九单元《谁的步数少?》/第九单元《谁的步数少?》.webp


binární
app/src/main/assets/chapter/第二单元《小小道路工程师》/第二单元《小小道路工程师》.webp


binární
app/src/main/assets/chapter/第二单元《小贝的好朋友》/第二单元《小贝的好朋友》.webp


binární
app/src/main/assets/chapter/第二单元《我会输入指令》/第二单元《我会输入指令》.webp


binární
app/src/main/assets/chapter/第五单元《一起去游乐园》/第五单元《一起去游乐园》.webp


binární
app/src/main/assets/chapter/第五单元《寻找秘密安全通道》/第五单元《寻找秘密安全通道》.webp


binární
app/src/main/assets/chapter/第五单元《英雄无敌》/第五单元《英雄无敌》.webp


binární
app/src/main/assets/chapter/第八单元《冲破障碍》/第八单元《冲破障碍》.webp


binární
app/src/main/assets/chapter/第八单元《教练与玩家》/第八单元《教练与玩家》.webp


binární
app/src/main/assets/chapter/第八单元《解决问题小能手》/第八单元《解决问题小能手》.webp


binární
app/src/main/assets/chapter/第六单元《丢失的金币》/第六单元《丢失的金币》.webp


binární
app/src/main/assets/chapter/第六单元《危险重重》/第六单元《危险重重》.webp


binární
app/src/main/assets/chapter/第六单元《弯弯的路》/第六单元《弯弯的路》.webp


binární
app/src/main/assets/chapter/第十一单元《哪条路更短?》/第十一单元《哪条路更短?》.webp


binární
app/src/main/assets/chapter/第十一单元《我能读懂指令》/第十一单元《我能读懂指令》.webp


binární
app/src/main/assets/chapter/第十三单元《创想天地》/第十三单元《创想天地》.webp


binární
app/src/main/assets/chapter/第十二单元《教练与玩家》/第十二单元《教练与玩家》.webp


binární
app/src/main/assets/chapter/第十单元《循环》/第十单元《循环》.webp


binární
app/src/main/assets/chapter/第十单元《规划路线我最行》/第十单元《规划路线我最行》.webp


binární
app/src/main/assets/chapter/第四单元《修理跳跳镇的路》/第四单元《修理跳跳镇的路》.webp


binární
app/src/main/assets/chapter/第四单元《我会调试》/第四单元《我会调试》.webp


binární
app/src/main/assets/chapter/第四单元《高级道路工程师》/第四单元《高级道路工程师》.webp


binární
app/src/main/assets/database/kids.db


+ 14 - 0
app/src/main/java/com/bearya/data/dao/AnalysisDao.kt

@@ -0,0 +1,14 @@
+package com.bearya.data.dao
+
+import androidx.paging.DataSource
+import androidx.room.Dao
+import androidx.room.Query
+import com.bearya.data.entity.Analysis
+
+@Dao
+interface AnalysisDao {
+
+    @Query("SELECT * FROM Analysis where `sectionId` = :id")
+    fun findAnalysisBySectionId(id:Int): DataSource.Factory<Int, Analysis>
+
+}

+ 14 - 0
app/src/main/java/com/bearya/data/dao/ChapterDao.kt

@@ -0,0 +1,14 @@
+package com.bearya.data.dao
+
+import androidx.paging.DataSource
+import androidx.room.Dao
+import androidx.room.Query
+import com.bearya.data.entity.Chapter
+
+@Dao
+interface ChapterDao {
+
+    @Query("SELECT * FROM Chapter WHERE `enable` = 1 AND `level`= :grade")
+    fun findChaptersByGradeLevel(grade: Int): DataSource.Factory<Int, Chapter>
+
+}

+ 14 - 0
app/src/main/java/com/bearya/data/dao/SectionDao.kt

@@ -0,0 +1,14 @@
+package com.bearya.data.dao
+
+import androidx.paging.DataSource
+import androidx.room.Dao
+import androidx.room.Query
+import com.bearya.data.entity.Section
+
+@Dao
+interface SectionDao {
+
+    @Query("SELECT * FROM Section WHERE `enable` = 1 AND `chapterId`= :chapterId")
+    fun findSectionsByChapterId(chapterId: Int): DataSource.Factory<Int, Section>
+
+}

+ 10 - 0
app/src/main/java/com/bearya/data/entity/Analysis.kt

@@ -0,0 +1,10 @@
+package com.bearya.data.entity
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity
+data class Analysis(@PrimaryKey(autoGenerate = true) val id: Int? = 0,
+                    val name: String?,
+                    val sectionId: Int?
+)

+ 13 - 0
app/src/main/java/com/bearya/data/entity/Chapter.kt

@@ -0,0 +1,13 @@
+package com.bearya.data.entity
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+// 单元章节
+@Entity
+data class Chapter(@PrimaryKey(autoGenerate = true) val id: Int? = 0,
+                   val name: String?, // 单元名称
+                   val level: Int?, // 关联的幼儿园年级Id
+                   val enable: Boolean?,// 启用状态
+                   var lock: Boolean? // 锁定状态
+)

+ 13 - 0
app/src/main/java/com/bearya/data/entity/Section.kt

@@ -0,0 +1,13 @@
+package com.bearya.data.entity
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity
+data class Section(@PrimaryKey(autoGenerate = true) val id: Int? = 0,
+                   val chapterId: Int?, // 关联的章节Id
+                   val name: String?, // 内容图片的名称
+                   val hasAnalysis: Boolean?, // 是否拥有答案解析
+                   val enable: Boolean?,// 启用状态
+                   var lock: Boolean? // 锁定状态
+)

+ 12 - 0
app/src/main/java/com/bearya/data/repository/AnalysisRepository.kt

@@ -0,0 +1,12 @@
+package com.bearya.data.repository
+
+import com.bearya.data.dao.AnalysisDao
+import com.bearya.kids.AppDatabase
+
+class AnalysisRepository {
+
+    private val analysisDao: AnalysisDao by lazy { AppDatabase.instance.analysisDao() }
+
+    fun findAnalysisBySectionId(id: Int) = analysisDao.findAnalysisBySectionId(id)
+
+}

+ 13 - 0
app/src/main/java/com/bearya/data/repository/ChapterRepository.kt

@@ -0,0 +1,13 @@
+package com.bearya.data.repository
+
+import com.bearya.data.dao.ChapterDao
+import com.bearya.kids.AppDatabase
+
+
+class ChapterRepository {
+
+    private val chapterDao: ChapterDao by lazy { AppDatabase.instance.chapterDao() }
+
+    fun findChaptersByGradeLevel(grade: Int = 1) = chapterDao.findChaptersByGradeLevel(grade)
+
+}

+ 11 - 0
app/src/main/java/com/bearya/data/repository/SectionRepository.kt

@@ -0,0 +1,11 @@
+package com.bearya.data.repository
+
+import com.bearya.data.dao.SectionDao
+import com.bearya.kids.AppDatabase
+
+class SectionRepository {
+
+    private val sectionDao:SectionDao by lazy { AppDatabase.instance.sectionDao() }
+
+    fun findSectionByChapterId(chapterId : Int = 1) = sectionDao.findSectionsByChapterId(chapterId)
+}

+ 22 - 0
app/src/main/java/com/bearya/kids/App.kt

@@ -0,0 +1,22 @@
+package com.bearya.kids
+
+import android.app.Application
+import androidx.databinding.DataBindingUtil
+import com.orhanobut.logger.Logger
+import library.BindingComponent
+import library.LoggerComponent
+
+class App : Application() {
+
+    override fun onCreate() {
+        super.onCreate()
+
+        LoggerComponent.init()
+//        BuglyComponent.init(applicationContext)
+        AppDatabase.init(applicationContext)
+
+        DataBindingUtil.setDefaultComponent(BindingComponent)
+
+    }
+
+}

+ 42 - 0
app/src/main/java/com/bearya/kids/AppDatabase.kt

@@ -0,0 +1,42 @@
+package com.bearya.kids
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import com.bearya.data.dao.AnalysisDao
+import com.bearya.data.dao.ChapterDao
+import com.bearya.data.dao.SectionDao
+import com.bearya.data.entity.Analysis
+import com.bearya.data.entity.Chapter
+import com.bearya.data.entity.Section
+
+@Database(entities = [
+    Chapter::class,
+    Section::class,
+    Analysis::class
+], version = 1, exportSchema = true)
+abstract class AppDatabase : RoomDatabase() {
+
+    abstract fun chapterDao(): ChapterDao
+    abstract fun sectionDao(): SectionDao
+    abstract fun analysisDao(): AnalysisDao
+
+    companion object {
+
+        private lateinit var mContext: Context
+
+        val instance: AppDatabase by lazy {
+            val room = Room.databaseBuilder(mContext, AppDatabase::class.java, "kids.db")
+                    .createFromAsset("database/kids.db")
+            if (BuildConfig.DEBUG) room.fallbackToDestructiveMigration()
+            room.build()
+        }
+
+        fun init(context: Context) {
+            this.mContext = context
+        }
+
+    }
+
+}

+ 99 - 0
app/src/main/java/com/bearya/kids/analysis/AnalysisAdapter.kt

@@ -0,0 +1,99 @@
+package com.bearya.kids.analysis
+
+import android.text.TextUtils
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.content.res.ResourcesCompat
+import androidx.databinding.DataBindingUtil
+import androidx.paging.PagedListAdapter
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import com.bearya.data.entity.Analysis
+import com.bearya.kids.R
+import com.bearya.kids.databinding.ItemSectionBinding
+import com.bearya.kids.databinding.ItemSectionPreviewBinding
+import library.OnItemClickListener
+import library.ext.assetsPath
+
+class AnalysisAdapter(private val dirName: String?) : PagedListAdapter<Analysis, AnalysisViewHolder>(object : DiffUtil.ItemCallback<Analysis>() {
+
+    override fun areItemsTheSame(oldItem: Analysis, newItem: Analysis): Boolean = oldItem.id == newItem.id
+
+    override fun areContentsTheSame(oldItem: Analysis, newItem: Analysis): Boolean =
+            TextUtils.equals(oldItem.name, newItem.name)
+
+}) {
+
+    var onItemClickListener: OnItemClickListener<Analysis>? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnalysisViewHolder = AnalysisViewHolder(
+            ItemSectionBinding.inflate(LayoutInflater.from(parent.context), parent, false).root
+    )
+
+    override fun onBindViewHolder(holder: AnalysisViewHolder, position: Int) {
+        val item = getItem(position)
+        holder.bindView?.image = "chapter/${dirName}/${item?.name}.webp".assetsPath()
+        holder.itemView.setOnClickListener { onItemClickListener?.invoke(it, item, position) }
+    }
+
+}
+
+class AnalysisViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+    val bindView: ItemSectionBinding? = DataBindingUtil.bind(itemView)
+}
+
+class AnalysisPreviewAdapter(private val dirName: String?) : PagedListAdapter<Analysis, AnalysisPreviewViewHolder>(object : DiffUtil.ItemCallback<Analysis>() {
+
+    override fun areItemsTheSame(oldItem: Analysis, newItem: Analysis): Boolean = oldItem.id == newItem.id
+
+    override fun areContentsTheSame(oldItem: Analysis, newItem: Analysis): Boolean =
+            TextUtils.equals(oldItem.name, newItem.name)
+
+}) {
+
+    var onItemClickListener: OnItemClickListener<Analysis>? = null
+
+    private var currentFocusItemIndex = 0
+    private var recyclerView: RecyclerView? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnalysisPreviewViewHolder = AnalysisPreviewViewHolder(
+            ItemSectionPreviewBinding.inflate(LayoutInflater.from(parent.context), parent, false).root
+    )
+
+    override fun onBindViewHolder(holder: AnalysisPreviewViewHolder, position: Int) {
+        val item = getItem(position)
+
+        val index = position + 1
+        holder.bindView?.index = "$index"
+
+        holder.bindView?.image = "chapter/${dirName}/${item?.name}.webp".assetsPath()
+
+        holder.bindView?.cover?.setBorderColor(ResourcesCompat.getColor(holder.itemView.context.resources,
+                if (position == currentFocusItemIndex) R.color.colorBlue else R.color.colorWhite, null))
+
+        holder.itemView.setOnClickListener { v -> onItemClickListener?.invoke(v, item, position) }
+    }
+
+    fun setSelectedIndex(position: Int) {
+        notifyItemChanged(currentFocusItemIndex)
+        currentFocusItemIndex = position
+        notifyItemChanged(currentFocusItemIndex)
+        recyclerView?.smoothScrollToPosition(currentFocusItemIndex)
+    }
+
+    override fun onAttachedToRecyclerView(rv: RecyclerView) {
+        super.onAttachedToRecyclerView(rv)
+        recyclerView = rv
+    }
+
+    override fun onDetachedFromRecyclerView(rv: RecyclerView) {
+        super.onDetachedFromRecyclerView(rv)
+        recyclerView = null
+    }
+
+}
+
+class AnalysisPreviewViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+    val bindView: ItemSectionPreviewBinding? = DataBindingUtil.bind(itemView)
+}

+ 106 - 0
app/src/main/java/com/bearya/kids/analysis/AnalysisFragment.kt

@@ -0,0 +1,106 @@
+package com.bearya.kids.analysis
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.observe
+import androidx.navigation.findNavController
+import androidx.navigation.fragment.navArgs
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.viewpager2.widget.ViewPager2
+import com.bearya.data.entity.Analysis
+import com.bearya.kids.databinding.FragmentAnalysisBinding
+import kotlinx.coroutines.*
+import library.ext.setData
+
+class AnalysisFragment : Fragment() {
+
+    private lateinit var bindView: FragmentAnalysisBinding
+    private val viewModel: AnalysisViewModel by viewModels()
+    private val args: AnalysisFragmentArgs by navArgs()
+
+    private val playAdapter: AnalysisAdapter by lazy { AnalysisAdapter(args.chapterName) }
+    private val previewAdapter: AnalysisPreviewAdapter by lazy { AnalysisPreviewAdapter(args.chapterName) }
+
+    private val pageChangeCallback by lazy {
+        object : ViewPager2.OnPageChangeCallback() {
+            override fun onPageSelected(position: Int) {
+                previewAdapter.setSelectedIndex(position)
+            }
+        }
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        viewModel.sectionId.setData(args.sectionId)
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        bindView = FragmentAnalysisBinding.inflate(inflater, container, false)
+        bindView.lifecycleOwner = viewLifecycleOwner
+        return bindView.root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        bindView.back.setOnClickListener { it.findNavController().navigateUp() }
+
+        bindView.playPager.adapter = playAdapter
+        bindView.playPager.registerOnPageChangeCallback(pageChangeCallback)
+        playAdapter.onItemClickListener = { _: View, _: Analysis?, _: Int ->
+
+            cancelHideSectionPreview()
+
+            val visibility = bindView.previewRecyclerView.visibility.takeIf { it == View.VISIBLE }
+                    ?.let { View.GONE } ?: View.VISIBLE
+            bindView.previewRecyclerView.visibility = visibility
+            bindView.back.visibility = visibility
+
+            hideSectionPreview()
+
+        }
+
+        bindView.previewRecyclerView.layoutManager =
+                LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
+        bindView.previewRecyclerView.adapter = previewAdapter
+        previewAdapter.onItemClickListener = { _: View, _: Analysis?, position: Int ->
+            bindView.playPager.currentItem = position
+        }
+
+        viewModel.analysis.observe(viewLifecycleOwner) {
+            playAdapter.submitList(it)
+            previewAdapter.submitList(it)
+        }
+    }
+
+    override fun onResume() {
+        super.onResume()
+        hideSectionPreview()
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        bindView.playPager.unregisterOnPageChangeCallback(pageChangeCallback)
+    }
+
+    private var launchDelayHide: Job? = null
+
+    private fun hideSectionPreview() {
+        launchDelayHide = lifecycleScope.launch {
+            delay(3000)
+            withContext(Dispatchers.Main) {
+                bindView.playPager.visibility.takeIf { it == View.VISIBLE }?.also {
+                    bindView.playPager.visibility = View.GONE
+                    bindView.back.visibility = View.GONE
+                }
+            }
+        }
+    }
+
+    private fun cancelHideSectionPreview() = launchDelayHide?.cancel()
+
+}

+ 24 - 0
app/src/main/java/com/bearya/kids/analysis/AnalysisViewModel.kt

@@ -0,0 +1,24 @@
+package com.bearya.kids.analysis
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.switchMap
+import androidx.paging.PagedList
+import com.bearya.data.entity.Analysis
+import com.bearya.data.entity.Chapter
+import com.bearya.data.repository.AnalysisRepository
+import com.bearya.data.repository.ChapterRepository
+import library.ext.toLiveData
+
+class AnalysisViewModel : ViewModel() {
+
+    private val repository: AnalysisRepository by lazy { AnalysisRepository() }
+
+    val sectionId : MutableLiveData<Int> by lazy { MutableLiveData<Int>() }
+
+    val analysis: LiveData<PagedList<Analysis>>  = sectionId.switchMap {
+        repository.findAnalysisBySectionId(it).toLiveData()
+    }
+
+}

+ 47 - 0
app/src/main/java/com/bearya/kids/chapter/ChapterAdapter.kt

@@ -0,0 +1,47 @@
+package com.bearya.kids.chapter
+
+import android.text.TextUtils
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.paging.PagedListAdapter
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import com.bearya.data.entity.Chapter
+import com.bearya.kids.databinding.ItemChapterBinding
+import library.OnItemClickListener
+import library.ext.assetsPath
+
+class ChapterAdapter : PagedListAdapter<Chapter, UnitViewHolder>(object : DiffUtil.ItemCallback<Chapter>() {
+
+    override fun areItemsTheSame(oldItem: Chapter, newItem: Chapter): Boolean {
+        return oldItem.id == newItem.id
+    }
+
+    override fun areContentsTheSame(oldItem: Chapter, newItem: Chapter): Boolean {
+        return TextUtils.equals(oldItem.name, newItem.name) && oldItem.lock == newItem.lock
+    }
+
+}) {
+
+    var onItemClickListener: OnItemClickListener<Chapter>? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UnitViewHolder = UnitViewHolder(
+            ItemChapterBinding.inflate(LayoutInflater.from(parent.context), parent, false).root
+    )
+
+    override fun onBindViewHolder(holder: UnitViewHolder, position: Int) {
+        val item = getItem(position)
+        holder.bindView?.name = item?.name
+        holder.bindView?.cover = "chapter/${item?.name}/${item?.name}.webp".assetsPath()
+        holder.itemView.setOnClickListener { onItemClickListener?.invoke(it, item, position) }
+    }
+
+}
+
+class UnitViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+
+    val bindView: ItemChapterBinding? = DataBindingUtil.bind(itemView)
+
+}

+ 71 - 0
app/src/main/java/com/bearya/kids/chapter/ChapterFragment.kt

@@ -0,0 +1,71 @@
+package com.bearya.kids.chapter
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.observe
+import androidx.navigation.findNavController
+import androidx.navigation.fragment.navArgs
+import androidx.recyclerview.widget.GridLayoutManager
+import com.bearya.data.entity.Chapter
+import com.bearya.kids.R
+import com.bearya.kids.databinding.FragmentChapterBinding
+import library.ext.setData
+
+class ChapterFragment : Fragment() {
+
+    private val gradeLevelPrimary = 1 // 小班
+    private val gradeLevelMiddle = 2 // 中班
+    private val gradeLevelLarge = 3 // 大班
+
+    private lateinit var bindView: FragmentChapterBinding
+    private val viewModel: ChapterViewModel by viewModels()
+    private val args: ChapterFragmentArgs by navArgs()
+
+    private val chapterAdapter: ChapterAdapter by lazy {
+        ChapterAdapter().apply {
+            onItemClickListener = { view: View, item: Chapter?, _: Int ->
+                view.findNavController().navigate(
+                        ChapterFragmentDirections.actionChapterFragmentToSectionFragment(item?.id
+                                ?: 1, item?.name)
+                )
+            }
+        }
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        viewModel.gradeLevel.setData(args.grade)
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        bindView = DataBindingUtil.inflate(inflater, R.layout.fragment_chapter, container, false)
+        bindView.lifecycleOwner = viewLifecycleOwner
+        return bindView.root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        bindView.gradeName.text = when (args.grade) {
+            gradeLevelPrimary -> "小班"
+            gradeLevelMiddle -> "中班"
+            gradeLevelLarge -> "大班"
+            else -> "幼儿园"
+        }
+
+        bindView.back.setOnClickListener { v -> v.findNavController().navigateUp() }
+        bindView.chapter.layoutManager = GridLayoutManager(context, 2, GridLayoutManager.HORIZONTAL, false)
+        bindView.chapter.adapter = chapterAdapter
+
+        viewModel.chapters.observe(viewLifecycleOwner) {
+            chapterAdapter.submitList(it)
+        }
+
+    }
+
+}

+ 21 - 0
app/src/main/java/com/bearya/kids/chapter/ChapterViewModel.kt

@@ -0,0 +1,21 @@
+package com.bearya.kids.chapter
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.switchMap
+import androidx.paging.PagedList
+import com.bearya.data.entity.Chapter
+import com.bearya.data.repository.ChapterRepository
+import library.ext.toLiveData
+
+class ChapterViewModel : ViewModel() {
+
+    private val chapterRepository: ChapterRepository by lazy { ChapterRepository() }
+
+    val gradeLevel : MutableLiveData<Int> by lazy { MutableLiveData<Int>() }
+
+    val chapters: LiveData<PagedList<Chapter>>  = gradeLevel.switchMap {
+        chapterRepository.findChaptersByGradeLevel(it).toLiveData()
+    }
+}

+ 46 - 0
app/src/main/java/com/bearya/kids/grade/GradeFragment.kt

@@ -0,0 +1,46 @@
+package com.bearya.kids.grade
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.navigation.findNavController
+import com.bearya.kids.BuildConfig
+import com.bearya.kids.R
+import com.bearya.kids.databinding.FragmentGradeBinding
+
+class GradeFragment : Fragment(), View.OnClickListener {
+
+    private val gradeLevelPrimary = 1 // 小班
+    private val gradeLevelMiddle = 2 // 中班
+    private val gradeLevelLarge = 3 // 大班
+
+    private lateinit var bindView: FragmentGradeBinding
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        bindView = FragmentGradeBinding.inflate(inflater, container, false)
+        bindView.lifecycleOwner = viewLifecycleOwner
+        return bindView.root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        val versionName = "当前版本 : v ${BuildConfig.VERSION_NAME}.${BuildConfig.VERSION_CODE}"
+        bindView.version.text = versionName
+        bindView.gradePrimary.setOnClickListener(this)
+        bindView.gradeMiddle.setOnClickListener(this)
+        bindView.gradeLarge.setOnClickListener(this)
+        bindView.back.setOnClickListener(this)
+    }
+
+    override fun onClick(v: View?) {
+        when (v?.id) {
+            R.id.grade_primary -> v.findNavController().navigate(GradeFragmentDirections.actionGradeFragmentToChapterFragment(gradeLevelPrimary))
+            R.id.grade_middle -> v.findNavController().navigate(GradeFragmentDirections.actionGradeFragmentToChapterFragment(gradeLevelMiddle))
+            R.id.grade_large -> v.findNavController().navigate(GradeFragmentDirections.actionGradeFragmentToChapterFragment(gradeLevelLarge))
+            R.id.back -> requireActivity().finish()
+        }
+    }
+
+}

+ 49 - 0
app/src/main/java/com/bearya/kids/main/MainActivity.kt

@@ -0,0 +1,49 @@
+package com.bearya.kids.main
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.navigation.Navigation
+import com.bearya.kids.R
+import com.bearya.kids.databinding.ActivityMainBinding
+import com.orhanobut.logger.Logger
+
+class MainActivity : AppCompatActivity() {
+
+    private lateinit var bindView: ActivityMainBinding
+
+    private val fragmentLifecycleCallback = object : FragmentManager.FragmentLifecycleCallbacks() {
+
+        override fun onFragmentCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) {
+            if ("SupportRequestManagerFragment" != f.javaClass.simpleName) {
+                Logger.t("Current").v("================= ${f.javaClass.simpleName} onFragmentCreated =================")
+            }
+        }
+
+        override fun onFragmentDestroyed(fm: FragmentManager, f: Fragment) {
+            if ("SupportRequestManagerFragment" != f.javaClass.simpleName) {
+                Logger.t("Current").v("================= ${f.javaClass.simpleName} onFragmentDestroyed =================")
+            }
+        }
+
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        bindView = DataBindingUtil.setContentView(this, R.layout.activity_main)
+        Logger.t("Current").v("================= MainActivity onCreate =================")
+        // recursive : 是否递归Fragment中的Fragment,就是Fragment栈中的Fragment
+        supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallback, true)
+    }
+
+    override fun onSupportNavigateUp(): Boolean = Navigation.findNavController(this, R.id.container_fragment).navigateUp()
+
+    override fun onDestroy() {
+        super.onDestroy()
+        supportFragmentManager.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallback)
+        Logger.t("Current").v("================= MainActivity onDestroyed =================")
+    }
+
+}

+ 102 - 0
app/src/main/java/com/bearya/kids/section/SectionAdapter.kt

@@ -0,0 +1,102 @@
+package com.bearya.kids.section
+
+import android.text.TextUtils
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.content.res.ResourcesCompat
+import androidx.databinding.DataBindingUtil
+import androidx.paging.PagedListAdapter
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import com.bearya.data.entity.Section
+import com.bearya.kids.R
+import com.bearya.kids.databinding.ItemSectionBinding
+import com.bearya.kids.databinding.ItemSectionPreviewBinding
+import library.OnItemClickListener
+import library.ext.assetsPath
+
+class SectionQuestionAdapter(private val dirName: String?) : PagedListAdapter<Section, SectionQuestionViewHolder>(object : DiffUtil.ItemCallback<Section>() {
+
+    override fun areItemsTheSame(oldItem: Section, newItem: Section): Boolean = oldItem.id == newItem.id
+
+    override fun areContentsTheSame(oldItem: Section, newItem: Section): Boolean =
+            TextUtils.equals(oldItem.name, newItem.name) && oldItem.lock == newItem.lock
+
+}) {
+
+    var onItemClickListener: OnItemClickListener<Section>? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionQuestionViewHolder = SectionQuestionViewHolder(
+            ItemSectionBinding.inflate(LayoutInflater.from(parent.context), parent, false).root
+    )
+
+    override fun onBindViewHolder(holder: SectionQuestionViewHolder, position: Int) {
+        val item = getItem(position)
+        holder.bindView?.image = "chapter/${dirName}/${item?.name}.webp".assetsPath()
+        holder.itemView.setOnClickListener { onItemClickListener?.invoke(it, item, position) }
+    }
+
+    fun getItemData(position:Int) = getItem(position)
+
+}
+
+class SectionQuestionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+    val bindView: ItemSectionBinding? = DataBindingUtil.bind(itemView)
+}
+
+class SectionPreviewAdapter(private val dirName: String?) : PagedListAdapter<Section, SectionPreviewViewHolder>(object : DiffUtil.ItemCallback<Section>() {
+
+    override fun areItemsTheSame(oldItem: Section, newItem: Section): Boolean = oldItem.id == newItem.id
+
+    override fun areContentsTheSame(oldItem: Section, newItem: Section): Boolean =
+            TextUtils.equals(oldItem.name, newItem.name) && oldItem.lock == newItem.lock
+
+}) {
+
+    var onItemClickListener: OnItemClickListener<Section>? = null
+
+    private var currentFocusItemIndex = 0
+    private var recyclerView: RecyclerView? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionPreviewViewHolder = SectionPreviewViewHolder(
+            ItemSectionPreviewBinding.inflate(LayoutInflater.from(parent.context), parent, false).root
+    )
+
+    override fun onBindViewHolder(holder: SectionPreviewViewHolder, position: Int) {
+        val item = getItem(position)
+
+        val index = position + 1
+        holder.bindView?.index = "$index"
+
+        holder.bindView?.image = "chapter/${dirName}/${item?.name}.webp".assetsPath()
+
+        holder.bindView?.cover?.setBorderColor(ResourcesCompat.getColor(holder.itemView.context.resources,
+                if (position == currentFocusItemIndex) R.color.colorBlue else R.color.colorWhite, null))
+
+        holder.itemView.setOnClickListener { v -> onItemClickListener?.invoke(v, item, position) }
+    }
+
+    fun setSelectedIndex(position: Int) {
+        notifyItemChanged(currentFocusItemIndex)
+        currentFocusItemIndex = position
+        notifyItemChanged(currentFocusItemIndex)
+        recyclerView?.smoothScrollToPosition(currentFocusItemIndex)
+    }
+
+    override fun onAttachedToRecyclerView(rv: RecyclerView) {
+        super.onAttachedToRecyclerView(rv)
+        recyclerView = rv
+    }
+
+    override fun onDetachedFromRecyclerView(rv: RecyclerView) {
+        super.onDetachedFromRecyclerView(rv)
+        recyclerView = null
+    }
+
+
+}
+
+class SectionPreviewViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+    val bindView: ItemSectionPreviewBinding? = DataBindingUtil.bind(itemView)
+}

+ 123 - 0
app/src/main/java/com/bearya/kids/section/SectionFragment.kt

@@ -0,0 +1,123 @@
+package com.bearya.kids.section
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.observe
+import androidx.navigation.findNavController
+import androidx.navigation.fragment.navArgs
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.viewpager2.widget.ViewPager2
+import com.bearya.data.entity.Section
+import com.bearya.kids.databinding.FragmentSectionBinding
+import kotlinx.coroutines.*
+import library.ext.setData
+
+class SectionFragment : Fragment() {
+
+    private lateinit var bindView: FragmentSectionBinding
+    private val viewModel: SectionViewModel by viewModels()
+    private val args: SectionFragmentArgs by navArgs()
+
+    private val sectionQuestionAdapter: SectionQuestionAdapter by lazy { SectionQuestionAdapter(args.chapterName) }
+    private val previewAdapter: SectionPreviewAdapter by lazy { SectionPreviewAdapter(args.chapterName) }
+
+    private val pageChangeCallback by lazy {
+        object : ViewPager2.OnPageChangeCallback() {
+            override fun onPageSelected(position: Int) {
+                previewAdapter.setSelectedIndex(position)
+            }
+        }
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        viewModel.chapterId.setData(args.chapterId)
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        bindView = FragmentSectionBinding.inflate(inflater, container, false)
+        bindView.lifecycleOwner = viewLifecycleOwner
+        return bindView.root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+
+        bindView.playPager.adapter = sectionQuestionAdapter
+
+        bindView.playPager.registerOnPageChangeCallback(pageChangeCallback)
+
+        sectionQuestionAdapter.onItemClickListener = { _: View, _: Section?, _: Int ->
+            cancelHideSectionPreview()
+
+            val visibility = bindView.playList.visibility.takeIf { it == View.VISIBLE }
+                    ?.let { View.GONE } ?: View.VISIBLE
+            bindView.playList.visibility = visibility
+            bindView.back.visibility = visibility
+            bindView.analysis.visibility = visibility
+
+            hideSectionPreview()
+
+        }
+
+        bindView.playList.layoutManager =
+                LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
+        bindView.playList.adapter = previewAdapter
+
+        previewAdapter.onItemClickListener = { _: View, _: Section?, position: Int ->
+            bindView.playPager.currentItem = position
+        }
+
+        viewModel.sections.observe(viewLifecycleOwner) {
+            sectionQuestionAdapter.submitList(it)
+            previewAdapter.submitList(it)
+        }
+
+        bindView.back.setOnClickListener { v -> v.findNavController().navigateUp() }
+
+        bindView.analysis.setOnClickListener {
+            val itemData = sectionQuestionAdapter.getItemData(bindView.playPager.currentItem)
+
+            if (itemData?.hasAnalysis == true) {
+                val action = SectionFragmentDirections.actionSectionFragmentToAnalysisFragment(itemData.id ?: 1 , args.chapterName)
+                it.findNavController().navigate(action)
+            } else {
+                Toast.makeText(context, "暂无该答案", Toast.LENGTH_LONG).show()
+            }
+        }
+
+    }
+
+    override fun onResume() {
+        super.onResume()
+        hideSectionPreview()
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        bindView.playPager.unregisterOnPageChangeCallback(pageChangeCallback)
+    }
+
+    private var launchDelayHide: Job? = null
+
+    private fun hideSectionPreview() {
+        launchDelayHide = lifecycleScope.launch {
+            delay(3000)
+            withContext(Dispatchers.Main) {
+                bindView.playList.visibility.takeIf { it == View.VISIBLE }?.also {
+                    bindView.playList.visibility = View.GONE
+                    bindView.back.visibility = View.GONE
+                    bindView.analysis.visibility = View.GONE
+                }
+            }
+        }
+    }
+
+    private fun cancelHideSectionPreview() = launchDelayHide?.cancel()
+
+}

+ 21 - 0
app/src/main/java/com/bearya/kids/section/SectionViewModel.kt

@@ -0,0 +1,21 @@
+package com.bearya.kids.section
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.switchMap
+import androidx.paging.PagedList
+import com.bearya.data.entity.Section
+import com.bearya.data.repository.SectionRepository
+import library.ext.toLiveData
+
+class SectionViewModel : ViewModel() {
+
+    private val repository: SectionRepository by lazy { SectionRepository() }
+
+    val chapterId : MutableLiveData<Int> by lazy { MutableLiveData<Int>() }
+
+    val sections :LiveData<PagedList<Section>> = chapterId.switchMap {
+        repository.findSectionByChapterId(it).toLiveData()
+    }
+}

+ 38 - 0
app/src/main/java/com/bearya/kids/splash/SplashFragment.kt

@@ -0,0 +1,38 @@
+package com.bearya.kids.splash
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.lifecycleScope
+import androidx.navigation.Navigation
+import com.bearya.kids.R
+import com.bearya.kids.databinding.FragmentSplashBinding
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.withContext
+
+class SplashFragment : Fragment() {
+
+    private lateinit var bindView: FragmentSplashBinding
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        lifecycleScope.launchWhenResumed {
+            delay(3000)
+            withContext(Dispatchers.Main) {
+                Navigation.findNavController(requireActivity(), R.id.container_fragment).navigate(R.id.action_splashFragment_to_gradeFragment)
+            }
+        }
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        bindView = FragmentSplashBinding.inflate(inflater, container, false)
+        bindView.lifecycleOwner = viewLifecycleOwner
+        return bindView.root
+    }
+
+
+}

+ 113 - 0
app/src/main/java/library/BindingComponent.kt

@@ -0,0 +1,113 @@
+package library
+
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.text.TextUtils
+import android.widget.ImageView
+import androidx.databinding.BindingAdapter
+import androidx.databinding.BindingConversion
+import androidx.databinding.DataBindingComponent
+import com.bumptech.glide.Glide
+import com.bumptech.glide.Priority
+import com.bumptech.glide.load.engine.DiskCacheStrategy
+import com.bumptech.glide.load.resource.bitmap.CircleCrop
+import com.bumptech.glide.request.RequestOptions
+import jp.wasabeef.glide.transformations.RoundedCornersTransformation
+import library.ext.dp2px
+
+/**
+ * DataBinding 默认控件组合
+ */
+object BindingComponent : DataBindingComponent {
+
+    /**
+     * 加载头像或者图标
+     *
+     * @param imageView   加载控件
+     * @param url         加载图片的地址
+     * @param placeHolder 控件的占位符
+     * @param error       加载失败的显示错误图片
+     */
+    @JvmStatic
+    @BindingAdapter(value = ["load", "placeholder", "error"], requireAll = false)
+    fun load(imageView: ImageView, url: String?, placeHolder: Drawable?, error: Drawable?) {
+        if (TextUtils.isEmpty(url)) {
+            imageView.setImageDrawable(placeHolder ?: error)
+        } else {
+            val requestOptions = RequestOptions().also {
+                if (placeHolder != null) it.placeholder(placeHolder)
+                if (error != null) it.error(error)
+                it.priority(Priority.HIGH)//优先级
+                it.diskCacheStrategy(DiskCacheStrategy.NONE)//缓存策略
+            }
+
+            Glide.with(imageView.context).load(url).apply(requestOptions).into(imageView)
+        }
+    }
+
+    /**
+     * 加载圆角方形头像或者图标
+     *
+     * @param imageView   加载控件
+     * @param url         加载图片的地址
+     * @param radius      图片剪切的圆角值
+     * @param placeHolder 控件的占位符
+     * @param error       加载失败的显示错误图片
+     */
+    @JvmStatic
+    @BindingAdapter(value = ["round", "radius", "placeholder", "error"], requireAll = false)
+    fun loadRound(imageView: ImageView, url: String?, radius: Int?, placeHolder: Drawable?, error: Drawable?) {
+        if (TextUtils.isEmpty(url)) {
+            imageView.setImageDrawable(placeHolder ?: error)
+        } else {
+            val transformation = RoundedCornersTransformation(radius?.dp2px(imageView.context) ?: 8, 8)
+
+            val requestOptions = RequestOptions().also {
+                it.centerCrop()
+                if (placeHolder != null) it.placeholder(placeHolder)
+                if (error != null) it.error(error)
+                it.priority(Priority.HIGH)//优先级
+                it.diskCacheStrategy(DiskCacheStrategy.NONE)//缓存策略
+                it.transform(transformation)//转化为圆形
+            }
+            Glide.with(imageView.context).load(url).apply(requestOptions).into(imageView)
+        }
+    }
+
+    /**
+     * 加载圆形头像或者图标
+     *
+     * @param imageView   加载控件
+     * @param url         加载图片的地址
+     * @param placeHolder 控件的占位符
+     * @param error       加载失败的显示错误图片
+     */
+    @JvmStatic
+    @BindingAdapter(value = ["circle", "placeholder", "error"], requireAll = false)
+    fun loadCircle(imageView: ImageView, url: String?, placeHolder: Drawable?, error: Drawable?) {
+        if (TextUtils.isEmpty(url)) {
+            imageView.setImageDrawable(placeHolder ?: error)
+        } else {
+            val transformation = CircleCrop()
+            val requestOptions = RequestOptions().also {
+                it.centerCrop()
+                if (placeHolder != null) it.placeholder(placeHolder)
+                if (error != null) it.error(error)
+                it.priority(Priority.HIGH)//优先级
+                it.diskCacheStrategy(DiskCacheStrategy.NONE)//缓存策略
+                it.transform(transformation)
+            }//转化为圆形
+            Glide.with(imageView.context).load(url).apply(requestOptions).into(imageView)
+        }
+    }
+
+    /**
+     * 对将Color转换Drawable类型
+     */
+    @JvmStatic
+    @BindingConversion
+    fun convertColorToDrawable(colorRes: Int): ColorDrawable {
+        return ColorDrawable(colorRes)
+    }
+
+}

+ 18 - 0
app/src/main/java/library/BuglyComponent.kt

@@ -0,0 +1,18 @@
+package library
+
+import android.content.Context
+import android.os.Build
+import com.bearya.kids.BuildConfig
+import com.tencent.bugly.Bugly
+import com.tencent.bugly.BuglyStrategy
+
+object BuglyComponent {
+
+    fun init(context: Context) {
+        // 开发设备设置
+        Bugly.setIsDevelopmentDevice(context, BuildConfig.DEBUG)
+        Bugly.init(context, BuildConfig.BuglyAppKey, BuildConfig.DEBUG, BuglyStrategy().apply { appVersion = BuildConfig.VERSION_NAME })
+        Bugly.setUserId(context, Build.SERIAL)
+    }
+
+}

+ 7 - 0
app/src/main/java/library/ListenerComponent.kt

@@ -0,0 +1,7 @@
+package library
+
+import android.view.View
+
+typealias OnItemClickListener<T> = (view: View, item: T?, position: Int) -> Unit
+
+typealias OnClickListener = () -> Unit

+ 41 - 0
app/src/main/java/library/LoggerComponent.kt

@@ -0,0 +1,41 @@
+package library
+
+import com.bearya.kids.BuildConfig
+import com.orhanobut.logger.AndroidLogAdapter
+import com.orhanobut.logger.DiskLogAdapter
+import com.orhanobut.logger.Logger
+import com.orhanobut.logger.PrettyFormatStrategy
+import com.orhanobut.logger.CsvFormatStrategy
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+
+object LoggerComponent {
+
+    fun init() {
+        val formatStrategy = PrettyFormatStrategy.newBuilder()
+                .showThreadInfo(true)
+                .methodCount(5)
+                .tag("LOGGER")
+                .build()
+
+        Logger.addLogAdapter(object : AndroidLogAdapter(formatStrategy) {
+            override fun isLoggable(priority: Int, tag: String?): Boolean {
+                return BuildConfig.DEBUG
+            }
+        })
+
+        val csvFormatStrategy = CsvFormatStrategy.newBuilder()
+                .tag("LOGGER")
+                .date(Date())
+                .dateFormat(SimpleDateFormat("yyyy.MM.dd HH:mm:ss.SSS", Locale.CHINA))
+                .build()
+
+        Logger.addLogAdapter(object : DiskLogAdapter(csvFormatStrategy) {
+            override fun isLoggable(priority: Int, tag: String?): Boolean {
+                return BuildConfig.DEBUG.not()
+            }
+        })
+    }
+
+}

+ 10 - 0
app/src/main/java/library/ext/IntExt.kt

@@ -0,0 +1,10 @@
+package library.ext
+
+import android.content.Context
+
+fun Int?.dp2px(context: Context): Int?  {
+
+    val scale = context.resources.displayMetrics.density;
+    return this?.times(scale)?.plus(0.5f)?.toInt();
+
+}

+ 22 - 0
app/src/main/java/library/ext/LiveDataExt.kt

@@ -0,0 +1,22 @@
+package library.ext
+
+import android.os.Looper
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.paging.DataSource
+import androidx.paging.LivePagedListBuilder
+import androidx.paging.PagedList
+
+/**
+ * 线程分配设置数据到LiveData
+ */
+fun <T> MutableLiveData<T>.setData(data: T?) =
+        if (Looper.getMainLooper().thread == Thread.currentThread()) {
+            setValue(data)
+        } else {
+            postValue(data)
+        }
+
+fun <T, K> DataSource.Factory<T, K>.toLiveData(pageSize: Int = 20,
+                                               callback: PagedList.BoundaryCallback<K>? = null): LiveData<PagedList<K>> =
+        LivePagedListBuilder(this, pageSize).setBoundaryCallback(callback).build()

+ 14 - 0
app/src/main/java/library/ext/PagedListExt.kt

@@ -0,0 +1,14 @@
+package library.ext
+
+import androidx.paging.PagedList
+
+fun PagedList.Config.Builder.create(pageSize: Int = 10,
+                                    prefetchDistance: Int = 10,
+                                    initialLoadSizeHint: Int = 10,
+                                    enablePlaceholders: Boolean = false): PagedList.Config =
+        PagedList.Config.Builder()
+                .setPageSize(pageSize)
+                .setPrefetchDistance(prefetchDistance)
+                .setInitialLoadSizeHint(initialLoadSizeHint)
+                .setEnablePlaceholders(enablePlaceholders)
+                .build()

+ 12 - 0
app/src/main/java/library/ext/StringExt.kt

@@ -0,0 +1,12 @@
+package library.ext
+
+import android.content.Context
+import java.io.File
+
+fun String?.assetsPath(): String =  "file:///android_asset/$this"
+
+fun String?.cachePath(context: Context?): String = "${context?.cacheDir}${File.separator}$this"
+
+fun String?.downloadPath(context: Context?): String = "${context?.filesDir}${File.separator}$this"
+
+fun emotion(emotion: String?): String? = emotion?.takeIf { it.isNotBlank() }?.let { "emotion/${emotion}.json"  }

+ 58 - 0
app/src/main/java/library/ext/ViewExt.kt

@@ -0,0 +1,58 @@
+package library.ext
+
+import android.animation.Keyframe
+import android.animation.ObjectAnimator
+import android.animation.PropertyValuesHolder
+import android.view.View
+import android.widget.ImageView
+
+fun withShow(vararg view: View) = view.forEach { it.show() }
+
+fun withGone(vararg view: View) = view.forEach { it.gone() }
+
+fun View.show() {
+    this.visibility = View.VISIBLE
+}
+
+fun ImageView.showImageResource(imageResource: Int) {
+    this.show()
+    this.setImageResource(imageResource)
+}
+
+fun View.gone() {
+    this.visibility = View.GONE
+}
+
+fun View.startShake(scaleSmall: Float = 0.9f, scaleLarge: Float = 1.1f, shakeDegrees: Float = 10f, duration: Long = 1000) {
+    val scaleXValuesHolder = PropertyValuesHolder.ofKeyframe(View.SCALE_X,
+            Keyframe.ofFloat(0f, 1.0f),
+            Keyframe.ofFloat(0.25f, scaleSmall),
+            Keyframe.ofFloat(0.5f, scaleLarge),
+            Keyframe.ofFloat(0.75f, scaleLarge),
+            Keyframe.ofFloat(1.0f, 1.0f)
+    )
+    val scaleYValuesHolder = PropertyValuesHolder.ofKeyframe(View.SCALE_Y,
+            Keyframe.ofFloat(0f, 1.0f),
+            Keyframe.ofFloat(0.25f, scaleSmall),
+            Keyframe.ofFloat(0.5f, scaleLarge),
+            Keyframe.ofFloat(0.75f, scaleLarge),
+            Keyframe.ofFloat(1.0f, 1.0f)
+    )
+    val rotateValuesHolder = PropertyValuesHolder.ofKeyframe(View.ROTATION,
+            Keyframe.ofFloat(0f, 0f),
+            Keyframe.ofFloat(0.1f, -shakeDegrees),
+            Keyframe.ofFloat(0.2f, shakeDegrees),
+            Keyframe.ofFloat(0.3f, -shakeDegrees),
+            Keyframe.ofFloat(0.4f, shakeDegrees),
+            Keyframe.ofFloat(0.5f, -shakeDegrees),
+            Keyframe.ofFloat(0.6f, shakeDegrees),
+            Keyframe.ofFloat(0.7f, -shakeDegrees),
+            Keyframe.ofFloat(0.8f, shakeDegrees),
+            Keyframe.ofFloat(0.9f, -shakeDegrees),
+            Keyframe.ofFloat(1.0f, 0f)
+    )
+    ObjectAnimator.ofPropertyValuesHolder(this, scaleXValuesHolder, scaleYValuesHolder, rotateValuesHolder).apply {
+        this.duration = duration
+    }.start()
+
+}

binární
app/src/main/res/drawable/analysis.webp


binární
app/src/main/res/drawable/back.webp


binární
app/src/main/res/drawable/chapters_bg.webp


binární
app/src/main/res/drawable/grade_bg.webp


binární
app/src/main/res/drawable/grade_large.webp


binární
app/src/main/res/drawable/grade_middle.webp


binární
app/src/main/res/drawable/grade_primary.webp


binární
app/src/main/res/drawable/launcher.webp


binární
app/src/main/res/drawable/row_list_bg.webp


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

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        tools:context=".main.MainActivity">
+
+        <androidx.fragment.app.FragmentContainerView
+            android:id="@+id/container_fragment"
+            android:name="androidx.navigation.fragment.NavHostFragment"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:defaultNavHost="true"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintRight_toRightOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:navGraph="@navigation/main_navigation"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</layout>

+ 57 - 0
app/src/main/res/layout/fragment_analysis.xml

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.viewpager2.widget.ViewPager2
+            android:id="@+id/play_pager"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:orientation="horizontal"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/preview_recycler_view"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:background="@drawable/row_list_bg"
+            android:overScrollMode="never"
+            android:paddingStart="@dimen/dp8"
+            android:paddingEnd="@dimen/dp8"
+            android:scrollbars="none"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHeight_percent="0.3"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="1"
+            tools:itemCount="10"
+            tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
+            tools:listitem="@layout/item_section_preview"
+            tools:spanCount="4" />
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/back"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHeight_percent="0.2"
+            app:layout_constraintHorizontal_bias="0.02"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="0.05"
+            app:layout_constraintWidth_percent="0.15"
+            app:srcCompat="@drawable/back" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+
+</layout>

+ 60 - 0
app/src/main/res/layout/fragment_chapter.xml

@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@drawable/chapters_bg">
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/back"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHeight_percent="0.2"
+            app:layout_constraintHorizontal_bias="0.02"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="0.05"
+            app:layout_constraintWidth_percent="0.15"
+            app:srcCompat="@drawable/back" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/grade_name"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:gravity="center"
+            android:textColor="@color/colorWhite"
+            android:textStyle="bold"
+            app:autoSizeTextType="uniform"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHeight_percent="0.2"
+            app:layout_constraintHorizontal_bias="0.5"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="0.05"
+            app:layout_constraintWidth_percent="0.5"
+            tools:text="@string/grade_primary" />
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/chapter"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:overScrollMode="never"
+            android:scrollbars="none"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/back"
+            tools:itemCount="10"
+            tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
+            tools:listitem="@layout/item_chapter"
+            tools:spanCount="4" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</layout>

+ 134 - 0
app/src/main/res/layout/fragment_grade.xml

@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:scaleType="centerCrop"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:srcCompat="@drawable/grade_bg" />
+
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            android:orientation="horizontal"
+            app:layout_constraintGuide_percent="0.7" />
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/grade_primary"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toBottomOf="@id/horizontal"
+            app:layout_constraintEnd_toStartOf="@+id/grade_middle"
+            app:layout_constraintHeight_percent="0.45"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="1"
+            app:layout_constraintWidth_percent="0.25"
+            app:srcCompat="@drawable/grade_primary" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:padding="@dimen/dp8"
+            android:text="@string/grade_primary"
+            android:textAlignment="center"
+            android:textColor="@color/colorWhite"
+            android:textSize="@dimen/sp40"
+            app:layout_constraintEnd_toEndOf="@id/grade_primary"
+            app:layout_constraintStart_toStartOf="@id/grade_primary"
+            app:layout_constraintTop_toBottomOf="@id/grade_primary" />
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/grade_middle"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toBottomOf="@id/horizontal"
+            app:layout_constraintEnd_toStartOf="@+id/grade_large"
+            app:layout_constraintHeight_percent="0.45"
+            app:layout_constraintStart_toEndOf="@id/grade_primary"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="1"
+            app:layout_constraintWidth_percent="0.25"
+            app:srcCompat="@drawable/grade_middle" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:padding="@dimen/dp8"
+            android:text="@string/grade_middle"
+            android:textAlignment="center"
+            android:textColor="@color/colorWhite"
+            android:textSize="@dimen/sp40"
+            app:layout_constraintEnd_toEndOf="@id/grade_middle"
+            app:layout_constraintStart_toStartOf="@id/grade_middle"
+            app:layout_constraintTop_toBottomOf="@id/grade_middle" />
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/grade_large"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toBottomOf="@id/horizontal"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHeight_percent="0.45"
+            app:layout_constraintStart_toEndOf="@id/grade_middle"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="1"
+            app:layout_constraintWidth_percent="0.25"
+            app:srcCompat="@drawable/grade_large" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:padding="@dimen/dp8"
+            android:text="@string/grade_large"
+            android:textAlignment="center"
+            android:textColor="@color/colorWhite"
+            android:textSize="@dimen/sp40"
+            app:autoSizeTextType="uniform"
+            app:layout_constraintEnd_toEndOf="@id/grade_large"
+            app:layout_constraintStart_toStartOf="@id/grade_large"
+            app:layout_constraintTop_toBottomOf="@id/grade_large" />
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/back"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHeight_percent="0.2"
+            app:layout_constraintHorizontal_bias="0.02"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="0.05"
+            app:layout_constraintWidth_percent="0.15"
+            app:srcCompat="@drawable/back" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/version"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:textColor="@color/colorWhite"
+            android:textSize="@dimen/sp16"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHeight_percent="0.05"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="0.98"
+            tools:text="当前版本" />
+
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</layout>

+ 70 - 0
app/src/main/res/layout/fragment_section.xml

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.viewpager2.widget.ViewPager2
+            android:id="@+id/play_pager"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:orientation="horizontal"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/play_list"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:background="@drawable/row_list_bg"
+            android:overScrollMode="never"
+            android:paddingStart="@dimen/dp8"
+            android:paddingEnd="@dimen/dp8"
+            android:scrollbars="none"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHeight_percent="0.3"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="1"
+            tools:itemCount="10"
+            tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
+            tools:listitem="@layout/item_section_preview"
+            tools:spanCount="4" />
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/back"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHeight_percent="0.2"
+            app:layout_constraintHorizontal_bias="0.02"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="0.05"
+            app:layout_constraintWidth_percent="0.15"
+            app:srcCompat="@drawable/back" />
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/analysis"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHeight_percent="0.2"
+            app:layout_constraintHorizontal_bias="0.98"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="0.05"
+            app:layout_constraintWidth_percent="0.15"
+            app:srcCompat="@drawable/analysis" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</layout>

+ 17 - 0
app/src/main/res/layout/fragment_splash.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:scaleType="centerCrop"
+            app:srcCompat="@drawable/launcher" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</layout>

+ 75 - 0
app/src/main/res/layout/item_chapter.xml

@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <data>
+
+        <variable
+            name="cover"
+            type="String" />
+
+        <variable
+            name="name"
+            type="String" />
+
+    </data>
+
+    <androidx.cardview.widget.CardView
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_marginStart="@dimen/dp4"
+        android:layout_marginTop="@dimen/dp4"
+        android:layout_marginEnd="@dimen/dp4"
+        android:layout_marginBottom="@dimen/dp4"
+        android:focusable="true"
+        android:foreground="?selectableItemBackground"
+        android:gravity="center"
+        app:cardBackgroundColor="#FFF"
+        app:cardCornerRadius="@dimen/dp8"
+        app:cardElevation="0dp"
+        app:cardPreventCornerOverlap="true"
+        app:cardUseCompatPadding="true"
+        app:contentPadding="0dp">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent">
+
+            <androidx.appcompat.widget.AppCompatImageView
+                android:id="@+id/unit_cover"
+                android:layout_width="@dimen/dp180"
+                android:layout_height="match_parent"
+                android:scaleType="fitXY"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                app:load="@{cover}"
+                tools:srcCompat="@tools:sample/backgrounds/scenic" />
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/item_name"
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:background="@color/colorMasking"
+                android:gravity="center"
+                android:maxLines="1"
+                android:text="@{name}"
+                android:textAlignment="center"
+                android:textAllCaps="false"
+                android:textColor="@color/colorWhite"
+                app:autoSizeTextType="uniform"
+                app:layout_constraintHeight_percent="0.2"
+                app:layout_constraintVertical_bias="1"
+                app:layout_constraintBottom_toBottomOf="@id/unit_cover"
+                app:layout_constraintEnd_toEndOf="@id/unit_cover"
+                app:layout_constraintStart_toStartOf="@id/unit_cover"
+                app:layout_constraintTop_toTopOf="@id/unit_cover"
+                tools:text="@tools:sample/full_names" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+    </androidx.cardview.widget.CardView>
+
+</layout>

+ 0 - 0
app/src/main/res/layout/item_section.xml


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů