<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) | ||||
} | } | ||||
} | } |