Browse Source

单词课程流程:自动播放、拼写

master
suliang 2 years ago
parent
commit
9a71c3ef19
29 changed files with 766 additions and 276 deletions
  1. 2
    1
      .idea/misc.xml
  2. BIN
      app/src/main/assets/common_voice_uk/a_uk.mp3
  3. BIN
      app/src/main/assets/common_voice_us/a_us.mp3
  4. 1
    1
      app/src/main/java/com/xkl/cdl/adapter/AdapterHistoricalRoute.kt
  5. 12
    24
      app/src/main/java/com/xkl/cdl/adapter/AdapterLesson.kt
  6. 2
    2
      app/src/main/java/com/xkl/cdl/adapter/AdapterSpell.kt
  7. 4
    2
      app/src/main/java/com/xkl/cdl/data/AppConstants.kt
  8. 6
    0
      app/src/main/java/com/xkl/cdl/data/bean/intentdata/LearnData.kt
  9. 76
    4
      app/src/main/java/com/xkl/cdl/data/manager/db/DBCourseManager.kt
  10. 8
    10
      app/src/main/java/com/xkl/cdl/data/repository/PhotoCache.kt
  11. 1
    1
      app/src/main/java/com/xkl/cdl/dialog/CommonDialog.kt
  12. 10
    3
      app/src/main/java/com/xkl/cdl/dialog/LearnDialog.kt
  13. 37
    3
      app/src/main/java/com/xkl/cdl/module/XKLApplication.kt
  14. 13
    7
      app/src/main/java/com/xkl/cdl/module/learn/LearnBaseViewModel.kt
  15. 6
    6
      app/src/main/java/com/xkl/cdl/module/learn/LearnExamViewModel.kt
  16. 219
    31
      app/src/main/java/com/xkl/cdl/module/learn/LearnWordActivity.kt
  17. 81
    10
      app/src/main/java/com/xkl/cdl/module/learn/LearnWordViewModel.kt
  18. 85
    89
      app/src/main/java/com/xkl/cdl/module/m_center_learn/CoursePackMainActivity.kt
  19. 0
    3
      app/src/main/java/com/xkl/cdl/module/m_center_learn/CoursePackMainActivityViewModel.kt
  20. 45
    9
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseLessonFragment.kt
  21. 0
    1
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseMainFragment.kt
  22. 85
    28
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseMainFragmentViewModel.kt
  23. 3
    3
      app/src/main/java/com/xkl/cdl/util/LearnRuleUtil.kt
  24. 46
    34
      app/src/main/res/layout/activity_learn_word.xml
  25. 1
    0
      app/src/main/res/layout/inc_learn_word.xml
  26. 7
    1
      app/src/main/res/values/strings.xml
  27. 1
    1
      lib/common/src/main/java/com/suliang/common/base/activity/BaseActivityVM.kt
  28. 13
    0
      lib/common/src/main/java/com/suliang/common/util/DrawableUti.kt
  29. 2
    2
      lib/common/src/main/java/com/suliang/common/util/media/MPUtil.kt

+ 2
- 1
.idea/misc.xml View File

@@ -46,7 +46,7 @@
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_exam.xml" value="0.25" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_exam_word.xml" value="0.33" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_spell.xml" value="0.47690217391304346" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_word.xml" value="0.5" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_word.xml" value="0.6435024322446143" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_word2.xml" value="0.4979166666666667" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_main.xml" value="0.5" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_splash.xml" value="0.4921875" />
@@ -102,6 +102,7 @@
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_statistics.xml" value="0.44166666666666665" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_play.xml" value="0.503125" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_play_pause.xml" value="0.503125" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_play_stop.xml" value="0.4973958333333333" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_right.xml" value="0.4425925925925926" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_spell.xml" value="0.5061538461538462" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_spoken.xml" value="0.5061538461538462" />

BIN
app/src/main/assets/common_voice_uk/a_uk.mp3 View File


BIN
app/src/main/assets/common_voice_us/a_us.mp3 View File


+ 1
- 1
app/src/main/java/com/xkl/cdl/adapter/AdapterHistoricalRoute.kt View File

@@ -121,7 +121,7 @@ class AdapterHistoricalRoute(val courseType : Int) : BaseRVAdapter<LearnWord>()
if (currentSelectPosition == position) return@click
//点击改变选中值与显示
previousSelectPosition = currentSelectPosition
previousIsShowLine = !(previousSelectPosition == itemCount-1 && lastIsLearnOver)
previousIsShowLine = previousSelectPosition == itemCount-1 && !lastIsLearnOver
currentSelectPosition = position
currentIsShowLine = true
notifyItemChanged(previousSelectPosition)

+ 12
- 24
app/src/main/java/com/xkl/cdl/adapter/AdapterLesson.kt View File

@@ -42,29 +42,6 @@ class AdapterLesson(vm: CourseMainFragmentViewModel) : BaseRVAdapterVM<Lesson, C
executePendingBindings()
initColor(position, lesson)
when(lesson.lessonType){
AppConstants.LESSON_TYPE_COMPOSITION_VIDEO -> { // 作文视频
}
AppConstants.LESSON_TYPE_COMPOSITION_EXAM -> { //作文章节测试

}
AppConstants.LESSON_TYPE_COMPOSITION_READING -> { //作文课堂练习

}
AppConstants.LESSON_TYPE_COMPOSITION_TASK -> { //作文课外练习

}
AppConstants.LESSON_TYPE_DIALOGUE -> { //口语对话

}

else -> { } // 其他课时类型为布局的正常显示
}





//事件
layoutContent.click {
//选中项非当前项,则需要改变选中颜色,直接通知更新,调用对应位置的notfy
@@ -111,7 +88,18 @@ class AdapterLesson(vm: CourseMainFragmentViewModel) : BaseRVAdapterVM<Lesson, C
}
} else { //未选中:根据学习情况判断
layoutContent.setBackgroundColor(translationColor) //未选中背景透明
if (lesson.learnIsOver) { //学习完成
//根据课时类型判断当前课时是否完全完成
val lessonLearnOver = when(lesson.lessonType){
AppConstants.LESSON_TYPE_WORD -> lesson.learnIsOver && lesson.afterTestScore != AppConstants.NOT_DOING
AppConstants.LESSON_TYPE_COMPOSITION_VIDEO -> lesson.learnIsOver // 作文视频
AppConstants.LESSON_TYPE_COMPOSITION_EXAM -> lesson.learnIsOver //作文章节测试
AppConstants.LESSON_TYPE_COMPOSITION_READING -> lesson.learnIsOver //作文课堂练习
AppConstants.LESSON_TYPE_COMPOSITION_TASK -> lesson.learnIsOver //作文课外练习
AppConstants.LESSON_TYPE_DIALOGUE -> lesson.learnIsOver //口语对话
else -> false // 其他课时类型为布局的正常显示
}
if (lessonLearnOver) { //学习完成 且学后测试完成
tvLessonName.run {
setTextColor(normalColorLearnOver) //课时名称颜色
paint.isFakeBoldText = false //加粗

+ 2
- 2
app/src/main/java/com/xkl/cdl/adapter/AdapterSpell.kt View File

@@ -294,8 +294,8 @@ class AdapterSpell : BaseRVAdapter<SpellItemBean>() {
private fun playItem(letter : Char) {
if (letter.isLetter()) {
when (defaultSoundWay) {
AppConstants.SOUND_TYPE_UK -> MPManager.playAsset("common_voice_uk/${letter}_uk.mp3")
AppConstants.SOUND_TYPE_US -> MPManager.playAsset("common_voice_us/${letter}_us.mp3")
AppConstants.SOUND_TYPE_UK -> MPManager.playAsset("common_voice_uk/${letter.lowercase()}_uk.mp3")
AppConstants.SOUND_TYPE_US -> MPManager.playAsset("common_voice_us/${letter.lowercase()}_us.mp3")
}
} else {
MPManager.playAsset("common_voice/konge.mp3")

+ 4
- 2
app/src/main/java/com/xkl/cdl/data/AppConstants.kt View File

@@ -153,9 +153,11 @@ object AppConstants {
const val DIALOG_TYPE_EXAM_START = 1
const val DIALOG_TYPE_EXAM_OVER = 2
/** 对话框类型: 学习结束弹窗类型 */
const val DIALOG_TYPE_LEARN_OVER = 3
const val DIALOG_TYPE_LEARNING_OVER = 3
/**对话框类型,item学习完成*/
const val DIALOG_TYPE_LESSON_ITEM_OVER = 4
const val DIALOG_TYPE_LESSON_ITEM_CLICK_ALL_OVER = 4
/**课时列表item点击: 课时未做学后测试*/
const val DIALOG_TYPE_LESSON_ITEM_CLICK_NOT_DOING_AFTER_TEST = 5
/**--- 总线动作 --------------------------------- */
/**action key 改变界面 到目录页 */

+ 6
- 0
app/src/main/java/com/xkl/cdl/data/bean/intentdata/LearnData.kt View File

@@ -7,6 +7,7 @@ import com.xkl.cdl.data.bean.course.Lesson
* author suliang
* create 2022/4/2 14:55
* Describe: 课时学习时的传参
* @param lesson 课时学习是为课时数据,其他特殊时候,如自动播放,只是用以包装课程的基本信息如果项目id、课程包类型、课程类型等
*/
class LearnData(val lesson: Lesson) {
@@ -15,4 +16,9 @@ class LearnData(val lesson: Lesson) {
/**学习数据*/
var learnWordList : List<LearnWord> = mutableListOf()
/** 是否自动播放 ,如果是自动播放,则lesson为手动虚拟创建的 */
var isAutoPlay = false
//自动播放次数
var autoPlayTime = 0
}

+ 76
- 4
app/src/main/java/com/xkl/cdl/data/manager/db/DBCourseManager.kt View File

@@ -107,7 +107,7 @@ object DBCourseManager {
val learnIndex = wordIds.indexOf(detail.lesson_learn_point.getOrElse(key, { -1 })) //学习位置,当前位置为已学
// 注:针对口语对话课时,如果没有学习完,是不会设置进度点的。
// 注:针对口语对话课时,如果没有学习完,是不会设置进度点的。 todo 分课程类型设置进度点
val learnIsOver = wordIds.size - 1 == learnIndex
val lesson = Lesson(base.subjectId,
@@ -132,6 +132,15 @@ object DBCourseManager {
}
mutableList.add(lesson)
positionIndex += 1
// TODO: 2022/5/9 对课时数量进行限制,课时太多,开发人员不好进行测试
val needBreak = when(base.coursePackType){
AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION -> positionIndex == 11 //保留十个课时
AppConstants.COURSEPACK_TYPE_ENGLISH_SPOKEN -> positionIndex == 7 //保留六个课时
else -> positionIndex == 3 //保留三个课时
}
if (needBreak){
break
}
}
it.close()
}
@@ -169,10 +178,10 @@ object DBCourseManager {
AppConstants.COURSE_TYPE_ENGLISH_SPELL -> { //拼写 不需要查询测试表,不需要选项
"SELECT id, word_id,word,chapter_id,lesson_id,basic_explaination FROM chapter " + when (testType) {
AppConstants.TEST_TYPE_BEFORE, AppConstants.TEST_TYPE_AFTER -> { //学前、学后测
" AND chapter_id = ${lesson!!.chapterId} AND lesson_id = ${lesson.lessonId} "
" WHERE chapter_id = ${lesson!!.chapterId} AND lesson_id = ${lesson.lessonId} "
}
else -> " ORDER by random() LIMIT $count"
}
else -> ""
} + " ORDER by random() LIMIT $count"
}
AppConstants.COURSE_TYPE_ENGLISH_SPOKEN -> { //口语
when (testType) {
@@ -509,5 +518,68 @@ object DBCourseManager {
return result
}
/**
* 根据wordId查询指定课程指定章节课时的指定单词数据,按照wordId顺序进行排序
* @param dbcb DbControlBase
* @param chapterId 章节id
* @param lessonId 课时id
* @param wordIds String 字符串样式的单词id 1,2,3,4,5 注意不要在最后添加逗号
* @param isNeedOriginSort 是否保持wordIds排序,默认false,通过word_sort排序 true:即wordIds不为word——sort的排序,需要保持顺序
* @return List<LearnWord>
*/
@SuppressLint("Range")
fun queryLearnWord(dbcb : DbControlBase, chapterId:Long, lessonId:Long, wordIds:String, isNeedOriginSort:Boolean = false):List<LearnWord>{
val result = mutableListOf<LearnWord>()
open(dbcb)
val sql = when{
!isNeedOriginSort -> "SELECT * FROM chapter WHERE chapter_id = $chapterId and lesson_id = $lessonId AND word_id in ($wordIds) ORDER by word_sort ASC"
else -> {
val sortSqlBuilder = StringBuilder() //排序value
wordIds.split(",").forEachIndexed { index, value ->
sortSqlBuilder.append(" when word_id = $value then $index ")
}
"SELECT * FROM chapter WHERE chapter_id = $chapterId and lesson_id = $lessonId AND word_id in ($wordIds) ORDER by CASE $sortSqlBuilder END"
}
}
mDataBase?.rawQuery(sql, null)?.run {
while (moveToNext()) {
//学习单词实体
result.add(LearnWord(dbcb.subjectId,
dbcb.coursePackId,
dbcb.courseId,
dbcb.coursePackType,
dbcb.courseType,
chapterId,
lessonId,
getLong(getColumnIndex("word_id")),
true,
AppConstants.LESSON_TYPE_WORD).apply {
word = getString(getColumnIndex("word"))
basic_explanation = getString(getColumnIndex("basic_explaination"))
extend_explanation = getString(getColumnIndex("all_explaination"))
phrase = getString(getColumnIndex("phrase"))
example = getString(getColumnIndex("example"))
reference = getString(getColumnIndex("reference"))
when (dbcb.courseType) {
AppConstants.COURSE_TYPE_CHINESE_LITERACY -> {
literacyIspolyphone = getInt(getColumnIndex("polyphone")) > 0
phonetic_cn = getString(getColumnIndex("phonetic"))
}
AppConstants.COURSE_TYPE_CHINESE_PINYIN -> {
phonetic_cn = getString(getColumnIndex("phonetic"))
}
else -> {
phonetic_uk = getString(getColumnIndex("phonetic_uk"))
phonetic_us = getString(getColumnIndex("phonetic_us"))
}
}
})
}
close()
}
return result
}
}

+ 8
- 10
app/src/main/java/com/xkl/cdl/data/repository/PhotoCache.kt View File

@@ -3,6 +3,7 @@ package com.xkl.cdl.data.repository
import android.util.LruCache
import androidx.lifecycle.MutableLiveData
import com.suliang.common.extension.diskIo2Main
import com.suliang.common.util.thread.AppExecutors
import com.xkl.cdl.data.manager.db.DBCourseManager
import com.xkl.cdl.data.manager.db.DbControlBase
import io.reactivex.rxjava3.core.Observable
@@ -39,18 +40,15 @@ object PhotoCache {
fun get(dbControlBase : DbControlBase, wordId : Long) {
//先查对应的文件路径 : 项目id_课程包id_课程id_单词id_发音方式
val defaultKey = "${dbControlBase.subjectId}_${dbControlBase.coursePackId}_${dbControlBase.courseId}_${wordId}"
Observable.fromCallable {
AppExecutors.diskIO.run {
//不为空,直接取值
if (lruCache.get(defaultKey) == null) {
val value = DBCourseManager.queryPhoto(dbControlBase, wordId)
lruCache.put(defaultKey, value) //保存路径
if (::photoLiveData.isInitialized){
photoLiveData.postValue(lruCache.get(defaultKey)?:let {
DBCourseManager.queryPhoto(dbControlBase, wordId)?.apply {
lruCache.put(defaultKey, this) //保存路径
}
})
}
return@fromCallable lruCache.get(defaultKey)
}.compose(diskIo2Main()).subscribe {
if (this::photoLiveData.isInitialized)
photoLiveData.value = it
}
}
}

+ 1
- 1
app/src/main/java/com/xkl/cdl/dialog/CommonDialog.kt View File

@@ -69,7 +69,7 @@ class CommonDialog private constructor() : BaseDialogFragment<DialogCommonBindin
}
}?:let {
binding.tvLeft.visibility = View.GONE
binding.vSpace.visibility = View.GONE
binding.vSplit.visibility = View.GONE
}
rightColor?.let {
binding.tvRight.setTextColor(ContextCompat.getColor(requireContext(),it))

+ 10
- 3
app/src/main/java/com/xkl/cdl/dialog/LearnDialog.kt View File

@@ -67,9 +67,11 @@ class LearnDialog private constructor() : BaseDialogFragment<DialogLessonLearnBi
AppConstants.TEST_TYPE_AFTER_TOTAL -> initCourseAfterTestOver()
}
//学习结束弹窗
AppConstants.DIALOG_TYPE_LEARN_OVER -> initLessonLearningOver()
AppConstants.DIALOG_TYPE_LEARNING_OVER -> initLessonLearningOver(false)
/**课时列表点击课时完全完成*/
AppConstants.DIALOG_TYPE_LESSON_ITEM_OVER -> initLessonItemClickLessonOver()
AppConstants.DIALOG_TYPE_LESSON_ITEM_CLICK_ALL_OVER -> initLessonItemClickLessonOver()
/** 课时列表item点击 */
AppConstants.DIALOG_TYPE_LESSON_ITEM_CLICK_NOT_DOING_AFTER_TEST -> initLessonLearningOver(true)
}
}
@@ -181,8 +183,9 @@ class LearnDialog private constructor() : BaseDialogFragment<DialogLessonLearnBi
/**
* 课时学习中的学习完成
* @param isShowCloseImg 是否需要显示关闭按钮
*/
private fun initLessonLearningOver(){
private fun initLessonLearningOver(isShowCloseImg:Boolean){
initNumber()
binding.run {
@@ -200,6 +203,10 @@ class LearnDialog private constructor() : BaseDialogFragment<DialogLessonLearnBi
}
binding.tvLeft.click { onDialogListener(AppConstants.DIALOG_LESSON_RELEARN, this) }
binding.tvRight.click { onDialogListener(AppConstants.DIALOG_START_TEST, this) }
if (isShowCloseImg){
binding.ivClose.visibility = View.VISIBLE
binding.ivClose.click { dismissAllowingStateLoss() }
}
}

+ 37
- 3
app/src/main/java/com/xkl/cdl/module/XKLApplication.kt View File

@@ -3,7 +3,14 @@ package com.xkl.cdl.module
import android.app.Application
import com.suliang.common.util.LogUtil
import com.tencent.mmkv.MMKV
import io.reactivex.rxjava3.exceptions.UndeliverableException
import io.reactivex.rxjava3.functions.Consumer
import io.reactivex.rxjava3.plugins.RxJavaPlugins
import net.sqlcipher.database.SQLiteDatabase
import java.io.IOException
import java.lang.IllegalArgumentException
import java.lang.IllegalStateException
import java.lang.NullPointerException
import java.util.*

/**
@@ -18,11 +25,38 @@ class XKLApplication : Application() {
SQLiteDatabase.loadLibs(this)
// ImageLoader.mStrategy = GlideLoaderStrategy()
LogUtil.e(UUID.randomUUID().toString().replace("-",""))
//初始MMKV存储
val rootDir = MMKV.initialize(this)
LogUtil.e(rootDir)


setRxJavaErrorHandler()
}
/***
* 避免 调用多次onError。正常来说第一次onError会走正常的Observer处理,其他会走ErrorHandler。通过此方法捕捉多次的error
*/
private fun setRxJavaErrorHandler(){
RxJavaPlugins.setErrorHandler(Consumer { e ->
e.printStackTrace()
LogUtil.e( "RxJavaErrorHandler --> \n $e")
if (e is UndeliverableException) {
return@Consumer
} else if (e is IOException) {
// fine, irrelevant network problem or API that throws on cancellation
return@Consumer
} else if (e is InterruptedException) {
// fine, some blocking code was interrupted by a dispose call
return@Consumer
} else if (e is NullPointerException || e is IllegalArgumentException) {
// that's likely a bug in the application
Thread.currentThread().uncaughtExceptionHandler.uncaughtException(Thread.currentThread(), e)
return@Consumer
} else if (e is IllegalStateException) {
// that's a bug in RxJava or in a custom operator
Thread.currentThread().uncaughtExceptionHandler.uncaughtException(Thread.currentThread(), e)
return@Consumer
}
LogUtil.e( "RxJavaErrorHandler --> unknown exception = \n $e")
})
}

}

+ 13
- 7
app/src/main/java/com/xkl/cdl/module/learn/LearnBaseViewModel.kt View File

@@ -6,6 +6,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import com.suliang.common.base.viewmodel.BaseViewModel
import com.suliang.common.util.LogUtil
import com.xkl.cdl.data.bean.intentdata.LearnData
import java.util.*
import kotlin.concurrent.timerTask

@@ -37,7 +38,7 @@ abstract class LearnBaseViewModel : BaseViewModel() {
val validTime : MutableLiveData<Long> = MutableLiveData(0)
/**记录当前倒计时已运行时间 默认18秒 */
private var currentValidSurplusTime = validMaxTime
var currentValidSurplusTime = validMaxTime
/** 开始计时 */
@@ -45,19 +46,24 @@ abstract class LearnBaseViewModel : BaseViewModel() {
timeTask = timerTask {
totalUseTime.postValue(totalUseTime.value?.plus(200))
//如果需要记录有效时间
if (isRunValidTime) {
validTime.postValue(validTime.value!!.plus(200))
currentValidSurplusTime -= 200
isRunValidTime = currentValidSurplusTime > 0
}
initRunValidTime()
}
totalTimer.schedule(timeTask, 200, 200)
LogUtil.e("开始总计时")
}
/** 有效时间记录规则 */
open fun initRunValidTime(){
if (isRunValidTime) {
validTime.postValue(validTime.value!!.plus(200))
currentValidSurplusTime -= 200
isRunValidTime = currentValidSurplusTime > 0
}
}
/** 停止计时 */
fun stopTotalCountTing() {
LogUtil.e("停止总计时")
// LogUtil.e("停止总计时")
timeTask?.cancel()
timeTask = null
}

+ 6
- 6
app/src/main/java/com/xkl/cdl/module/learn/LearnExamViewModel.kt View File

@@ -207,14 +207,14 @@ class LearnExamViewModel : LearnBaseViewModel() {
/** 停止获取下一题 */
fun pauseToNext() {
LogUtil.e("停止获取下一题")
// LogUtil.e("停止获取下一题")
isErrorPauseToNext = true
mHandler.removeCallbacks(toNextRunable)
}
/** 继续获取下一题 */
fun continueToNext() {
LogUtil.e("获取下一题")
// LogUtil.e("获取下一题")
isErrorPauseToNext = false
isGetNextIng = true
mHandler.post(toNextRunable)
@@ -264,13 +264,13 @@ class LearnExamViewModel : LearnBaseViewModel() {
currentMaxTime = if (intentData.courseType == AppConstants.COURSE_TYPE_ENGLISH_VOICE) 0 else Math.max(
AppConstants.TEST_MIN_TIME,
(nextExam.word.length + nextExam.correct.length) * 200)
LogUtil.e("------currentMaxTime = $currentMaxTime")
// LogUtil.e("------currentMaxTime = $currentMaxTime")
currentSuplusTime.value = currentMaxTime
}
}
currentExamBean.value = nextExam
//开始倒计时
LogUtil.e("开始单题倒计时")
// LogUtil.e("开始单题倒计时")
mHandler.postDelayed(currentCountingTimeRunnable, currentCountingIntervalTime)
}
}
@@ -340,7 +340,7 @@ class LearnExamViewModel : LearnBaseViewModel() {
fun chooseResult(position : Int) {
//移除计时
mHandler.removeCallbacks(currentCountingTimeRunnable)
LogUtil.e("结果移除单题倒计时")
// LogUtil.e("结果移除单题倒计时")
//创建单题的测试记录
currentExamRecord = ExamRecord.newBuilder().apply {
questionId = currentExamBean.value!!.word_id
@@ -395,7 +395,7 @@ class LearnExamViewModel : LearnBaseViewModel() {
fun spellOver(selectedValue : String, errorSize : Int) {
//移除计时
mHandler.removeCallbacks(currentCountingTimeRunnable)
LogUtil.e("结果移除单题倒计时")
// LogUtil.e("结果移除单题倒计时")
//创建测试记录
currentExamRecord = ExamRecord.newBuilder().apply {
questionId = currentExamBean.value!!.word_id

+ 219
- 31
app/src/main/java/com/xkl/cdl/module/learn/LearnWordActivity.kt View File

@@ -1,6 +1,7 @@
package com.xkl.cdl.module.learn

import android.annotation.SuppressLint
import android.graphics.Color
import android.os.Bundle
import android.text.SpannableStringBuilder
import android.view.MotionEvent
@@ -17,6 +18,7 @@ import com.jeremyliao.liveeventbus.LiveEventBus
import com.suliang.common.base.activity.BaseActivityVM
import com.suliang.common.extension.click
import com.suliang.common.util.DateUtil
import com.suliang.common.util.DrawableUti
import com.suliang.common.util.LogUtil
import com.suliang.common.util.image.ImageLoader
import com.suliang.common.util.media.EMediaState
@@ -91,6 +93,14 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView
/** 横幅 自动播放 与 复习时使用 */
private fun initBanner() {
// TODO: 2022/4/25 初始化横幅,默认隐藏
if (vm.learnData.isAutoPlay) {
binding.tvBanner.apply {
visibility = View.VISIBLE
setText(R.string.auto_playing)
setBackgroundColor(Color.parseColor("#1A5082E6"))
setTextColor(ContextCompat.getColor(this@LearnWordActivity, R.color.theme_color))
}
}
}
/** 标题初始 */
@@ -122,9 +132,9 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView
private fun initHistoricalRoute() {
adapterHistorical = AdapterHistoricalRoute(vm.learnData.lesson.courseType).apply {
onItemClick = { v : View, position : Int, item : LearnWord ->
//历史轨迹的点击,改变当前选项
vm.currentIsHistoricalItemClick = true
vm.currentLearnWord.value = item
if (!vm.learnData.isAutoPlay) { //自动播放历史轨迹点击无效
vm.clickHistoricalItem(item)
}
}
}
binding.rvHistoricalRoute.apply {
@@ -177,8 +187,11 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView
}
AppConstants.COURSE_TYPE_ENGLISH_VOICE -> {
bindingWord = IncLearnWordBinding.inflate(layoutInflater, binding.containerLayout, true)
bindingWord.ivVoice.click { readWord() }
bindingWord.incWord.tvWord.click { readWord() }
//自动播放点击无效
if (!vm.learnData.isAutoPlay) {
bindingWord.ivVoice.click { readWord() }
bindingWord.incWord.tvWord.click { readWord() }
}
}
else -> {
bindingWord = IncLearnWordBinding.inflate(layoutInflater, binding.containerLayout, true)
@@ -186,10 +199,28 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView
bindingWord.incWord.tvWord.click { readWord() }
}
}
//自动播放初始可见
if (vm.learnData.isAutoPlay) {
View.VISIBLE.let {
bindingWord.run {
imgWord.visibility = it
incWord.tvWord.visibility = it
tvPhonetic.visibility = it
tvExplain.visibility = it
tvExpandExplain.visibility = it
}
incWorcDetailBinding.root.visibility = it
}
}
}
//初始化按钮
private fun initControlButton() {
//自动播放隐藏操作按钮
if (vm.learnData.isAutoPlay){
binding.incControlButton.root.visibility= View.GONE
return
}
//其他课程默认按钮全部隐藏
binding.incControlButton.run {
tvLeft.visibility = View.INVISIBLE
@@ -222,19 +253,74 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView
//最底部
private fun initBottom(){
binding.tvPlay.visibility = View.GONE
binding.tvPlayStop.visibility = View.GONE
//有效时间
vm.validTime.observe(this){
binding.tvValidTime.text = "本次学习 ${DateUtil.formatGMT(it,DateUtil.FORMAT_2)}"
}
if (vm.learnData.isAutoPlay){
binding.tvPlay.run {
visibility = View.VISIBLE
tag = 1
setText(R.string.auto_play_paused)
}
binding.tvPlayStop.run {
visibility = View.VISIBLE
setText(R.string.auto_play_stop)
}
binding.tvPlay.click {
when(binding.tvPlay.tag as Int){
1 -> { //暂停播放
vm.isAutoPlaying = false
binding.tvPlay.apply {
setText(R.string.auto_play_continue)
setIconResource(R.drawable.ic_play)
tag = 2
}
//点击暂停后
binding.tvBanner.apply {
setText(R.string.auto_play_banner_paused)
setBackgroundColor(Color.parseColor("#1AF26255"))
setTextColor(ContextCompat.getColor(this@LearnWordActivity,R.color.red_1))
}
}
2 -> { //继续播放
vm.isAutoPlaying = true
binding.tvPlay.apply {
setText(R.string.auto_play_paused)
setIconResource(R.drawable.ic_play_pause)
tag = 1
LiveEventBus.get<Int>("auto_play").post(0)
}
//点击继续后
binding.tvBanner.apply {
setText(R.string.auto_playing)
setBackgroundColor(Color.parseColor("#1A5082E6"))
setTextColor(ContextCompat.getColor(this@LearnWordActivity, R.color.theme_color))
}
}
}
}
//停止播放
binding.tvPlayStop.click {onBackPressed()}
}
}
/** 发音监听 */
private val impListener = object : IMPListener {
override fun onMpState(state : EMediaState) {
when (state) {
EMediaState.RUNNING -> bindingWord.ivVoice.playAnimation()
EMediaState.COMPLETE, EMediaState.ERROR -> bindingWord.ivVoice.cancelAnimation()
when{
vm.learnData.isAutoPlay -> when (state) {
EMediaState.COMPLETE, EMediaState.ERROR -> {
//播放次数+1
vm.currentPlayTime ++
//继续播放,或获取下一条
LiveEventBus.get<Int>("auto_play").postDelay(0,700)
}
}
else -> when (state) {
EMediaState.RUNNING -> bindingWord.ivVoice.playAnimation()
EMediaState.COMPLETE, EMediaState.ERROR -> bindingWord.ivVoice.cancelAnimation()
}
}
}
}
@@ -242,9 +328,30 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView
override fun loadData() {
vm.loadNext()
//如果自动播放,监听信息
if (vm.learnData.isAutoPlay){
LiveEventBus.get<Int>("auto_play").observe(this){
if (!vm.isAutoPlaying) return@observe
when(vm.currentPlayTime){
vm.learnData.autoPlayTime -> vm.loadNext()
else -> readWord()
}
}
}
//发音数据
AudioCache.initAudioLiveData().observe(this) {
//自动播放的单词音频数据
if (vm.learnData.isAutoPlay){
it?.run {
MPManager.play(it,listener = impListener)
}?:let {
showToast("未找到发音文件")
//获取下一条
vm.loadNext()
}
return@observe
}
it?.run {
if (vm.learnData.lesson.courseType == AppConstants.COURSE_TYPE_ENGLISH_VOICE && bindingWord.ivVoice.visibility == View.VISIBLE) MPManager.play(
it, listener = impListener)
@@ -255,7 +362,10 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView
if (vm.isNeedLoadPhoto) {
//图片数据
PhotoCache.initPhotoLiveData().observe(this) {
it?.run { ImageLoader.loadImage(bindingWord.imgWord, it) } ?: let { bindingWord.imgWord.visibility = View.GONE }
it?.run {
if(vm.learnData.isAutoPlay)bindingWord.imgWord.visibility = View.VISIBLE
ImageLoader.loadImage(bindingWord.imgWord, it)
} ?: let { bindingWord.imgWord.visibility = View.GONE }
}
}
@@ -266,6 +376,10 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView
adapterHistorical.addData(it)
binding.rvHistoricalRoute.scrollToPosition(adapterHistorical.itemCount - 1)
}
if (vm.learnData.isAutoPlay){
initAutoPlayWord(it)
return@observe
}
//界面初始
when (vm.learnData.lesson.courseType) {
AppConstants.COURSE_TYPE_ENGLISH_VOICE -> initVoice(it)
@@ -285,6 +399,42 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView
}
}
/**新数据到来,自动播放界面显示*/
@SuppressLint("SetTextI18n")
private fun initAutoPlayWord(learnWord : LearnWord) {
//发音
readWord()
//图片
if (vm.isNeedLoadPhoto) {
bindingWord.imgWord.visibility = View.INVISIBLE
PhotoCache.get(vm.dbControlBase, learnWord.wordId)
}
//单词内容
bindingWord.incWord.tvWord.apply {
//识字需单独处理
text = if (vm.learnData.lesson.courseType == AppConstants.COURSE_TYPE_CHINESE_LITERACY) ViewUtil.literacyToHtmlWord(
learnWord.word, learnWord.showColor) else learnWord.word
setTextColor(ContextCompat.getColor(this@LearnWordActivity, learnWord.showColor)) //单词显示颜色
}
//音标
if (vm.learnData.lesson.subjectId == AppConstants.SUBJECT_ENGLISH) {
val generatePhonetic = ViewUtil.generatePhonetic(bindingWord.tvPhonetic, learnWord.phonetic_uk, learnWord.phonetic_us)
bindingWord.tvPhonetic.text = generatePhonetic
} else {
bindingWord.tvPhonetic.text = learnWord.phonetic_cn
}
//基本释义
bindingWord.tvExplain.text = learnWord.basic_explanation
//扩展释义
initFirstVisible(bindingWord.tvExpandExplain, learnWord.extend_explanation)
if (bindingWord.tvExpandExplain.text.isNotEmpty()) {
bindingWord.tvExpandExplain.text = "扩展释义:${bindingWord.tvExpandExplain.text}"
bindingWord.tvExpandExplain.visibility = View.VISIBLE
}
//详情
incWorcDetailBinding.initValue(learnWord.phrase, learnWord.example, learnWord.reference)
}
/**新数据到来,进行初始化*/
@SuppressLint("SetTextI18n")
@@ -423,6 +573,7 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView
private fun clickNext() {
//拼写,需要处理历史轨迹 和在下一条的时候保存当前学习数据
if (vm.learnData.lesson.courseType == AppConstants.COURSE_TYPE_ENGLISH_SPELL){
adapterHistorical.currentLearnOver()
when{
//正确
vm.currentSpellIsCorrect -> {
@@ -440,8 +591,8 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView
}
}
//当前数据为历史轨迹点击 且 最后一条学习未完成
if (vm.currentIsHistoricalItemClick && !adapterHistorical.lastIsLearnOver) {
//当前数据为历史轨迹点击 且 最后一条学习未完成 且不为最后一条
if (!adapterHistorical.currentIsLastSelect && vm.currentIsHistoricalItemClick && !adapterHistorical.lastIsLearnOver) {
skipToHistoricalLastItem()
} else {
vm.loadNext()
@@ -586,7 +737,13 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView
override fun dispatchTouchEvent(ev : MotionEvent?) : Boolean {
//执行有效计时
vm.executeLearnValidTime()
when{
//自动播放只有在非播放时触摸有效
vm.learnData.isAutoPlay -> when{
!vm.isAutoPlaying -> vm.executeLearnValidTime()
}
else -> vm.executeLearnValidTime()
}
return super.dispatchTouchEvent(ev)
}
@@ -595,30 +752,61 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView
when {
vm.isAllOver -> finish()
else -> when {
vm.isHasLearned -> {
CommonDialog.newInstance(
CommonDialogBean(titleText = R.string.quit_learn_title, contentText = R.string.quit_learn_content,
leftText = R.string.quit, rightText = R.string.cancel)).apply {
onCommonDialogButtonClickListener = { dialog, isRightClick ->
dialog.dismissAllowingStateLoss()
when {
// TODO: 2022/4/26 取消,恢复计时
isRightClick -> ""
// TODO: 2022/4/26 保存数据
else -> vm.saveData()
}
}
}.show(supportFragmentManager, "learn_back_dialog")
}
vm.learnData.isAutoPlay -> autoPlayBackDialog()
vm.isHasLearned -> learnBackDialog()
else -> finish()
}
}
}
/** 学习时的返回弹窗 */
private fun learnBackDialog() {
vm.showOrDismissBackDialogForTime(true)
CommonDialog.newInstance(
CommonDialogBean(titleText = R.string.quit_learn_title, contentText = R.string.quit_learn_content, leftText = R.string.quit, rightText = R.string.cancel)).apply {
onCommonDialogButtonClickListener = { dialog, isRightClick ->
dialog.dismissAllowingStateLoss()
when {
isRightClick -> vm.showOrDismissBackDialogForTime(false)
else -> vm.saveData()
}
}
}.show(supportFragmentManager, "learn_back_dialog")
}
/** 自动播放时的返回弹窗 */
private fun autoPlayBackDialog() {
vm.showOrDismissBackDialogForTime(true)
CommonDialog.newInstance(
CommonDialogBean(titleText = R.string.quit_auto_play_title, leftText = R.string.cancel, rightText = R.string.quit)).apply {
onCommonDialogButtonClickListener = { dialog, isRightClick ->
dialog.dismissAllowingStateLoss()
when {
isRightClick -> vm.saveData()
else -> {
vm.showOrDismissBackDialogForTime(false)
LiveEventBus.get<Int>("auto_play").post(0)
}
}
}
}.show(supportFragmentManager, "auto_play_back_dialog")
}
/** 学习完成 */
private fun showLearnOverDialog() {
//自动播放完成弹窗
if (vm.learnData.isAutoPlay){
val drawable = DrawableUti.changeSvgSizeAndColor(resources, R.drawable.ic_right, R.color.theme_color,3)
CommonDialog.newInstance(CommonDialogBean(titleText = R.string.quit_auto_play_title_over,
rightText = R.string.sure),drawable).apply {
onCommonDialogButtonClickListener = { dialog, isRightClick ->
dialog.dismissAllowingStateLoss()
finish()
}
}.show(supportFragmentManager, "auto_play_back_dialog")
return
}
vm.loadAfterTest().observe(this) { showTimeCount ->
LearnDialog.newInstance(LearnDialogBean(AppConstants.DIALOG_TYPE_LEARN_OVER).apply {
LearnDialog.newInstance(LearnDialogBean(AppConstants.DIALOG_TYPE_LEARNING_OVER).apply {
correctNumber = vm.learnData.lesson.correctNumber
errorNumber = vm.learnData.lesson.errorNumber
this.showTimeCount = showTimeCount

+ 81
- 10
app/src/main/java/com/xkl/cdl/module/learn/LearnWordViewModel.kt View File

@@ -7,6 +7,7 @@ import com.jeremyliao.liveeventbus.LiveEventBus
import com.suliang.common.extension.createRandomNewChar
import com.suliang.common.extension.diskIo2Main
import com.suliang.common.util.DateUtil
import com.suliang.common.util.LogUtil
import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.DataTransferHolder
import com.xkl.cdl.data.bean.LearnWord
@@ -32,6 +33,7 @@ class LearnWordViewModel : LearnBaseViewModel() {
//记录是否是历史轨迹的item点击
var currentIsHistoricalItemClick : Boolean = false
private set
//默认发音
var defaultSoundWay : Int = 0
@@ -72,12 +74,42 @@ class LearnWordViewModel : LearnBaseViewModel() {
//数据上传完成监听: true 学习完成的上传 false可能是半途的返回事件,直接退出
val saveDataLiveData = MutableLiveData<Boolean>()
//是否显示返回弹窗
private var isShowBackDialog = false
//自动播放模式:是否在自动播放中,默认是
var isAutoPlaying = true
//当前单词的自动播放次数
var currentPlayTime = 0
/**
* 定义有效时间
* 学习和自动播放有一点不一样哟
*/
override fun initRunValidTime() {
//自动播放单独定义有效时间规则,播放时时间和总时间一直,停止时播放时,通过触摸判断
if (learnData.isAutoPlay){
if (isRunValidTime){
validTime.postValue(validTime.value!!.plus(200))
if (!isAutoPlaying){ //停止播放了
currentValidSurplusTime -= 200
isRunValidTime = currentValidSurplusTime > 0
}
}
}else {
super.initRunValidTime()
}
}
/** 获取数据 */
fun loadNext() {
//修改标记
currentIsHistoricalItemClick = false
//新数据自动播放次数归0
if (learnData.isAutoPlay){
currentPlayTime = 0
}
val word = learnRuleUtil.loadNext
word?.let {
@@ -140,8 +172,29 @@ class LearnWordViewModel : LearnBaseViewModel() {
/** 设置已学位置 */
private fun setLearnPoint(currentIsLastSelect : Boolean, isCycleFirst : Boolean) {
if (currentIsLastSelect && isCycleFirst) {
currentLessonLearnedPosition++
if (!learnData.isAutoPlay) { //自动播放不记录学习点位置
if (currentIsLastSelect && isCycleFirst) {
currentLessonLearnedPosition++
}
}
}
/**
* 显示或关闭的返回弹窗: 不影响总计时,只影响当前的下一题计时或者倒计时
* 显示返回弹窗的时机,只会在测试当种,则是结束后则不会再显示此弹窗
* @param isShow Boolean
*/
fun showOrDismissBackDialogForTime(isShow : Boolean) {
isShowBackDialog = isShow
when {
isShowBackDialog -> { //显示返回弹窗 总计时不停,停止有效计时
isAutoPlaying = false
isRunValidTime = false
}
else -> { //关闭返回弹窗 恢复有效计时
isRunValidTime = true
if (learnData.isAutoPlay) isAutoPlaying = true
}
}
}
@@ -242,15 +295,17 @@ class LearnWordViewModel : LearnBaseViewModel() {
// TODO: 2022/4/14 传递保存record信息
// record 已经实例化并已经将数据保存
if (!saveInit) {
learnData.lesson.apply {
learnedIndex = currentLessonLearnedPosition
correctNumber += learnRuleUtil.currentCorrectMap.size
errorNumber += learnRuleUtil.currentErrorMap.size
learnIsOver = learnedIndex == wordIds.size - 1
//自动播放不修改课时信息
if (!learnData.isAutoPlay) {
learnData.lesson.apply {
learnedIndex = currentLessonLearnedPosition
correctNumber += learnRuleUtil.currentCorrectMap.size
errorNumber += learnRuleUtil.currentErrorMap.size
learnIsOver = learnedIndex == wordIds.size - 1
}
//添加到错误本集合中,主要用于小游戏练习(学前总 课时都没有添加,从这里添加到集合后发送出去,添加到集合)
learnData.examErrorMap?.putAll(learnRuleUtil.currentErrorMap)
}
//添加到错误本集合中,主要用于小游戏练习(学前总 课时都没有添加,从这里添加到集合后发送出去,添加到集合)
learnData.examErrorMap?.putAll(learnRuleUtil.currentErrorMap)
record.addDuration(saveCurrentLearnDuration())
saveInit = true
}
@@ -282,6 +337,8 @@ class LearnWordViewModel : LearnBaseViewModel() {
/** 发送数据事件 */
private fun sendEventBus() {
//自动播放不发送信息通知
if (learnData.isAutoPlay) return
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_DATA).post(LearnEventData(learnData.lesson.subjectId,
learnData.lesson.courseId,
AppConstants.DATA_LESSON_LEARN_OVER).apply {
@@ -301,5 +358,19 @@ class LearnWordViewModel : LearnBaseViewModel() {
stopTotalCountTing()
}
/**
* 历史轨迹item的点击
* @param item LearnWord
*/
fun clickHistoricalItem(item : LearnWord) {
//历史轨迹的点击,改变当前选项
currentIsHistoricalItemClick = true
//拼写时,需要对当前内容选项进行拼接
if (learnData.lesson.courseType == AppConstants.COURSE_TYPE_ENGLISH_SPELL) {
initSpellOption(item.word)
}
currentLearnWord.value = item
}
}

+ 85
- 89
app/src/main/java/com/xkl/cdl/module/m_center_learn/CoursePackMainActivity.kt View File

@@ -22,14 +22,20 @@ import com.xkl.cdl.R
import com.xkl.cdl.adapter.AdapterAutoPlaySelectRepeat
import com.xkl.cdl.adapter.ViewPagerAdapter
import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.DataTransferHolder
import com.xkl.cdl.data.bean.LearnDialogBean
import com.xkl.cdl.data.bean.LearnWord
import com.xkl.cdl.data.bean.course.Lesson
import com.xkl.cdl.data.bean.intentdata.LearnData
import com.xkl.cdl.data.binding.BindingAdapter
import com.xkl.cdl.data.manager.CourseManager
import com.xkl.cdl.data.repository.DataRepository
import com.xkl.cdl.databinding.ActivityCourseMainBinding
import com.xkl.cdl.databinding.DialogBottomAutoPlaySelectBinding
import com.xkl.cdl.databinding.DialogBottomCourseMoreBinding
import com.xkl.cdl.dialog.CommonDialog
import com.xkl.cdl.dialog.CommonDialogBean
import com.xkl.cdl.module.learn.LearnWordActivity
import com.xkl.cdl.module.m_center_learn.coursechildren.CourseMainFragment
import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly

@@ -37,34 +43,33 @@ import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly
* 课程中心
*/
class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CoursePackMainActivityViewModel>() {
companion object {
@JvmStatic
fun newInstance(context: Context, subjectId: Int, coursePackInPosition: Int) {
fun newInstance(context : Context, subjectId : Int, coursePackInPosition : Int) {
context.startActivity(Intent(context, CoursePackMainActivity::class.java).apply {
putExtra(AppConfig.INTENT_1, subjectId)
putExtra(AppConfig.INTENT_2, coursePackInPosition)
})
}
}
//子课程对应的Fragment
private var childFragments = mutableListOf<Fragment>()
//更多按钮的弹窗显示
private var moreDialog: BottomSheetDialog? = null
private var moreDialog : BottomSheetDialog? = null
//自动播放次数的弹窗选择
private var autoPlaySeletDialog: BottomSheetDialog? = null
override fun initViewModel(): CoursePackMainActivityViewModel {
private var autoPlaySeletDialog : BottomSheetDialog? = null
override fun initViewModel() : CoursePackMainActivityViewModel {
val subjectId = intent.getIntExtra(AppConfig.INTENT_1, 0)
val coursePackInListPosition = intent.getIntExtra(AppConfig.INTENT_2, 0)
return ViewModelProvider(
this, ViewModelFactory(subjectId, coursePackInListPosition)
)[CoursePackMainActivityViewModel::class.java]
return ViewModelProvider(this, ViewModelFactory(subjectId,
coursePackInListPosition))[CoursePackMainActivityViewModel::class.java]
}
override fun initStatusBar() {
statusBarOnly {
fitWindow = false //布局是否侵入状态栏
@@ -72,9 +77,9 @@ class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CourseP
light = true
}
}
override fun initActivity(savedInstanceState: Bundle?) {
LogUtil.e("CoursePackMainActivity coursePackMainActivityVM hashCode -> ${vm.hashCode()}")
override fun initActivity(savedInstanceState : Bundle?) {
// LogUtil.e("CoursePackMainActivity coursePackMainActivityVM hashCode -> ${vm.hashCode()}")
//设置布局数据
binding.coursePack = vm.coursePack
//状态栏高度设置
@@ -86,13 +91,13 @@ class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CourseP
}
//返回事件
binding.includeTitleBar.titleBar.run {
onBackClick = {_ -> finish() }
onBackClick = { _ -> finish() }
}
//初始化tab和viewPager
initTabAndViewPager()
}
private fun initTabAndViewPager() {
//英语课程包且为英语单词课程包才显示tab
if (vm.coursePack.subjectId == AppConstants.SUBJECT_ENGLISH && vm.coursePack.coursePackType == AppConstants.COURSEPACK_TYPE_ENGLISH_WORD) {
@@ -124,52 +129,28 @@ class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CourseP
adapter = ViewPagerAdapter(this@CoursePackMainActivity, childFragments)
//注册页面改变监听
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
override fun onPageSelected(position : Int) {
super.onPageSelected(position)
//改变tab的选中颜色
if (binding.includeCourseTypeTab.root.visibility == View.VISIBLE) { //可见,才做操作
binding.includeCourseTypeTab.apply {
tabDiscern.run {
setTextColor(
ContextCompat.getColor(
this@CoursePackMainActivity,
if (position == 0) R.color.theme_color else R.color.gray_2
)
)
BindingAdapter.loadVectorDrawable(
this, ContextCompat.getColor(
this@CoursePackMainActivity,
if (position == 0) R.color.theme_color else R.color.gray_2
)
)
setTextColor(ContextCompat.getColor(this@CoursePackMainActivity,
if (position == 0) R.color.theme_color else R.color.gray_2))
BindingAdapter.loadVectorDrawable(this, ContextCompat.getColor(this@CoursePackMainActivity,
if (position == 0) R.color.theme_color else R.color.gray_2))
}
tabSpell.run {
setTextColor(
ContextCompat.getColor(
this@CoursePackMainActivity,
if (position == 1) R.color.theme_color else R.color.gray_2
)
)
BindingAdapter.loadVectorDrawable(
this, ContextCompat.getColor(
this@CoursePackMainActivity,
if (position == 1) R.color.theme_color else R.color.gray_2
)
)
setTextColor(ContextCompat.getColor(this@CoursePackMainActivity,
if (position == 1) R.color.theme_color else R.color.gray_2))
BindingAdapter.loadVectorDrawable(this, ContextCompat.getColor(this@CoursePackMainActivity,
if (position == 1) R.color.theme_color else R.color.gray_2))
}
tabVoice.run {
setTextColor(
ContextCompat.getColor(
this@CoursePackMainActivity,
if (position == 2) R.color.theme_color else R.color.gray_2
)
)
BindingAdapter.loadVectorDrawable(
this, ContextCompat.getColor(
this@CoursePackMainActivity,
if (position == 2) R.color.theme_color else R.color.gray_2
)
)
setTextColor(ContextCompat.getColor(this@CoursePackMainActivity,
if (position == 2) R.color.theme_color else R.color.gray_2))
BindingAdapter.loadVectorDrawable(this, ContextCompat.getColor(this@CoursePackMainActivity,
if (position == 2) R.color.theme_color else R.color.gray_2))
}
}
}
@@ -204,29 +185,26 @@ class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CourseP
}
//更多按钮点击
binding.includeCourseProgress.ivMore.click {
if (vm.showMoreEnable)
showMoreDialog()
else
showToast("请先进行学习")
if ((childFragments[binding.viewPager2.currentItem] as CourseMainFragment).vm.showMoreIsEnable()) showMoreDialog()
else showToast("请先进行学习")
}
}
override fun loadData() {
}
/** 点击显示更多的弹窗 */
private fun showMoreDialog() {
if (moreDialog == null) {
moreDialog = BottomSheetDialog(this, R.style.dialog_style).apply {
val moreBinding = DataBindingUtil.inflate<DialogBottomCourseMoreBinding>(
layoutInflater, R.layout.dialog_bottom_course_more, null, false
)
val moreBinding = DataBindingUtil.inflate<DialogBottomCourseMoreBinding>(layoutInflater,
R.layout.dialog_bottom_course_more, null,
false)
setContentView(moreBinding.root)
//体验账号、拼些、口语课程、作文课程不显示自动播放
when (vm.coursePack.childrenCourses[binding.viewPager2.currentItem].courseType) {
AppConstants.COURSE_TYPE_ENGLISH_SPELL, AppConstants.COURSE_TYPE_ENGLISH_SPOKEN, AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> moreBinding.tvAutoPlay.visibility =
View.GONE
AppConstants.COURSE_TYPE_ENGLISH_SPELL, AppConstants.COURSE_TYPE_ENGLISH_SPOKEN, AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> moreBinding.tvAutoPlay.visibility = View.GONE
else -> moreBinding.tvAutoPlay.visibility = View.VISIBLE
}
moreBinding.ivCancel.click {
@@ -244,34 +222,35 @@ class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CourseP
}
moreDialog?.show()
}
/** 课程重学弹窗提示 */
fun showCourseRelearnDialog(){
fun showCourseRelearnDialog() {
//弹窗显示清除学习记录
val bean = CommonDialogBean(titleText = R.string.course_relearn_title,
contentText = R.string.course_relearn_content,
val bean = CommonDialogBean(titleText = R.string.course_relearn_title, contentText = R.string.course_relearn_content,
leftText = R.string.course_relearn_sure, rightText = R.string.cancel)
CommonDialog.newInstance(bean).apply {
onCommonDialogButtonClickListener = { dialog, isRightClick ->
dialog.dismissAllowingStateLoss()
if (!isRightClick){
((this@CoursePackMainActivity.binding.viewPager2.adapter as ViewPagerAdapter).getItem(this@CoursePackMainActivity.binding.viewPager2.currentItem) as CourseMainFragment).run {
if (!isRightClick) {
((this@CoursePackMainActivity.binding.viewPager2.adapter as ViewPagerAdapter).getItem(
this@CoursePackMainActivity.binding.viewPager2.currentItem) as CourseMainFragment).run {
this.courseRelearn()
}
}
}
}.show(supportFragmentManager,"course_relearn_dialog")
}.show(supportFragmentManager, "course_relearn_dialog")
}
/** 自动播放次数选择: 仅单词会有该选项 */
private fun showAutoPlaySelectDialog() {
if (autoPlaySeletDialog == null) {
autoPlaySeletDialog = BottomSheetDialog(this, R.style.dialog_style).apply {
val autoPlayBinding = DataBindingUtil.inflate<DialogBottomAutoPlaySelectBinding>(
layoutInflater, R.layout.dialog_bottom_auto_play_select, null, false
)
val autoPlayBinding = DataBindingUtil.inflate<DialogBottomAutoPlaySelectBinding>(layoutInflater,
R.layout.dialog_bottom_auto_play_select,
null, false)
setContentView(autoPlayBinding.root)
autoPlayBinding.ivCancel.click { v -> dismiss() }
autoPlayBinding.rvRepeat.run {
adapter = AdapterAutoPlaySelectRepeat().apply {
onItemClick = { _, position, _ ->
@@ -284,31 +263,48 @@ class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CourseP
}
autoPlaySeletDialog?.show()
}
/**
* 开始自动播放
* @param time Int
* @param time Int 播放次数
*/
private fun startAutoPlay(time: Int) {

private fun startAutoPlay(time : Int) {
//查询单词,进入学习界面,开始播放
(childFragments[binding.viewPager2.currentItem] as CourseMainFragment).vm.let {
it.queryWordAutoPlayWord().observe(this) { value ->
value?.let { data ->
val lesson = Lesson(it.course.subjectId, it.course.coursePackId, it.course.coursePackType,
it.course.courseId, it.course.courseType, 0, "", 0, it.course.courseTitle)
DataTransferHolder.instance.putData(value = LearnData(lesson).apply {
learnWordList = data
isAutoPlay = true
autoPlayTime = time
})
startActivity(LearnWordActivity::class.java)
} ?: let {
showToast("未查询到自动播放数据")
}
}
}
}
override fun onStop() {
super.onStop()
//如果是中文项目,更新课程包的进度
if (vm.coursePack.subjectId == AppConstants.SUBJECT_CHINESE){
if (vm.coursePack.subjectId == AppConstants.SUBJECT_CHINESE) {
vm.coursePack.learnProgress = vm.coursePack.childrenCourses[0].courseLearnProgress
}
}


/** ViewModel Factory工厂 */
inner class ViewModelFactory(private val subjectId: Int, private val coursePackInPosition: Int) :
inner class ViewModelFactory(private val subjectId : Int, private val coursePackInPosition : Int) :
ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
override fun <T : ViewModel?> create(modelClass : Class<T>) : T {
return CoursePackMainActivityViewModel(subjectId, coursePackInPosition) as T
}
}
}

+ 0
- 3
app/src/main/java/com/xkl/cdl/module/m_center_learn/CoursePackMainActivityViewModel.kt View File

@@ -17,7 +17,4 @@ class CoursePackMainActivityViewModel(subjectId: Int , coursePackInPosition : In
//设置显示当前课程的进度值和显示内容
val currentCourseProgress = MutableLiveData<Double>()
//更多点击是否有效
var showMoreEnable = false
}

+ 45
- 9
app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseLessonFragment.kt View File

@@ -127,7 +127,7 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM
adapterLesson.notifyItemChanged(learnEventData.leesonPositionIndex)
vm.allLesson[learnEventData.leesonPositionIndex].let {
val key = "${it.chapterId}_${it.lessonId}"
vm.courseDetail.after.put(key, it.beforeTestScore)
vm.courseDetail.after.put(key, it.afterTestScore)
}
}
}
@@ -235,7 +235,7 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM
} else if (!entity.learnIsOver) { //当前课时未学完,直接开始学习
startLearn(entity)
} else if (entity.afterTestScore == AppConstants.NOT_DOING) {
loadLessonAfterTest(entity)
loadLessonAfterTest(entity,true)
} else { //当前课时学习完成的弹窗
showLessonAllOverDialog(entity)
}
@@ -338,13 +338,49 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM
}
}
/** 开始课时学后测试
* 请求数据后,直接开始跳转
/** 请求测试数据,去进行课时学后测试
* @param isNeedShowLearningOverDialog 是否需要显示课时学习完成,去学后测试的弹窗,只有在直接进行item点击的时候才为true
*/
private fun loadLessonAfterTest(lesson : Lesson) {
private fun loadLessonAfterTest(lesson : Lesson, isNeedShowLearningOverDialog:Boolean = false) {
vm.loadTest(AppConstants.TEST_TYPE_AFTER, lesson).observe(this) {
startLessonTest(lesson, AppConstants.TEST_TYPE_AFTER, it)
when {
!isNeedShowLearningOverDialog -> startLessonTest(lesson, AppConstants.TEST_TYPE_AFTER, it)
else -> LearnDialog.newInstance(LearnDialogBean(AppConstants.DIALOG_TYPE_LESSON_ITEM_CLICK_NOT_DOING_AFTER_TEST).apply {
correctNumber = lesson.correctNumber
errorNumber = lesson.errorNumber
this.showTimeCount = CourseManager.expectedTestTime(lesson.courseType,AppConstants.TEST_TYPE_AFTER,it)
}).apply {
onDialogListener = { action, dialog ->
when (action) {
//重学动作
AppConstants.DIALOG_LESSON_RELEARN -> CommonDialog.newInstance(CommonDialogBean(titleText = R.string.lesson_relearn_title,
contentText = R.string.lesson_relearn_content,
leftText = R.string.cancel,
rightText = R.string.sure))
.apply {
onCommonDialogButtonClickListener = { relearnDialog, isRightClick ->
relearnDialog.dismissAllowingStateLoss()
if (isRightClick){
dialog.dismissAllowingStateLoss()
//发送动作 : 重新学习
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post(
LearnEventData(lesson.subjectId,
lesson.courseId,
AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN).apply {
leesonPositionIndex = lesson.lessonPositionInList
})
}
}
}.show(childFragmentManager,"lesson_relearn_tip")
// 开始学后测试
AppConstants.DIALOG_START_TEST -> {
dialog.dismissAllowingStateLoss()
startLessonTest(lesson, AppConstants.TEST_TYPE_AFTER, it)
}
}
}
}.show(childFragmentManager, "learn_over_dialog")
}
}
}
@@ -377,7 +413,7 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM
* 当前课时学前、学习、学后都完成了的弹窗
*/
private fun showLessonAllOverDialog(lesson : Lesson) {
LearnDialog.newInstance(LearnDialogBean(AppConstants.DIALOG_TYPE_LESSON_ITEM_OVER).apply {
LearnDialog.newInstance(LearnDialogBean(AppConstants.DIALOG_TYPE_LESSON_ITEM_CLICK_ALL_OVER).apply {
correctNumber = lesson.correctNumber
errorNumber = lesson.errorNumber
score = lesson.afterTestScore.toInt()
@@ -439,7 +475,7 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM
courseType = vm.course.courseType
this.testData = it
}
(parentFragment as CourseMainFragment).startExam(examData)
(requireParentFragment().parentFragment as CourseMainFragment).startExam(examData)
}
}
}

+ 0
- 1
app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseMainFragment.kt View File

@@ -42,7 +42,6 @@ class CourseMainFragment : BaseFragmentVM<FragmentCourseMainBinding, CourseMainF
requireArguments().getInt(AppConfig.INTENT_1)))[CourseMainFragmentViewModel::class.java].apply {
coursePackMainActivityVM = ViewModelProvider(requireActivity())[CoursePackMainActivityViewModel::class.java]
}
lifecycle.addObserver(vmmodel)
return vmmodel
}

+ 85
- 28
app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseMainFragmentViewModel.kt View File

@@ -2,11 +2,13 @@ package com.xkl.cdl.module.m_center_learn.coursechildren

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import com.google.common.base.Joiner
import com.suliang.common.base.viewmodel.BaseViewModel
import com.suliang.common.extension.diskIo2DiskIo
import com.suliang.common.extension.diskIo2Main
import com.suliang.common.util.DateUtil
import com.suliang.common.util.file.FileUtil
import com.suliang.common.util.thread.AppExecutors
import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.bean.LearnWord
import com.xkl.cdl.data.bean.course.Course
@@ -19,8 +21,11 @@ import com.xkl.cdl.data.manager.db.DBCourseManager
import com.xkl.cdl.data.manager.db.DbControlBase
import com.xkl.cdl.data.repository.DataRepository
import com.xkl.cdl.module.m_center_learn.CoursePackMainActivityViewModel
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Scheduler
import io.reactivex.rxjava3.functions.BiFunction
import io.reactivex.rxjava3.schedulers.Schedulers
import mqComsumerV1.Struct
import java.io.File
import java.util.*
@@ -61,18 +66,17 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() {
//获取课程的章节数据
fun loadMain() : MutableLiveData<Boolean> {
val mutableLiveData = MutableLiveData<Boolean>()
Observable.zip(DataRepository.getCourseStatistics(course.subjectId,course.coursePackId,course.courseId).flatMap {
Observable.zip(DataRepository.getCourseStatistics(course.subjectId, course.coursePackId, course.courseId).flatMap {
courseDetail = it
course.courseLearnProgress = it.courseLearnProgress
coursePackMainActivityVM.currentCourseProgress.postValue(course.courseLearnProgress)
return@flatMap DataRepository.getCourseAllLesson(dbControlBase, it)
}.flatMap {
allLesson = it
return@flatMap DataRepository.getCourseCollect()
},
DataRepository.getReviewData(),
}, DataRepository.getReviewData(),
BiFunction<HashMap<String, Long>, Array<String>, Boolean> { t1 : HashMap<String, Long>?, t2 : Array<String>? ->
// TODO: 2022/5/5 初始化需要复习的数据
showMoreIsEnable()
true
}).compose(diskIo2Main()).subscribe {
mutableLiveData.value = it
@@ -81,8 +85,8 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() {
}
/** 课程包主页上的更多按钮点击是否有效 */
private fun showMoreIsEnable(){
coursePackMainActivityVM.showMoreEnable = courseDetail.st_before != AppConstants.NOT_DOING
fun showMoreIsEnable() : Boolean {
return courseDetail.st_before != AppConstants.NOT_DOING
}
/**
@@ -131,18 +135,17 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() {
* @param lesson Lesson 课时
* @return MutableLiveData<LearnData>
*/
fun loadLessonLearnData(lesson : Lesson): MutableLiveData<LearnData>{
fun loadLessonLearnData(lesson : Lesson) : MutableLiveData<LearnData> {
val result = MutableLiveData<LearnData>()
Observable.create<List<LearnWord>> {
it.onNext(DBCourseManager.queryLearnData(dbControlBase, lesson))
it.onComplete()
}.compose(diskIo2Main())
.subscribe {
result.value = LearnData(lesson).apply {
learnWordList = it
examErrorMap = courseDetail.exam_w_r_list
}.compose(diskIo2Main()).subscribe {
result.value = LearnData(lesson).apply {
learnWordList = it
examErrorMap = courseDetail.exam_w_r_list
}
}
}
return result
}
@@ -169,7 +172,7 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() {
fun initSelectPosition() : Int {
//没有学习点,默认课程学习课时位置为第一个
var temposition = 0
when{
when {
//学后总测试完成,默认无选中
courseDetail.st_after != AppConstants.NOT_DOING -> temposition = -1
//有学习点
@@ -191,16 +194,17 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() {
* 课时重学
* @param lessonPositionIndex Int 重学课时所在位置
*/
fun relearnLesson(lessonPositionIndex : Int):MutableLiveData<Boolean> {
fun relearnLesson(lessonPositionIndex : Int) : MutableLiveData<Boolean> {
val result = MutableLiveData<Boolean>()
//重学课时后课程的进度
val courseProgress = CourseManager.calculateEnglishCourseProgress(allLesson,true,lessonPositionIndex)
val courseProgress = CourseManager.calculateEnglishCourseProgress(allLesson, true, lessonPositionIndex)
//重学课时后项目的总进度
val subjectProgress = CourseManager.calculateSubjectProgressWithCourseLessonRelearn(course.subjectId,course.coursePackId,course.courseId,courseProgress)
val subjectProgress = CourseManager.calculateSubjectProgressWithCourseLessonRelearn(course.subjectId, course.coursePackId,
course.courseId, courseProgress)
Observable.fromCallable {
// TODO: 2022/5/5 进行数据清除和保存
}.compose(diskIo2DiskIo()).subscribe{
}.compose(diskIo2DiskIo()).subscribe {
//更新lesson
val lesson = allLesson[lessonPositionIndex].apply {
errorNumber = 0
@@ -223,11 +227,11 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() {
//移除总测、课时学前测试的学习错误数据
exam_w_r_list.filterKeys {
it.startsWith(wordStartKey)
}.forEach{
}.forEach {
exam_w_r_list.remove(it.key)
}
//移除错误本中的记录
temporary_words.filterKeys { it.startsWith(wordStartKey) }.forEach{
temporary_words.filterKeys { it.startsWith(wordStartKey) }.forEach {
temporary_words.remove(it.key)
}
//更新课程进度
@@ -235,8 +239,7 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() {
course.courseLearnProgress = courseProgress
coursePackMainActivityVM.currentCourseProgress.postValue(courseProgress)
//更新统计总进度
CourseManager.calculateSubjectProgress(course.subjectId, course.coursePackId,
course.courseId, courseProgress)
CourseManager.calculateSubjectProgress(course.subjectId, course.coursePackId, course.courseId, courseProgress)
//完成
result.postValue(true)
}
@@ -248,16 +251,18 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() {
/**
* 课程重学方法
*/
fun relearnCourse(): MutableLiveData<Boolean> {
fun relearnCourse() : MutableLiveData<Boolean> {
val result = MutableLiveData<Boolean>()
//重学需要重新计算进度
val subjectProgress = CourseManager.calculateSubjectProgressWithCourseRelearn(course.subjectId,course.coursePackId,course.courseId)
val subjectProgress = CourseManager.calculateSubjectProgressWithCourseRelearn(course.subjectId, course.coursePackId,
course.courseId)
Observable.fromCallable {
// TODO: 2022/5/5 调用课程重学 重新请求详情
val file = File(FileUtil.getSaveDirPath("appcache"), "${course.subjectId}_${course.coursePackId}_${course.courseId}")
if (file.exists()){ file.delete() }
}.compose(diskIo2DiskIo())
.subscribe{
if (file.exists()) {
file.delete()
}
}.compose(diskIo2DiskIo()).subscribe {
courseDetail.run {
courseLearnProgress = 0.0
st_before = AppConstants.NOT_DOING
@@ -282,9 +287,61 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() {
// TODO: 2022/5/6 这里在退出时进行了统计数据缓存保存,在loadMain的时候进行了read缓存 ,后期需要清除
override fun onDestroy(owner : LifecycleOwner) {
val objectToBytes = FileUtil.objectToBytes(courseDetail)
FileUtil.writeBytesToFile(FileUtil.getSaveDirPath("appcache"),"${course.subjectId}_${course.coursePackId}_${course.courseId}",objectToBytes)
FileUtil.writeBytesToFile(FileUtil.getSaveDirPath("appcache"),
"${course.subjectId}_${course.coursePackId}_${course.courseId}", objectToBytes)
super.onDestroy(owner)
}
/**
* 单词类,自动播放,查询自动播放使用的单词
* @return MutableLiveData<LearnData?>
*/
fun queryWordAutoPlayWord() : MutableLiveData<List<LearnWord>?> {
val result = MutableLiveData<List<LearnWord>?>()
Observable.create<Boolean> {
//获取当前学习点所在课时
val courseLearnPoint = courseDetail.course_learn_point
//没有学习点,则没有可自动播放的单词
if (courseLearnPoint.isEmpty()) {
result.postValue(null)
it.onComplete()
return@create
}
//有学习点,找到学习点的课时,先自动播放向后播放,再自动播放向前播放
val allAutoPlayWordList = mutableListOf<LearnWord>()
val split = courseLearnPoint.split("_")
val currentLessonChapterId = split[0].toLong()
val currentLessonLessonId = split[1].toLong()
var isFindCurrentLesson = false
allLesson.forEachIndexed { index, lesson ->
//进行判断,查询,学习内容
if (lesson.learnedIndex != -1) { //学习位置不为-1 ,即有已学
//查询
val wordIds = Joiner.on(",").join(lesson.wordIds.subList(0, lesson.learnedIndex + 1))
DBCourseManager.queryLearnWord(dbControlBase, lesson.chapterId, lesson.lessonId, wordIds).let {
//当前学习课时
if (lesson.chapterId == currentLessonChapterId && lesson.lessonId == currentLessonLessonId) {
isFindCurrentLesson = true
//添加到第一个
allAutoPlayWordList.addAll(0, it)
} else {
when {
isFindCurrentLesson -> allAutoPlayWordList.addAll(1, it)// 直接插入到第一个后面,第一个则为当前课时
else -> allAutoPlayWordList.addAll(it) //直接添加到最后就行
}
}
}
}
}
if (allAutoPlayWordList.isEmpty()) {
result.postValue(null)
} else {
result.postValue(allAutoPlayWordList)
}
}.subscribeOn(Schedulers.from(AppExecutors.diskIO)).subscribe()
return result
}
}

+ 3
- 3
app/src/main/java/com/xkl/cdl/util/LearnRuleUtil.kt View File

@@ -308,7 +308,7 @@ class LearnRuleUtil<T : BaseWord> constructor(private val originLearnList : List
currentCorrectMap[key] = true
}
if (isLearnFirst) currentIsError = false
LogUtil.e("currentCorrectMap--->${currentCorrectMap.size} --> $currentCorrectMap")
// LogUtil.e("currentCorrectMap--->${currentCorrectMap.size} --> $currentCorrectMap")
}
/**
@@ -326,7 +326,7 @@ class LearnRuleUtil<T : BaseWord> constructor(private val originLearnList : List
} else if (!isInExamErrorMap(key) && !isInCurrentErrorMap(key)) { //测试中数据的错误
currentErrorMap[key] = true
}
LogUtil.e("currentErrorMap --->${currentErrorMap.size} $currentErrorMap")
// LogUtil.e("currentErrorMap --->${currentErrorMap.size} $currentErrorMap")
//开始进行数据扩容替换
replaceErrorToNull(item) //替换已有错误为空
insertErrorWord(item, false) //开始插入
@@ -346,7 +346,7 @@ class LearnRuleUtil<T : BaseWord> constructor(private val originLearnList : List
}
/** 判断源数据中的数据是否已经学习完成:由外部调用,originLearnPosition下标的数据为已经取出来学习的数据,只有为第一个的时候,才判断此方法 */
fun isLearnOverForOrigin() : Boolean {
private fun isLearnOverForOrigin() : Boolean {
return originLearnPosition >= originLearnList.size - 1
}
}

+ 46
- 34
app/src/main/res/layout/activity_learn_word.xml View File

@@ -22,7 +22,7 @@
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@color/gray_1"
app:layout_constraintTop_toTopOf="@id/rv_historical_route"/>
app:layout_constraintTop_toTopOf="@id/rv_historical_route" />
<!--历史轨迹-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_historical_route"
@@ -31,15 +31,14 @@
android:orientation="horizontal"
android:paddingStart="@dimen/global_spacing"
android:paddingEnd="@dimen/global_spacing"
app:layout_constraintTop_toBottomOf="@+id/inc_learn_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:reverseLayout="true"
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="1"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/inc_learn_title"
tools:itemCount="1"
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_historical_route"
/>
tools:reverseLayout="true" />

<TextView
android:id="@+id/tv_banner"
@@ -50,8 +49,8 @@
android:text="@string/auto_playing"
android:textColor="@color/theme_color"
android:textSize="@dimen/smallerSize"
app:layout_constraintTop_toBottomOf="@+id/rv_historical_route"
android:visibility="gone"/>
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@+id/rv_historical_route" />


<!--内容布局-->
@@ -103,12 +102,11 @@


<!--详情-->
<!-- <include layout="@layout/inc_word_detail" />-->
<!-- <include layout="@layout/inc_word_detail" />-->
<!--拼写提示-->
<!-- <include layout="@layout/inc_spell_learn_tip" />-->
<!-- <include layout="@layout/inc_spell_learn_tip" />-->

</FrameLayout>


<!--总时间-->
@@ -121,51 +119,65 @@
android:textColor="@color/gray_2"
android:textSize="@dimen/smallerSize"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

<Button
<com.google.android.material.button.MaterialButton
android:id="@+id/tv_play"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="wrap_content"
android:layout_height="22dp"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
tools:text="暂停播放"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="@dimen/global_spacing"
android:layout_marginBottom="4dp"
android:gravity="center"
android:insetTop="0dp"
android:insetBottom="0dp"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textColor="@color/theme_color"
app:strokeColor = "@color/theme_color"
app:strokeWidth="@dimen/line_height"
android:layout_marginBottom="4dp"
android:layout_marginStart="@dimen/global_spacing"
android:textSize="@dimen/smallerSize"
android:visibility="gone"
app:icon="@drawable/ic_play_pause"
app:iconGravity="textStart"
app:iconSize="16dp"
app:iconTint="@color/theme_color"
app:rippleColor="@color/white"/>
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:rippleColor="@color/white"
app:strokeColor="@color/theme_color"
app:strokeWidth="@dimen/line_height"
tools:text="暂停播放"
tools:visibility="visible" />

<Button
<com.google.android.material.button.MaterialButton
android:id="@+id/tv_play_stop"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="wrap_content"
android:layout_height="22dp"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
tools:text="停止播放"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="@dimen/global_spacing"
android:layout_marginBottom="4dp"
android:gravity="center"
android:insetTop="0dp"
android:insetBottom="0dp"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textColor="@color/theme_color"
app:strokeColor = "@color/theme_color"
app:strokeWidth="@dimen/line_height"
android:layout_marginBottom="4dp"
android:layout_marginEnd="@dimen/global_spacing"
android:textSize="@dimen/smallerSize"
android:visibility="gone"
app:icon="@drawable/ic_play_stop"
app:iconSize="10dp"
app:iconTint="@color/theme_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:rippleColor="@color/white"
/>
app:strokeColor="@color/theme_color"
app:strokeWidth="@dimen/line_height"
tools:text="停止播放"
tools:visibility="visible" />


</androidx.constraintlayout.widget.ConstraintLayout>

+ 1
- 0
app/src/main/res/layout/inc_learn_word.xml View File

@@ -45,6 +45,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/img_word"
app:layout_goneMarginTop="87dp"
android:visibility="gone"
tools:visibility="visible" />


+ 7
- 1
app/src/main/res/values/strings.xml View File

@@ -72,11 +72,17 @@
<string name="phrase">词组</string>
<string name="example">例句</string>
<string name="reference">参考</string>
<string name="auto_playing">自动播放中···</string>
<string name="auto_playing">自动播放中…</string>
<string name="auto_play_banner_paused">已暂停</string>
<string name="auto_play_continue">继续播放</string>
<string name="auto_play_paused">暂停播放</string>
<string name="auto_play_stop">停止播放</string>
<string name="quit_learn_title">你确定要退出本课程的学习吗?</string>
<string name="quit_learn_content">退出后系统将保存你的学习进度</string>
<string name="course_relearn_title">你确定要清空本课程的学习记录吗?</string>
<string name="course_relearn_content">清空后你的学习记录将不可恢复,请谨慎操作</string>
<string name="course_relearn_sure">确认清空</string>
<string name="quit_auto_play_title">你确定要退出自动播放吗?</string>
<string name="quit_auto_play_title_over">本课程自动播放完毕</string>

</resources>

+ 1
- 1
lib/common/src/main/java/com/suliang/common/base/activity/BaseActivityVM.kt View File

@@ -28,7 +28,7 @@ abstract class BaseActivityVM<VB : ViewBinding, VM : BaseViewModel> : BaseActivi
vm.pageEvent.observe(this) { startActivity(it) }
vm.toastEvent.observe(this){ showToast(it) }
vm.loadingEvent.observe(this) {
LogUtil.e("监听到 activity vm 的 loadingEvent isShow $it")
// LogUtil.e("监听到 activity vm 的 loadingEvent isShow $it")
showHideLoading(it)}

}

+ 13
- 0
lib/common/src/main/java/com/suliang/common/util/DrawableUti.kt View File

@@ -14,6 +14,7 @@ import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import java.io.ByteArrayOutputStream

@@ -21,6 +22,7 @@ import java.io.ByteArrayOutputStream
* author suliang
* create 2022/3/25 17:08
* Describe:
* Android 中 Bitmap 和 Drawable 相互转换的方法 https://blog.csdn.net/l_lhc/article/details/50923372
*/
class DrawableUti {
companion object {
@@ -93,6 +95,17 @@ class DrawableUti {
return BitmapFactory.decodeByteArray(byteArray,0,byteArray.size)
}
fun changeSvgSizeAndColor(resource:Resources,svgId : Int,changeColor : Int,scale:Int):Drawable{
val vectorDrawableCompat = VectorDrawableCompat.create(resource, svgId, null)!!
vectorDrawableCompat.setTint(ResourcesCompat.getColor(resource, changeColor, null))
val oldBitmap = vectorDrawableCompat.toBitmap(scale*vectorDrawableCompat.intrinsicWidth,scale*vectorDrawableCompat.intrinsicHeight,Bitmap.Config.ARGB_8888)
return BitmapDrawable(resource,oldBitmap)
// val newBitmap = Bitmap.createScaledBitmap(oldBitmap,scale*oldBitmap.width,scale*oldBitmap.height,true)
}
}
}

+ 2
- 2
lib/common/src/main/java/com/suliang/common/util/media/MPUtil.kt View File

@@ -296,9 +296,9 @@ internal class MPUtil : IMP,MediaPlayer.OnPreparedListener,MediaPlayer.OnErrorLi
val temp = currentState
synchronized(temp) {
listener?.let {
LogUtil.e("MPUtil发送 --》 ${Thread.currentThread()}")
// LogUtil.e("MPUtil发送 --》 ${Thread.currentThread()}")
Handler(Looper.getMainLooper()).post {
LogUtil.e("MPUtil发送 --》 ${temp.name}")
// LogUtil.e("MPUtil发送 --》 ${temp.name}")
it.onMpState(temp)
}
}

Loading…
Cancel
Save