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

<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.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_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_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_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_main.xml" value="0.5" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_splash.xml" value="0.4921875" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_splash.xml" value="0.4921875" />
<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_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.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_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_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_spell.xml" value="0.5061538461538462" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_spoken.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

if (currentSelectPosition == position) return@click if (currentSelectPosition == position) return@click
//点击改变选中值与显示 //点击改变选中值与显示
previousSelectPosition = currentSelectPosition previousSelectPosition = currentSelectPosition
previousIsShowLine = !(previousSelectPosition == itemCount-1 && lastIsLearnOver)
previousIsShowLine = previousSelectPosition == itemCount-1 && !lastIsLearnOver
currentSelectPosition = position currentSelectPosition = position
currentIsShowLine = true currentIsShowLine = true
notifyItemChanged(previousSelectPosition) notifyItemChanged(previousSelectPosition)

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

executePendingBindings() executePendingBindings()
initColor(position, lesson) 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 { layoutContent.click {
//选中项非当前项,则需要改变选中颜色,直接通知更新,调用对应位置的notfy //选中项非当前项,则需要改变选中颜色,直接通知更新,调用对应位置的notfy
} }
} else { //未选中:根据学习情况判断 } else { //未选中:根据学习情况判断
layoutContent.setBackgroundColor(translationColor) //未选中背景透明 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 { tvLessonName.run {
setTextColor(normalColorLearnOver) //课时名称颜色 setTextColor(normalColorLearnOver) //课时名称颜色
paint.isFakeBoldText = false //加粗 paint.isFakeBoldText = false //加粗

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

private fun playItem(letter : Char) { private fun playItem(letter : Char) {
if (letter.isLetter()) { if (letter.isLetter()) {
when (defaultSoundWay) { 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 { } else {
MPManager.playAsset("common_voice/konge.mp3") MPManager.playAsset("common_voice/konge.mp3")

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

const val DIALOG_TYPE_EXAM_START = 1 const val DIALOG_TYPE_EXAM_START = 1
const val DIALOG_TYPE_EXAM_OVER = 2 const val DIALOG_TYPE_EXAM_OVER = 2
/** 对话框类型: 学习结束弹窗类型 */ /** 对话框类型: 学习结束弹窗类型 */
const val DIALOG_TYPE_LEARN_OVER = 3
const val DIALOG_TYPE_LEARNING_OVER = 3
/**对话框类型,item学习完成*/ /**对话框类型,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 改变界面 到目录页 */ /**action key 改变界面 到目录页 */

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

* author suliang * author suliang
* create 2022/4/2 14:55 * create 2022/4/2 14:55
* Describe: 课时学习时的传参 * Describe: 课时学习时的传参
* @param lesson 课时学习是为课时数据,其他特殊时候,如自动播放,只是用以包装课程的基本信息如果项目id、课程包类型、课程类型等
*/ */
class LearnData(val lesson: Lesson) { class LearnData(val lesson: Lesson) {
/**学习数据*/ /**学习数据*/
var learnWordList : List<LearnWord> = mutableListOf() 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

val learnIndex = wordIds.indexOf(detail.lesson_learn_point.getOrElse(key, { -1 })) //学习位置,当前位置为已学 val learnIndex = wordIds.indexOf(detail.lesson_learn_point.getOrElse(key, { -1 })) //学习位置,当前位置为已学
// 注:针对口语对话课时,如果没有学习完,是不会设置进度点的。
// 注:针对口语对话课时,如果没有学习完,是不会设置进度点的。 todo 分课程类型设置进度点
val learnIsOver = wordIds.size - 1 == learnIndex val learnIsOver = wordIds.size - 1 == learnIndex
val lesson = Lesson(base.subjectId, val lesson = Lesson(base.subjectId,
} }
mutableList.add(lesson) mutableList.add(lesson)
positionIndex += 1 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() it.close()
} }
AppConstants.COURSE_TYPE_ENGLISH_SPELL -> { //拼写 不需要查询测试表,不需要选项 AppConstants.COURSE_TYPE_ENGLISH_SPELL -> { //拼写 不需要查询测试表,不需要选项
"SELECT id, word_id,word,chapter_id,lesson_id,basic_explaination FROM chapter " + when (testType) { "SELECT id, word_id,word,chapter_id,lesson_id,basic_explaination FROM chapter " + when (testType) {
AppConstants.TEST_TYPE_BEFORE, AppConstants.TEST_TYPE_AFTER -> { //学前、学后测 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 -> { //口语 AppConstants.COURSE_TYPE_ENGLISH_SPOKEN -> { //口语
when (testType) { when (testType) {
return result 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

import android.util.LruCache import android.util.LruCache
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.suliang.common.extension.diskIo2Main 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.DBCourseManager
import com.xkl.cdl.data.manager.db.DbControlBase import com.xkl.cdl.data.manager.db.DbControlBase
import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Observable
fun get(dbControlBase : DbControlBase, wordId : Long) { fun get(dbControlBase : DbControlBase, wordId : Long) {
//先查对应的文件路径 : 项目id_课程包id_课程id_单词id_发音方式 //先查对应的文件路径 : 项目id_课程包id_课程id_单词id_发音方式
val defaultKey = "${dbControlBase.subjectId}_${dbControlBase.coursePackId}_${dbControlBase.courseId}_${wordId}" 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

} }
}?:let { }?:let {
binding.tvLeft.visibility = View.GONE binding.tvLeft.visibility = View.GONE
binding.vSpace.visibility = View.GONE
binding.vSplit.visibility = View.GONE
} }
rightColor?.let { rightColor?.let {
binding.tvRight.setTextColor(ContextCompat.getColor(requireContext(),it)) binding.tvRight.setTextColor(ContextCompat.getColor(requireContext(),it))

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

AppConstants.TEST_TYPE_AFTER_TOTAL -> initCourseAfterTestOver() 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)
} }
} }
/** /**
* 课时学习中的学习完成 * 课时学习中的学习完成
* @param isShowCloseImg 是否需要显示关闭按钮
*/ */
private fun initLessonLearningOver(){
private fun initLessonLearningOver(isShowCloseImg:Boolean){
initNumber() initNumber()
binding.run { binding.run {
} }
binding.tvLeft.click { onDialogListener(AppConstants.DIALOG_LESSON_RELEARN, this) } binding.tvLeft.click { onDialogListener(AppConstants.DIALOG_LESSON_RELEARN, this) }
binding.tvRight.click { onDialogListener(AppConstants.DIALOG_START_TEST, 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

import android.app.Application import android.app.Application
import com.suliang.common.util.LogUtil import com.suliang.common.util.LogUtil
import com.tencent.mmkv.MMKV 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 net.sqlcipher.database.SQLiteDatabase
import java.io.IOException
import java.lang.IllegalArgumentException
import java.lang.IllegalStateException
import java.lang.NullPointerException
import java.util.* import java.util.*


/** /**
SQLiteDatabase.loadLibs(this) SQLiteDatabase.loadLibs(this)
// ImageLoader.mStrategy = GlideLoaderStrategy() // ImageLoader.mStrategy = GlideLoaderStrategy()
LogUtil.e(UUID.randomUUID().toString().replace("-","")) LogUtil.e(UUID.randomUUID().toString().replace("-",""))
//初始MMKV存储
val rootDir = MMKV.initialize(this) val rootDir = MMKV.initialize(this)
LogUtil.e(rootDir) 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

import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.suliang.common.base.viewmodel.BaseViewModel import com.suliang.common.base.viewmodel.BaseViewModel
import com.suliang.common.util.LogUtil import com.suliang.common.util.LogUtil
import com.xkl.cdl.data.bean.intentdata.LearnData
import java.util.* import java.util.*
import kotlin.concurrent.timerTask import kotlin.concurrent.timerTask


val validTime : MutableLiveData<Long> = MutableLiveData(0) val validTime : MutableLiveData<Long> = MutableLiveData(0)
/**记录当前倒计时已运行时间 默认18秒 */ /**记录当前倒计时已运行时间 默认18秒 */
private var currentValidSurplusTime = validMaxTime
var currentValidSurplusTime = validMaxTime
/** 开始计时 */ /** 开始计时 */
timeTask = timerTask { timeTask = timerTask {
totalUseTime.postValue(totalUseTime.value?.plus(200)) 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) totalTimer.schedule(timeTask, 200, 200)
LogUtil.e("开始总计时") LogUtil.e("开始总计时")
} }
/** 有效时间记录规则 */
open fun initRunValidTime(){
if (isRunValidTime) {
validTime.postValue(validTime.value!!.plus(200))
currentValidSurplusTime -= 200
isRunValidTime = currentValidSurplusTime > 0
}
}
/** 停止计时 */ /** 停止计时 */
fun stopTotalCountTing() { fun stopTotalCountTing() {
LogUtil.e("停止总计时")
// LogUtil.e("停止总计时")
timeTask?.cancel() timeTask?.cancel()
timeTask = null timeTask = null
} }

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

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

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

package com.xkl.cdl.module.learn package com.xkl.cdl.module.learn


import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.view.MotionEvent import android.view.MotionEvent
import com.suliang.common.base.activity.BaseActivityVM import com.suliang.common.base.activity.BaseActivityVM
import com.suliang.common.extension.click import com.suliang.common.extension.click
import com.suliang.common.util.DateUtil import com.suliang.common.util.DateUtil
import com.suliang.common.util.DrawableUti
import com.suliang.common.util.LogUtil import com.suliang.common.util.LogUtil
import com.suliang.common.util.image.ImageLoader import com.suliang.common.util.image.ImageLoader
import com.suliang.common.util.media.EMediaState import com.suliang.common.util.media.EMediaState
/** 横幅 自动播放 与 复习时使用 */ /** 横幅 自动播放 与 复习时使用 */
private fun initBanner() { private fun initBanner() {
// TODO: 2022/4/25 初始化横幅,默认隐藏 // 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))
}
}
} }
/** 标题初始 */ /** 标题初始 */
private fun initHistoricalRoute() { private fun initHistoricalRoute() {
adapterHistorical = AdapterHistoricalRoute(vm.learnData.lesson.courseType).apply { adapterHistorical = AdapterHistoricalRoute(vm.learnData.lesson.courseType).apply {
onItemClick = { v : View, position : Int, item : LearnWord -> onItemClick = { v : View, position : Int, item : LearnWord ->
//历史轨迹的点击,改变当前选项
vm.currentIsHistoricalItemClick = true
vm.currentLearnWord.value = item
if (!vm.learnData.isAutoPlay) { //自动播放历史轨迹点击无效
vm.clickHistoricalItem(item)
}
} }
} }
binding.rvHistoricalRoute.apply { binding.rvHistoricalRoute.apply {
} }
AppConstants.COURSE_TYPE_ENGLISH_VOICE -> { AppConstants.COURSE_TYPE_ENGLISH_VOICE -> {
bindingWord = IncLearnWordBinding.inflate(layoutInflater, binding.containerLayout, true) 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 -> { else -> {
bindingWord = IncLearnWordBinding.inflate(layoutInflater, binding.containerLayout, true) bindingWord = IncLearnWordBinding.inflate(layoutInflater, binding.containerLayout, true)
bindingWord.incWord.tvWord.click { readWord() } 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() { private fun initControlButton() {
//自动播放隐藏操作按钮
if (vm.learnData.isAutoPlay){
binding.incControlButton.root.visibility= View.GONE
return
}
//其他课程默认按钮全部隐藏 //其他课程默认按钮全部隐藏
binding.incControlButton.run { binding.incControlButton.run {
tvLeft.visibility = View.INVISIBLE tvLeft.visibility = View.INVISIBLE
//最底部 //最底部
private fun initBottom(){ private fun initBottom(){
binding.tvPlay.visibility = View.GONE
binding.tvPlayStop.visibility = View.GONE
//有效时间 //有效时间
vm.validTime.observe(this){ vm.validTime.observe(this){
binding.tvValidTime.text = "本次学习 ${DateUtil.formatGMT(it,DateUtil.FORMAT_2)}" 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 { private val impListener = object : IMPListener {
override fun onMpState(state : EMediaState) { 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()
}
} }
} }
} }
override fun loadData() { override fun loadData() {
vm.loadNext() 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) { AudioCache.initAudioLiveData().observe(this) {
//自动播放的单词音频数据
if (vm.learnData.isAutoPlay){
it?.run {
MPManager.play(it,listener = impListener)
}?:let {
showToast("未找到发音文件")
//获取下一条
vm.loadNext()
}
return@observe
}
it?.run { it?.run {
if (vm.learnData.lesson.courseType == AppConstants.COURSE_TYPE_ENGLISH_VOICE && bindingWord.ivVoice.visibility == View.VISIBLE) MPManager.play( if (vm.learnData.lesson.courseType == AppConstants.COURSE_TYPE_ENGLISH_VOICE && bindingWord.ivVoice.visibility == View.VISIBLE) MPManager.play(
it, listener = impListener) it, listener = impListener)
if (vm.isNeedLoadPhoto) { if (vm.isNeedLoadPhoto) {
//图片数据 //图片数据
PhotoCache.initPhotoLiveData().observe(this) { 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 }
} }
} }
adapterHistorical.addData(it) adapterHistorical.addData(it)
binding.rvHistoricalRoute.scrollToPosition(adapterHistorical.itemCount - 1) binding.rvHistoricalRoute.scrollToPosition(adapterHistorical.itemCount - 1)
} }
if (vm.learnData.isAutoPlay){
initAutoPlayWord(it)
return@observe
}
//界面初始 //界面初始
when (vm.learnData.lesson.courseType) { when (vm.learnData.lesson.courseType) {
AppConstants.COURSE_TYPE_ENGLISH_VOICE -> initVoice(it) AppConstants.COURSE_TYPE_ENGLISH_VOICE -> initVoice(it)
} }
} }
/**新数据到来,自动播放界面显示*/
@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") @SuppressLint("SetTextI18n")
private fun clickNext() { private fun clickNext() {
//拼写,需要处理历史轨迹 和在下一条的时候保存当前学习数据 //拼写,需要处理历史轨迹 和在下一条的时候保存当前学习数据
if (vm.learnData.lesson.courseType == AppConstants.COURSE_TYPE_ENGLISH_SPELL){ if (vm.learnData.lesson.courseType == AppConstants.COURSE_TYPE_ENGLISH_SPELL){
adapterHistorical.currentLearnOver()
when{ when{
//正确 //正确
vm.currentSpellIsCorrect -> { vm.currentSpellIsCorrect -> {
} }
} }
//当前数据为历史轨迹点击 且 最后一条学习未完成
if (vm.currentIsHistoricalItemClick && !adapterHistorical.lastIsLearnOver) {
//当前数据为历史轨迹点击 且 最后一条学习未完成 且不为最后一条
if (!adapterHistorical.currentIsLastSelect && vm.currentIsHistoricalItemClick && !adapterHistorical.lastIsLearnOver) {
skipToHistoricalLastItem() skipToHistoricalLastItem()
} else { } else {
vm.loadNext() vm.loadNext()
override fun dispatchTouchEvent(ev : MotionEvent?) : Boolean { override fun dispatchTouchEvent(ev : MotionEvent?) : Boolean {
//执行有效计时 //执行有效计时
vm.executeLearnValidTime()
when{
//自动播放只有在非播放时触摸有效
vm.learnData.isAutoPlay -> when{
!vm.isAutoPlaying -> vm.executeLearnValidTime()
}
else -> vm.executeLearnValidTime()
}
return super.dispatchTouchEvent(ev) return super.dispatchTouchEvent(ev)
} }
when { when {
vm.isAllOver -> finish() vm.isAllOver -> finish()
else -> when { 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() 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() { 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 -> 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 correctNumber = vm.learnData.lesson.correctNumber
errorNumber = vm.learnData.lesson.errorNumber errorNumber = vm.learnData.lesson.errorNumber
this.showTimeCount = showTimeCount this.showTimeCount = showTimeCount

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

import com.suliang.common.extension.createRandomNewChar import com.suliang.common.extension.createRandomNewChar
import com.suliang.common.extension.diskIo2Main import com.suliang.common.extension.diskIo2Main
import com.suliang.common.util.DateUtil import com.suliang.common.util.DateUtil
import com.suliang.common.util.LogUtil
import com.xkl.cdl.data.AppConstants import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.DataTransferHolder import com.xkl.cdl.data.DataTransferHolder
import com.xkl.cdl.data.bean.LearnWord import com.xkl.cdl.data.bean.LearnWord
//记录是否是历史轨迹的item点击 //记录是否是历史轨迹的item点击
var currentIsHistoricalItemClick : Boolean = false var currentIsHistoricalItemClick : Boolean = false
private set
//默认发音 //默认发音
var defaultSoundWay : Int = 0 var defaultSoundWay : Int = 0
//数据上传完成监听: true 学习完成的上传 false可能是半途的返回事件,直接退出 //数据上传完成监听: true 学习完成的上传 false可能是半途的返回事件,直接退出
val saveDataLiveData = MutableLiveData<Boolean>() 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() { fun loadNext() {
//修改标记 //修改标记
currentIsHistoricalItemClick = false currentIsHistoricalItemClick = false
//新数据自动播放次数归0
if (learnData.isAutoPlay){
currentPlayTime = 0
}
val word = learnRuleUtil.loadNext val word = learnRuleUtil.loadNext
word?.let { word?.let {
/** 设置已学位置 */ /** 设置已学位置 */
private fun setLearnPoint(currentIsLastSelect : Boolean, isCycleFirst : Boolean) { 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
}
} }
} }
// TODO: 2022/4/14 传递保存record信息 // TODO: 2022/4/14 传递保存record信息
// record 已经实例化并已经将数据保存 // record 已经实例化并已经将数据保存
if (!saveInit) { 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()) record.addDuration(saveCurrentLearnDuration())
saveInit = true saveInit = true
} }
/** 发送数据事件 */ /** 发送数据事件 */
private fun sendEventBus() { private fun sendEventBus() {
//自动播放不发送信息通知
if (learnData.isAutoPlay) return
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_DATA).post(LearnEventData(learnData.lesson.subjectId, LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_DATA).post(LearnEventData(learnData.lesson.subjectId,
learnData.lesson.courseId, learnData.lesson.courseId,
AppConstants.DATA_LESSON_LEARN_OVER).apply { AppConstants.DATA_LESSON_LEARN_OVER).apply {
stopTotalCountTing() 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

import com.xkl.cdl.adapter.AdapterAutoPlaySelectRepeat import com.xkl.cdl.adapter.AdapterAutoPlaySelectRepeat
import com.xkl.cdl.adapter.ViewPagerAdapter import com.xkl.cdl.adapter.ViewPagerAdapter
import com.xkl.cdl.data.AppConstants 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.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.binding.BindingAdapter
import com.xkl.cdl.data.manager.CourseManager 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.ActivityCourseMainBinding
import com.xkl.cdl.databinding.DialogBottomAutoPlaySelectBinding import com.xkl.cdl.databinding.DialogBottomAutoPlaySelectBinding
import com.xkl.cdl.databinding.DialogBottomCourseMoreBinding import com.xkl.cdl.databinding.DialogBottomCourseMoreBinding
import com.xkl.cdl.dialog.CommonDialog import com.xkl.cdl.dialog.CommonDialog
import com.xkl.cdl.dialog.CommonDialogBean 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.xkl.cdl.module.m_center_learn.coursechildren.CourseMainFragment
import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly


* 课程中心 * 课程中心
*/ */
class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CoursePackMainActivityViewModel>() { class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CoursePackMainActivityViewModel>() {
companion object { companion object {
@JvmStatic @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 { context.startActivity(Intent(context, CoursePackMainActivity::class.java).apply {
putExtra(AppConfig.INTENT_1, subjectId) putExtra(AppConfig.INTENT_1, subjectId)
putExtra(AppConfig.INTENT_2, coursePackInPosition) putExtra(AppConfig.INTENT_2, coursePackInPosition)
}) })
} }
} }
//子课程对应的Fragment //子课程对应的Fragment
private var childFragments = mutableListOf<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 subjectId = intent.getIntExtra(AppConfig.INTENT_1, 0)
val coursePackInListPosition = intent.getIntExtra(AppConfig.INTENT_2, 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() { override fun initStatusBar() {
statusBarOnly { statusBarOnly {
fitWindow = false //布局是否侵入状态栏 fitWindow = false //布局是否侵入状态栏
light = true 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 binding.coursePack = vm.coursePack
//状态栏高度设置 //状态栏高度设置
} }
//返回事件 //返回事件
binding.includeTitleBar.titleBar.run { binding.includeTitleBar.titleBar.run {
onBackClick = {_ -> finish() }
onBackClick = { _ -> finish() }
} }
//初始化tab和viewPager //初始化tab和viewPager
initTabAndViewPager() initTabAndViewPager()
} }
private fun initTabAndViewPager() { private fun initTabAndViewPager() {
//英语课程包且为英语单词课程包才显示tab //英语课程包且为英语单词课程包才显示tab
if (vm.coursePack.subjectId == AppConstants.SUBJECT_ENGLISH && vm.coursePack.coursePackType == AppConstants.COURSEPACK_TYPE_ENGLISH_WORD) { if (vm.coursePack.subjectId == AppConstants.SUBJECT_ENGLISH && vm.coursePack.coursePackType == AppConstants.COURSEPACK_TYPE_ENGLISH_WORD) {
adapter = ViewPagerAdapter(this@CoursePackMainActivity, childFragments) adapter = ViewPagerAdapter(this@CoursePackMainActivity, childFragments)
//注册页面改变监听 //注册页面改变监听
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
override fun onPageSelected(position : Int) {
super.onPageSelected(position) super.onPageSelected(position)
//改变tab的选中颜色 //改变tab的选中颜色
if (binding.includeCourseTypeTab.root.visibility == View.VISIBLE) { //可见,才做操作 if (binding.includeCourseTypeTab.root.visibility == View.VISIBLE) { //可见,才做操作
binding.includeCourseTypeTab.apply { binding.includeCourseTypeTab.apply {
tabDiscern.run { 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 { 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 { 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))
} }
} }
} }
} }
//更多按钮点击 //更多按钮点击
binding.includeCourseProgress.ivMore.click { 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() { override fun loadData() {
} }
/** 点击显示更多的弹窗 */ /** 点击显示更多的弹窗 */
private fun showMoreDialog() { private fun showMoreDialog() {
if (moreDialog == null) { if (moreDialog == null) {
moreDialog = BottomSheetDialog(this, R.style.dialog_style).apply { 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) setContentView(moreBinding.root)
//体验账号、拼些、口语课程、作文课程不显示自动播放 //体验账号、拼些、口语课程、作文课程不显示自动播放
when (vm.coursePack.childrenCourses[binding.viewPager2.currentItem].courseType) { 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 else -> moreBinding.tvAutoPlay.visibility = View.VISIBLE
} }
moreBinding.ivCancel.click { moreBinding.ivCancel.click {
} }
moreDialog?.show() 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) leftText = R.string.course_relearn_sure, rightText = R.string.cancel)
CommonDialog.newInstance(bean).apply { CommonDialog.newInstance(bean).apply {
onCommonDialogButtonClickListener = { dialog, isRightClick -> onCommonDialogButtonClickListener = { dialog, isRightClick ->
dialog.dismissAllowingStateLoss() 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() this.courseRelearn()
} }
} }
} }
}.show(supportFragmentManager,"course_relearn_dialog")
}.show(supportFragmentManager, "course_relearn_dialog")
} }
/** 自动播放次数选择: 仅单词会有该选项 */ /** 自动播放次数选择: 仅单词会有该选项 */
private fun showAutoPlaySelectDialog() { private fun showAutoPlaySelectDialog() {
if (autoPlaySeletDialog == null) { if (autoPlaySeletDialog == null) {
autoPlaySeletDialog = BottomSheetDialog(this, R.style.dialog_style).apply { 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) setContentView(autoPlayBinding.root)
autoPlayBinding.ivCancel.click { v -> dismiss() } autoPlayBinding.ivCancel.click { v -> dismiss() }
autoPlayBinding.rvRepeat.run { autoPlayBinding.rvRepeat.run {
adapter = AdapterAutoPlaySelectRepeat().apply { adapter = AdapterAutoPlaySelectRepeat().apply {
onItemClick = { _, position, _ -> onItemClick = { _, position, _ ->
} }
autoPlaySeletDialog?.show() 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() { override fun onStop() {
super.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 vm.coursePack.learnProgress = vm.coursePack.childrenCourses[0].courseLearnProgress
} }
} }


/** ViewModel Factory工厂 */ /** 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 { 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 return CoursePackMainActivityViewModel(subjectId, coursePackInPosition) as T
} }
} }
} }

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

//设置显示当前课程的进度值和显示内容 //设置显示当前课程的进度值和显示内容
val currentCourseProgress = MutableLiveData<Double>() 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

adapterLesson.notifyItemChanged(learnEventData.leesonPositionIndex) adapterLesson.notifyItemChanged(learnEventData.leesonPositionIndex)
vm.allLesson[learnEventData.leesonPositionIndex].let { vm.allLesson[learnEventData.leesonPositionIndex].let {
val key = "${it.chapterId}_${it.lessonId}" val key = "${it.chapterId}_${it.lessonId}"
vm.courseDetail.after.put(key, it.beforeTestScore)
vm.courseDetail.after.put(key, it.afterTestScore)
} }
} }
} }
} else if (!entity.learnIsOver) { //当前课时未学完,直接开始学习 } else if (!entity.learnIsOver) { //当前课时未学完,直接开始学习
startLearn(entity) startLearn(entity)
} else if (entity.afterTestScore == AppConstants.NOT_DOING) { } else if (entity.afterTestScore == AppConstants.NOT_DOING) {
loadLessonAfterTest(entity)
loadLessonAfterTest(entity,true)
} else { //当前课时学习完成的弹窗 } else { //当前课时学习完成的弹窗
showLessonAllOverDialog(entity) showLessonAllOverDialog(entity)
} }
} }
} }
/** 开始课时学后测试
* 请求数据后,直接开始跳转
/** 请求测试数据,去进行课时学后测试
* @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) { 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")
}
} }
} }
* 当前课时学前、学习、学后都完成了的弹窗 * 当前课时学前、学习、学后都完成了的弹窗
*/ */
private fun showLessonAllOverDialog(lesson : Lesson) { 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 correctNumber = lesson.correctNumber
errorNumber = lesson.errorNumber errorNumber = lesson.errorNumber
score = lesson.afterTestScore.toInt() score = lesson.afterTestScore.toInt()
courseType = vm.course.courseType courseType = vm.course.courseType
this.testData = it 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

requireArguments().getInt(AppConfig.INTENT_1)))[CourseMainFragmentViewModel::class.java].apply { requireArguments().getInt(AppConfig.INTENT_1)))[CourseMainFragmentViewModel::class.java].apply {
coursePackMainActivityVM = ViewModelProvider(requireActivity())[CoursePackMainActivityViewModel::class.java] coursePackMainActivityVM = ViewModelProvider(requireActivity())[CoursePackMainActivityViewModel::class.java]
} }
lifecycle.addObserver(vmmodel)
return vmmodel return vmmodel
} }

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



import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.google.common.base.Joiner
import com.suliang.common.base.viewmodel.BaseViewModel import com.suliang.common.base.viewmodel.BaseViewModel
import com.suliang.common.extension.diskIo2DiskIo import com.suliang.common.extension.diskIo2DiskIo
import com.suliang.common.extension.diskIo2Main import com.suliang.common.extension.diskIo2Main
import com.suliang.common.util.DateUtil import com.suliang.common.util.DateUtil
import com.suliang.common.util.file.FileUtil 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.AppConstants
import com.xkl.cdl.data.bean.LearnWord import com.xkl.cdl.data.bean.LearnWord
import com.xkl.cdl.data.bean.course.Course import com.xkl.cdl.data.bean.course.Course
import com.xkl.cdl.data.manager.db.DbControlBase import com.xkl.cdl.data.manager.db.DbControlBase
import com.xkl.cdl.data.repository.DataRepository import com.xkl.cdl.data.repository.DataRepository
import com.xkl.cdl.module.m_center_learn.CoursePackMainActivityViewModel 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.Observable
import io.reactivex.rxjava3.core.Scheduler
import io.reactivex.rxjava3.functions.BiFunction import io.reactivex.rxjava3.functions.BiFunction
import io.reactivex.rxjava3.schedulers.Schedulers
import mqComsumerV1.Struct import mqComsumerV1.Struct
import java.io.File import java.io.File
import java.util.* import java.util.*
//获取课程的章节数据 //获取课程的章节数据
fun loadMain() : MutableLiveData<Boolean> { fun loadMain() : MutableLiveData<Boolean> {
val mutableLiveData = 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 courseDetail = it
course.courseLearnProgress = it.courseLearnProgress course.courseLearnProgress = it.courseLearnProgress
coursePackMainActivityVM.currentCourseProgress.postValue(course.courseLearnProgress)
return@flatMap DataRepository.getCourseAllLesson(dbControlBase, it) return@flatMap DataRepository.getCourseAllLesson(dbControlBase, it)
}.flatMap { }.flatMap {
allLesson = it allLesson = it
return@flatMap DataRepository.getCourseCollect() return@flatMap DataRepository.getCourseCollect()
},
DataRepository.getReviewData(),
}, DataRepository.getReviewData(),
BiFunction<HashMap<String, Long>, Array<String>, Boolean> { t1 : HashMap<String, Long>?, t2 : Array<String>? -> BiFunction<HashMap<String, Long>, Array<String>, Boolean> { t1 : HashMap<String, Long>?, t2 : Array<String>? ->
// TODO: 2022/5/5 初始化需要复习的数据 // TODO: 2022/5/5 初始化需要复习的数据
showMoreIsEnable()
true true
}).compose(diskIo2Main()).subscribe { }).compose(diskIo2Main()).subscribe {
mutableLiveData.value = it mutableLiveData.value = it
} }
/** 课程包主页上的更多按钮点击是否有效 */ /** 课程包主页上的更多按钮点击是否有效 */
private fun showMoreIsEnable(){
coursePackMainActivityVM.showMoreEnable = courseDetail.st_before != AppConstants.NOT_DOING
fun showMoreIsEnable() : Boolean {
return courseDetail.st_before != AppConstants.NOT_DOING
} }
/** /**
* @param lesson Lesson 课时 * @param lesson Lesson 课时
* @return MutableLiveData<LearnData> * @return MutableLiveData<LearnData>
*/ */
fun loadLessonLearnData(lesson : Lesson): MutableLiveData<LearnData>{
fun loadLessonLearnData(lesson : Lesson) : MutableLiveData<LearnData> {
val result = MutableLiveData<LearnData>() val result = MutableLiveData<LearnData>()
Observable.create<List<LearnWord>> { Observable.create<List<LearnWord>> {
it.onNext(DBCourseManager.queryLearnData(dbControlBase, lesson)) it.onNext(DBCourseManager.queryLearnData(dbControlBase, lesson))
it.onComplete() 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 return result
} }
fun initSelectPosition() : Int { fun initSelectPosition() : Int {
//没有学习点,默认课程学习课时位置为第一个 //没有学习点,默认课程学习课时位置为第一个
var temposition = 0 var temposition = 0
when{
when {
//学后总测试完成,默认无选中 //学后总测试完成,默认无选中
courseDetail.st_after != AppConstants.NOT_DOING -> temposition = -1 courseDetail.st_after != AppConstants.NOT_DOING -> temposition = -1
//有学习点 //有学习点
* 课时重学 * 课时重学
* @param lessonPositionIndex Int 重学课时所在位置 * @param lessonPositionIndex Int 重学课时所在位置
*/ */
fun relearnLesson(lessonPositionIndex : Int):MutableLiveData<Boolean> {
fun relearnLesson(lessonPositionIndex : Int) : MutableLiveData<Boolean> {
val result = 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 { Observable.fromCallable {
// TODO: 2022/5/5 进行数据清除和保存 // TODO: 2022/5/5 进行数据清除和保存
}.compose(diskIo2DiskIo()).subscribe{
}.compose(diskIo2DiskIo()).subscribe {
//更新lesson //更新lesson
val lesson = allLesson[lessonPositionIndex].apply { val lesson = allLesson[lessonPositionIndex].apply {
errorNumber = 0 errorNumber = 0
//移除总测、课时学前测试的学习错误数据 //移除总测、课时学前测试的学习错误数据
exam_w_r_list.filterKeys { exam_w_r_list.filterKeys {
it.startsWith(wordStartKey) it.startsWith(wordStartKey)
}.forEach{
}.forEach {
exam_w_r_list.remove(it.key) 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) temporary_words.remove(it.key)
} }
//更新课程进度 //更新课程进度
course.courseLearnProgress = courseProgress course.courseLearnProgress = courseProgress
coursePackMainActivityVM.currentCourseProgress.postValue(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) result.postValue(true)
} }
/** /**
* 课程重学方法 * 课程重学方法
*/ */
fun relearnCourse(): MutableLiveData<Boolean> {
fun relearnCourse() : MutableLiveData<Boolean> {
val result = 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 { Observable.fromCallable {
// TODO: 2022/5/5 调用课程重学 重新请求详情 // TODO: 2022/5/5 调用课程重学 重新请求详情
val file = File(FileUtil.getSaveDirPath("appcache"), "${course.subjectId}_${course.coursePackId}_${course.courseId}") 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 { courseDetail.run {
courseLearnProgress = 0.0 courseLearnProgress = 0.0
st_before = AppConstants.NOT_DOING st_before = AppConstants.NOT_DOING
// TODO: 2022/5/6 这里在退出时进行了统计数据缓存保存,在loadMain的时候进行了read缓存 ,后期需要清除 // TODO: 2022/5/6 这里在退出时进行了统计数据缓存保存,在loadMain的时候进行了read缓存 ,后期需要清除
override fun onDestroy(owner : LifecycleOwner) { override fun onDestroy(owner : LifecycleOwner) {
val objectToBytes = FileUtil.objectToBytes(courseDetail) 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) 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

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

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

android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" android:layout_height="30dp"
android:background="@color/gray_1" 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 <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_historical_route" android:id="@+id/rv_historical_route"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingStart="@dimen/global_spacing" android:paddingStart="@dimen/global_spacing"
android:paddingEnd="@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" app:layout_constraintEnd_toEndOf="parent"
tools:reverseLayout="true"
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="1"
app:layout_constraintHorizontal_bias="0" 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:listitem="@layout/item_historical_route"
/>
tools:reverseLayout="true" />


<TextView <TextView
android:id="@+id/tv_banner" android:id="@+id/tv_banner"
android:text="@string/auto_playing" android:text="@string/auto_playing"
android:textColor="@color/theme_color" android:textColor="@color/theme_color"
android:textSize="@dimen/smallerSize" 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" />




<!--内容布局--> <!--内容布局-->




<!--详情--> <!--详情-->
<!-- <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> </FrameLayout>




<!--总时间--> <!--总时间-->
android:textColor="@color/gray_2" android:textColor="@color/gray_2"
android:textSize="@dimen/smallerSize" android:textSize="@dimen/smallerSize"
app:layout_constraintBottom_toBottomOf="parent" 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" android:id="@+id/tv_play"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="22dp" 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:insetTop="0dp"
android:insetBottom="0dp" android:insetBottom="0dp"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textColor="@color/theme_color" 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:textSize="@dimen/smallerSize"
android:visibility="gone"
app:icon="@drawable/ic_play_pause" app:icon="@drawable/ic_play_pause"
app:iconGravity="textStart"
app:iconSize="16dp" app:iconSize="16dp"
app:iconTint="@color/theme_color" 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" android:id="@+id/tv_play_stop"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="22dp" 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:insetTop="0dp"
android:insetBottom="0dp" android:insetBottom="0dp"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textColor="@color/theme_color" 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:textSize="@dimen/smallerSize"
android:visibility="gone"
app:icon="@drawable/ic_play_stop" app:icon="@drawable/ic_play_stop"
app:iconSize="10dp" app:iconSize="10dp"
app:iconTint="@color/theme_color" app:iconTint="@color/theme_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:rippleColor="@color/white" app:rippleColor="@color/white"
/>
app:strokeColor="@color/theme_color"
app:strokeWidth="@dimen/line_height"
tools:text="停止播放"
tools:visibility="visible" />




</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

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

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



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

<string name="phrase">词组</string> <string name="phrase">词组</string>
<string name="example">例句</string> <string name="example">例句</string>
<string name="reference">参考</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_title">你确定要退出本课程的学习吗?</string>
<string name="quit_learn_content">退出后系统将保存你的学习进度</string> <string name="quit_learn_content">退出后系统将保存你的学习进度</string>
<string name="course_relearn_title">你确定要清空本课程的学习记录吗?</string> <string name="course_relearn_title">你确定要清空本课程的学习记录吗?</string>
<string name="course_relearn_content">清空后你的学习记录将不可恢复,请谨慎操作</string> <string name="course_relearn_content">清空后你的学习记录将不可恢复,请谨慎操作</string>
<string name="course_relearn_sure">确认清空</string> <string name="course_relearn_sure">确认清空</string>
<string name="quit_auto_play_title">你确定要退出自动播放吗?</string>
<string name="quit_auto_play_title_over">本课程自动播放完毕</string>


</resources> </resources>

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

vm.pageEvent.observe(this) { startActivity(it) } vm.pageEvent.observe(this) { startActivity(it) }
vm.toastEvent.observe(this){ showToast(it) } vm.toastEvent.observe(this){ showToast(it) }
vm.loadingEvent.observe(this) { vm.loadingEvent.observe(this) {
LogUtil.e("监听到 activity vm 的 loadingEvent isShow $it")
// LogUtil.e("监听到 activity vm 的 loadingEvent isShow $it")
showHideLoading(it)} showHideLoading(it)}


} }

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

import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream


* author suliang * author suliang
* create 2022/3/25 17:08 * create 2022/3/25 17:08
* Describe: * Describe:
* Android 中 Bitmap 和 Drawable 相互转换的方法 https://blog.csdn.net/l_lhc/article/details/50923372
*/ */
class DrawableUti { class DrawableUti {
companion object { companion object {
return BitmapFactory.decodeByteArray(byteArray,0,byteArray.size) 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

val temp = currentState val temp = currentState
synchronized(temp) { synchronized(temp) {
listener?.let { listener?.let {
LogUtil.e("MPUtil发送 --》 ${Thread.currentThread()}")
// LogUtil.e("MPUtil发送 --》 ${Thread.currentThread()}")
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
LogUtil.e("MPUtil发送 --》 ${temp.name}")
// LogUtil.e("MPUtil发送 --》 ${temp.name}")
it.onMpState(temp) it.onMpState(temp)
} }
} }

Loading…
Cancel
Save