| <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" /> |
| 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) |
| 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 //加粗 |
| 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") |
| 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 改变界面 到目录页 */ |
| * 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 | |||||
| } | } |
| 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 | |||||
| } | |||||
| } | } |
| 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 | |||||
| } | } | ||||
| } | } | ||||
| } | } |
| } | } | ||||
| }?: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)) |
| 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() } | |||||
| } | |||||
| } | } | ||||
| 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") | |||||
| }) | |||||
| } | } | ||||
| } | } |
| 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 | ||||
| } | } |
| /** 停止获取下一题 */ | /** 停止获取下一题 */ | ||||
| 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 |
| 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 |
| 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 | |||||
| } | |||||
| } | } |
| 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 | ||||
| } | } | ||||
| } | } | ||||
| } | } |
| //设置显示当前课程的进度值和显示内容 | //设置显示当前课程的进度值和显示内容 | ||||
| val currentCourseProgress = MutableLiveData<Double>() | val currentCourseProgress = MutableLiveData<Double>() | ||||
| //更多点击是否有效 | |||||
| var showMoreEnable = false | |||||
| } | } |
| 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) | |||||
| } | } | ||||
| } | } | ||||
| } | } |
| 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 | ||||
| } | } | ||||
| 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 | |||||
| } | |||||
| } | } |
| 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 | ||||
| } | } | ||||
| } | } |
| 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> |
| 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" /> | ||||
| <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> |
| 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)} | ||||
| } | } |
| 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) | |||||
| } | |||||
| } | } | ||||
| } | } |
| 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) | ||||
| } | } | ||||
| } | } |