Browse Source

learn

master
suliang 2 years ago
parent
commit
46d0154889
40 changed files with 956 additions and 392 deletions
  1. 1
    0
      .idea/misc.xml
  2. 3
    1
      app/src/main/java/com/xkl/cdl/Extension.kt
  3. 15
    10
      app/src/main/java/com/xkl/cdl/adapter/AdapterLesson.kt
  4. 20
    3
      app/src/main/java/com/xkl/cdl/data/AppConstants.kt
  5. 5
    11
      app/src/main/java/com/xkl/cdl/data/bean/LearnDialogBean.kt
  6. 2
    0
      app/src/main/java/com/xkl/cdl/data/bean/course/Course.kt
  7. 6
    3
      app/src/main/java/com/xkl/cdl/data/bean/course/CourseDetail.kt
  8. 2
    2
      app/src/main/java/com/xkl/cdl/data/bean/course/Lesson.kt
  9. 32
    1
      app/src/main/java/com/xkl/cdl/data/manager/CourseManager.kt
  10. 2
    2
      app/src/main/java/com/xkl/cdl/data/manager/db/DBCourseManager.kt
  11. 6
    6
      app/src/main/java/com/xkl/cdl/data/repository/AudioCache.kt
  12. 11
    2
      app/src/main/java/com/xkl/cdl/data/repository/DataRepository.kt
  13. 7
    7
      app/src/main/java/com/xkl/cdl/data/repository/PhotoCache.kt
  14. 0
    17
      app/src/main/java/com/xkl/cdl/dialog/DialogEventAction.kt
  15. 107
    11
      app/src/main/java/com/xkl/cdl/dialog/LearnDialog.kt
  16. 43
    18
      app/src/main/java/com/xkl/cdl/module/learn/LearnExamActivity.kt
  17. 22
    17
      app/src/main/java/com/xkl/cdl/module/learn/LearnExamViewModel.kt
  18. 12
    10
      app/src/main/java/com/xkl/cdl/module/learn/LearnWordActivity.kt
  19. 9
    9
      app/src/main/java/com/xkl/cdl/module/learn/LearnWordViewModel.kt
  20. 39
    8
      app/src/main/java/com/xkl/cdl/module/m_center_learn/CoursePackMainActivity.kt
  21. 5
    3
      app/src/main/java/com/xkl/cdl/module/m_center_learn/CoursePackMainActivityViewModel.kt
  22. 169
    43
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseLessonFragment.kt
  23. 57
    31
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseMainFragment.kt
  24. 136
    3
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseMainFragmentViewModel.kt
  25. 25
    37
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseTotalTestFragment.kt
  26. 0
    3
      app/src/main/java/com/xkl/cdl/module/main/MainActivity.kt
  27. 4
    1
      app/src/main/java/com/xkl/cdl/util/LearnRuleUtil.kt
  28. 18
    12
      app/src/main/res/layout/dialog_lesson_learn.xml
  29. 1
    1
      app/src/main/res/layout/inc_over_number.xml
  30. 3
    0
      app/src/main/res/values/strings.xml
  31. 2
    0
      build.gradle
  32. 2
    0
      lib/common/build.gradle
  33. 44
    44
      lib/common/src/main/java/com/suliang/common/base/activity/LifecycleLogActivity.kt
  34. 1
    0
      lib/common/src/main/java/com/suliang/common/base/fragment/BaseFragmentVM.kt
  35. 7
    7
      lib/common/src/main/java/com/suliang/common/base/viewmodel/BaseViewModel.kt
  36. 87
    47
      lib/common/src/main/java/com/suliang/common/eventbus/LiveDataBus.kt
  37. 32
    17
      lib/common/src/main/java/com/suliang/common/eventbus/NonStickyMutableLivedata.kt
  38. 1
    1
      lib/common/src/main/java/com/suliang/common/extension/ViewClickExtension.kt
  39. 17
    2
      lib/common/src/main/java/com/suliang/common/util/file/FileUtil.kt
  40. 1
    2
      lib/common/src/main/res/values/strings.xml

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

<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_total_test.xml" value="0.33" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_total_test.xml" value="0.33" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_first.xml" value="0.4979166666666667" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_first.xml" value="0.4979166666666667" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_learn_center.xml" value="0.25" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_learn_center.xml" value="0.25" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_memo.xml" value="0.20104166666666667" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_my.xml" value="0.28229166666666666" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_my.xml" value="0.28229166666666666" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_control_button.xml" value="0.46467391304347827" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_control_button.xml" value="0.46467391304347827" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_exam_spell_content.xml" value="0.45153985507246375" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_exam_spell_content.xml" value="0.45153985507246375" />

+ 3
- 1
app/src/main/java/com/xkl/cdl/Extension.kt View File

package com.xkl.cdl package com.xkl.cdl


import android.annotation.SuppressLint
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
* @param example String? 例句 * @param example String? 例句
* @param refrence String? 参考 * @param refrence String? 参考
*/ */
@SuppressLint("SetTextI18n")
fun IncWordDetailBinding.initValue(phrase : String?, example : String?, refrence : String?){ fun IncWordDetailBinding.initValue(phrase : String?, example : String?, refrence : String?){
phrase?.let { phrase?.let {
tvPhraseFlag.visibility = View.VISIBLE tvPhraseFlag.visibility = View.VISIBLE
example?.let { example?.let {
tvExampleFlag.visibility = View.VISIBLE tvExampleFlag.visibility = View.VISIBLE
tvExample.visibility = View.VISIBLE tvExample.visibility = View.VISIBLE
tvExample.text = example
tvExample.text = "▪ ${it.replace("\n", "\n▪ ")}"
}?:let { }?:let {
tvExampleFlag.visibility = View.GONE tvExampleFlag.visibility = View.GONE
tvExample.visibility = View.GONE tvExample.visibility = View.GONE

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

} }
} }
} }
private val normalColorNotLearnOver by lazy {
ContextCompat.getColor(context, R.color.gray_2) //学习未完成未选中时的颜色
}
private val normalColorLearnOver by lazy { ContextCompat.getColor(context, R.color.gray_1) } //学习已完成未选中时的颜色
private val mainTextColor by lazy {ContextCompat.getColor(context, R.color.main_text_color) } //选中颜色
private val translationColor by lazy {ContextCompat.getColor(context, R.color.translation) } //透明颜色
private val themeColor by lazy { ContextCompat.getColor(context,R.color.theme_color) } //主题颜色


/** /**
* 处理颜色 选中色 未选中色(学习完成/学习未完成) * 处理颜色 选中色 未选中色(学习完成/学习未完成)
* @param lesson Lesson * @param lesson Lesson
*/ */
private fun ItemCourseLessonBinding.initColor(position: Int, lesson: Lesson) { private fun ItemCourseLessonBinding.initColor(position: Int, lesson: Lesson) {
val normalColorNotLearnOver = ContextCompat.getColor(context, R.color.gray_2) //学习未完成未选中时的颜色
val normalColorLearnOver = ContextCompat.getColor(context, R.color.gray_1) //学习已完成未选中时的颜色
val mainTextColor = ContextCompat.getColor(context, R.color.main_text_color) //选中颜色
val translationColor = ContextCompat.getColor(context, R.color.translation) //透明颜色
//item的背景和状态设置 //item的背景和状态设置
if (selectPos == position) { //选中,背景设置 if (selectPos == position) { //选中,背景设置
layoutContent.setBackgroundColor(ContextCompat.getColor(context, R.color.gray_3)) //选中背景 layoutContent.setBackgroundColor(ContextCompat.getColor(context, R.color.gray_3)) //选中背景
} }
} else { //未选中:根据学习情况判断 } else { //未选中:根据学习情况判断
layoutContent.setBackgroundColor(translationColor) //未选中背景透明 layoutContent.setBackgroundColor(translationColor) //未选中背景透明
if (lesson.learnIsOver) {
if (lesson.learnIsOver) { //学习完成
tvLessonName.run { tvLessonName.run {
setTextColor(normalColorLearnOver) //课时名称颜色 setTextColor(normalColorLearnOver) //课时名称颜色
paint.isFakeBoldText = false //加粗 paint.isFakeBoldText = false //加粗
tvLessonNumber.setTextColor(normalColorLearnOver) //数量颜色 tvLessonNumber.setTextColor(normalColorLearnOver) //数量颜色
ivArrowRight.run { ivArrowRight.run {
setBackgroundColor(translationColor) setBackgroundColor(translationColor)
imageTintList = ColorStateList.valueOf(ContextCompat.getColor(context, R.color.theme_color))
strokeColor = ColorStateList.valueOf(ContextCompat.getColor(context, R.color.theme_color))
imageTintList = ColorStateList.valueOf(normalColorLearnOver)
strokeColor = ColorStateList.valueOf(normalColorLearnOver)
} }
} else {
} else { //未完成
tvLessonName.run { tvLessonName.run {
setTextColor(normalColorNotLearnOver) //课时名称颜色 setTextColor(normalColorNotLearnOver) //课时名称颜色
paint.isFakeBoldText = false //加粗 paint.isFakeBoldText = false //加粗
tvLessonNumber.setTextColor(normalColorNotLearnOver) //数量颜色 tvLessonNumber.setTextColor(normalColorNotLearnOver) //数量颜色
ivArrowRight.run { ivArrowRight.run {
setBackgroundColor(translationColor) setBackgroundColor(translationColor)
imageTintList = ColorStateList.valueOf(normalColorNotLearnOver)
strokeColor = ColorStateList.valueOf(normalColorNotLearnOver)
imageTintList = ColorStateList.valueOf(themeColor)
strokeColor = ColorStateList.valueOf(themeColor)
} }
} }
} }

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

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_LEARN_OVER = 3
/**对话框类型,item学习完成*/
const val DIALOG_TYPE_LESSON_ITEM_OVER = 4
/**--- 总线动作 --------------------------------- */ /**--- 总线动作 --------------------------------- */
/**action key 改变界面 到目录页 */ /**action key 改变界面 到目录页 */
const val EVENT_COURSE = "action_change_page"
const val EVENT_CHANGE_PAGE = "action_change_page"
/** lesson学习 数据传递 */
const val EVENT_LESSON_DATA = "lesson_learn_data"
/** lesson学习中动作事件 : 课时学习中的动作*/
const val EVENT_LESSON_ACTION = "lesson_learn_action"
/**总测数据与动作*/
const val EVENT_TOTAL_TEST = "total_test"
/** 事件动作:学前总测结束弹窗之 开始学习 */ /** 事件动作:学前总测结束弹窗之 开始学习 */
const val ACTION_COURSE_TEST_START_LEARN = 1 const val ACTION_COURSE_TEST_START_LEARN = 1
const val ACTION_LESSON_AFTER_TEST_NEXT = 8 const val ACTION_LESSON_AFTER_TEST_NEXT = 8
/**数据动作: 课时学习完成*/ /**数据动作: 课时学习完成*/
const val DATA_LESSON_LEARN_OVER = 9 const val DATA_LESSON_LEARN_OVER = 9
/**学后总测试完成: 再测一次*/
const val ACTION_COURSE_TEST_AFTER_TOTAL_AGAIN = 10
/** 学后总测试完成,切换到目录页显示 */
const val ACTION_COURSE_TEST_AFTER_TOTAL_OVER = 11
/**学后总测结束,传递数据*/
const val DATA_COURSE_AFTER_TEST_OVER = 12
/**--- 弹窗动作 --------------------------------- */ /**--- 弹窗动作 --------------------------------- */
/** 学前总测结束弹窗: 开始学习 ,课时学前测试开始弹窗*/ /** 学前总测结束弹窗: 开始学习 ,课时学前测试开始弹窗*/
const val DIALOG_LESSON_AFTER_TEST_AGAIN = 4 const val DIALOG_LESSON_AFTER_TEST_AGAIN = 4
/**课时学后测试弹窗动作: 下一步*/ /**课时学后测试弹窗动作: 下一步*/
const val DIALOG_LESSON_AFTER_TEST_NEXT = 5 const val DIALOG_LESSON_AFTER_TEST_NEXT = 5
/**弹窗动作提示: 完成*/
const val DIALOG_OVER = 6
/**课时学后总测的再测一次*/
const val DIALOG_AFTER_TOTAL_TEST_AGAIN = 7
} }

+ 5
- 11
app/src/main/java/com/xkl/cdl/data/bean/LearnDialogBean.kt View File

* 词汇量测试 * 词汇量测试
* *
*/ */
class LearnDialogBean(val dialogType:Int) : Parcelable {
class LearnDialogBean(val dialogType:Int) :Parcelable {
/*---------------测试使用------------------------------------*/ /*---------------测试使用------------------------------------*/
/** 测试类型 */ /** 测试类型 */
/** 题数与时间 */ /** 题数与时间 */
var showTimeCount = "" var showTimeCount = ""
constructor(parcel : Parcel) : this(parcel.readInt()) { constructor(parcel : Parcel) : this(parcel.readInt()) {
examType = parcel.readInt() examType = parcel.readInt()
score = parcel.readInt() score = parcel.readInt()
correctNumber = parcel.readInt() correctNumber = parcel.readInt()
errorNumber = parcel.readInt() errorNumber = parcel.readInt()
chapter_lesson_name = parcel.readString().toString()
showTimeCount = parcel.readString().toString()
} }
override fun writeToParcel(parcel : Parcel, flags : Int) { override fun writeToParcel(parcel : Parcel, flags : Int) {
parcel.writeInt(score) parcel.writeInt(score)
parcel.writeInt(correctNumber) parcel.writeInt(correctNumber)
parcel.writeInt(errorNumber) parcel.writeInt(errorNumber)
parcel.writeString(chapter_lesson_name)
parcel.writeString(showTimeCount)
} }
override fun describeContents() : Int { override fun describeContents() : Int {
return arrayOfNulls(size) return arrayOfNulls(size)
} }
} }
} }

+ 2
- 0
app/src/main/java/com/xkl/cdl/data/bean/course/Course.kt View File

package com.xkl.cdl.data.bean.course package com.xkl.cdl.data.bean.course


import androidx.databinding.BaseObservable

/** /**
* author suliang * author suliang
* create 2022/3/22 10:08 * create 2022/3/22 10:08

+ 6
- 3
app/src/main/java/com/xkl/cdl/data/bean/course/CourseDetail.kt View File

package com.xkl.cdl.data.bean.course package com.xkl.cdl.data.bean.course


import com.xkl.cdl.data.AppConstants
import java.io.Serializable



/** /**
* author suliang * author suliang
* create 2022/3/29 9:52 * create 2022/3/29 9:52
* Describe: 课程学习的详情 * Describe: 课程学习的详情
*/ */
class CourseDetail {
class CourseDetail:Serializable{
var courseLearnProgress: Double = 0.0 //课程学习进度 var courseLearnProgress: Double = 0.0 //课程学习进度
var st_before: Double = -1.0 //学前总成绩 : -1代表没有进行学前总测试
var st_after: Double = -1.0 //学后总成绩 : -1代表没有进行学后总测试
var st_before: Double = AppConstants.NOT_DOING //学前总成绩 : -1代表没有进行学前总测试
var st_after: Double = AppConstants.NOT_DOING //学后总成绩 : -1代表没有进行学后总测试
var before: HashMap<String, Double> = hashMapOf() //章节学前测试成绩,key=>{chapter_id}_{lesson_id} value=>成绩 var before: HashMap<String, Double> = hashMapOf() //章节学前测试成绩,key=>{chapter_id}_{lesson_id} value=>成绩
var after: HashMap<String, Double> = hashMapOf() //章节学后测试成绩,key=>{chapter_id}_{lesson_id} value=>成绩 var after: HashMap<String, Double> = hashMapOf() //章节学后测试成绩,key=>{chapter_id}_{lesson_id} value=>成绩
var right = hashMapOf<String, Int>() //正确条目数,key=>{chapter_id}_{lesson_id} value=>条目数量 var right = hashMapOf<String, Int>() //正确条目数,key=>{chapter_id}_{lesson_id} value=>条目数量

+ 2
- 2
app/src/main/java/com/xkl/cdl/data/bean/course/Lesson.kt View File

/** 学习进度位置,为已学下标的位置,在作文课堂练习时,学习取当前条,其他取下一条*/ /** 学习进度位置,为已学下标的位置,在作文课堂练习时,学习取当前条,其他取下一条*/
var learnedIndex: Int = -1 var learnedIndex: Int = -1
/**课时学前测试成绩 */ /**课时学前测试成绩 */
var beforeTestScore = -1.0
var beforeTestScore = AppConstants.NOT_DOING
/** 课时学后测试 */ /** 课时学后测试 */
var afterTestScore = -1.0
var afterTestScore = AppConstants.NOT_DOING
/**是否学习完成: 为学习内容是否完成,不包含学前,学后测试*/ /**是否学习完成: 为学习内容是否完成,不包含学前,学后测试*/
var learnIsOver = false var learnIsOver = false
/**是否是最后一个课时 */ /**是否是最后一个课时 */

+ 32
- 1
app/src/main/java/com/xkl/cdl/data/manager/CourseManager.kt View File

} }
/** /**
* 计算项目总进度
* 计算项目总进度 ,并将课程进度值设置进入集合中
* @param subjectId Int 项目Id * @param subjectId Int 项目Id
* @param packId Long 修改进度的课程包id * @param packId Long 修改进度的课程包id
* @param courseId Long 修改进度的课程id * @param courseId Long 修改进度的课程id
if (totalCourseSize == 0) return 0.0 if (totalCourseSize == 0) return 0.0
return totalProgress / totalCourseSize return totalProgress / totalCourseSize
} }
/**
* 重学课程的课时时,计算项目的进度,不会进行赋值
* @param subjectId Int 项目
* @param courseId Long 重学的课程
* @param courseProgress Double 被重学课时的课程进度
* @return Double 项目的总进度
*/
fun calculateSubjectProgressWithCourseLessonRelearn(subjectId : Int,coursePackId:Long, courseId : Long,courseProgress:Double) : Double {
val totalProgress = mSortInfoList.get(subjectId)?.let {
var tempProgress = 0.0
it.forEach {
if (it.packId == coursePackId && it.courseId == courseId){
tempProgress += courseProgress
return@forEach
}else {
tempProgress += it.s
}
}
tempProgress
}?: 0.0
if (totalProgress == 0.0 ) return totalProgress
var totalCourseSize = 0
subjectWithCoursePackMap.get(subjectId)?.forEach {
totalCourseSize += it.childrenCourses.size
}
if (totalCourseSize == 0) return 0.0
return totalProgress / totalCourseSize
}
} }

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

totalNumber = this.wordIds.size //总数 totalNumber = this.wordIds.size //总数
correctNumber = detail.right.getOrElse(key, { 0 }) //正确数 correctNumber = detail.right.getOrElse(key, { 0 }) //正确数
errorNumber = detail.wrong.getOrElse(key, { 0 }) //错误数 errorNumber = detail.wrong.getOrElse(key, { 0 }) //错误数
beforeTestScore = detail.before.getOrElse(key, { -1.0 }) //课时学前测试成绩
afterTestScore = detail.after.getOrElse(key, { -1.0 }) //课时学后测试成绩
beforeTestScore = detail.before.getOrElse(key, { AppConstants.NOT_DOING }) //课时学前测试成绩
afterTestScore = detail.after.getOrElse(key, { AppConstants.NOT_DOING }) //课时学后测试成绩
this.learnedIndex = learnIndex //学习位置,当前位置为已学 this.learnedIndex = learnIndex //学习位置,当前位置为已学
this.learnIsOver = learnIsOver this.learnIsOver = learnIsOver
this.lessonType = lessonType this.lessonType = lessonType

+ 6
- 6
app/src/main/java/com/xkl/cdl/data/repository/AudioCache.kt View File

import android.util.LruCache import android.util.LruCache
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.suliang.common.AppConfig import com.suliang.common.AppConfig
import com.suliang.common.eventbus.NonStickyMutableLiveData
import com.suliang.common.extension.diskIo2Main import com.suliang.common.extension.diskIo2Main
import com.suliang.common.util.file.FileUtil import com.suliang.common.util.file.FileUtil
import com.xkl.cdl.data.AppConstants import com.xkl.cdl.data.AppConstants
} }
/** 通知查到的录音 */ /** 通知查到的录音 */
var audioLiveData = NonStickyMutableLiveData<String?>()
private lateinit var audioLiveData : MutableLiveData<String?>
// fun initAudioLiveData() : MutableLiveData<String?>{
// audioLiveData = NonStickyMutableLiveData<String?>()
// return audioLiveData
// }
fun initAudioLiveData() : MutableLiveData<String?> {
audioLiveData = MutableLiveData<String?>()
return audioLiveData
}
/** /**
* 获取对应音频 * 获取对应音频
return@fromCallable lruCache.get(defaultKey) ?: "" return@fromCallable lruCache.get(defaultKey) ?: ""
}.compose(diskIo2Main()).subscribe { }.compose(diskIo2Main()).subscribe {
if (this::audioLiveData.isInitialized)
audioLiveData.value = it audioLiveData.value = it
} }
} }

+ 11
- 2
app/src/main/java/com/xkl/cdl/data/repository/DataRepository.kt View File

package com.xkl.cdl.data.repository package com.xkl.cdl.data.repository


import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.suliang.common.util.file.FileUtil
import com.xkl.cdl.data.bean.course.CourseDetail import com.xkl.cdl.data.bean.course.CourseDetail
import com.xkl.cdl.data.bean.course.Lesson import com.xkl.cdl.data.bean.course.Lesson
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
import mqComsumerV1.Struct import mqComsumerV1.Struct
import java.io.File


/** /**
* author suliang * author suliang
} }


/** 获取课程的详情 */ /** 获取课程的详情 */
fun getCourseStatistics(): Observable<CourseDetail>{
fun getCourseStatistics(subjectId:Int,coursePackId:Long,courseId:Long): Observable<CourseDetail>{
return Observable.create{ return Observable.create{
val courseDetail = CourseDetail()
// TODO: 2022/5/6 这里自己使用的缓存
val file = File(FileUtil.getSaveDirPath("appcache"), "${subjectId}_${coursePackId}_${courseId}")
val courseDetail = if (file.exists()){
FileUtil.bytesToObject(FileUtil.readFile(file)) as CourseDetail
}else{
CourseDetail()
}
it.onNext(courseDetail) it.onNext(courseDetail)
it.onComplete() it.onComplete()
} }

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



import android.util.LruCache import android.util.LruCache
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.suliang.common.AppConfig
import com.suliang.common.eventbus.NonStickyMutableLiveData
import com.suliang.common.extension.diskIo2Main import com.suliang.common.extension.diskIo2Main
import com.suliang.common.util.file.FileUtil
import com.xkl.cdl.data.AppConstants
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
import java.io.File
import java.util.* import java.util.*


/** /**
} }
} }
/** 通知查到的录音 */
var photoLiveData = NonStickyMutableLiveData<ByteArray?>()
/** 通知查到的图片字节码 */
private lateinit var photoLiveData : MutableLiveData<ByteArray?>
fun initPhotoLiveData() : MutableLiveData<ByteArray?> {
photoLiveData = MutableLiveData<ByteArray?>()
return photoLiveData
}
/** /**
* 获取对应图片 * 获取对应图片
* 1缓存 2文件 3数据库 * 1缓存 2文件 3数据库
return@fromCallable lruCache.get(defaultKey) return@fromCallable lruCache.get(defaultKey)
}.compose(diskIo2Main()).subscribe { }.compose(diskIo2Main()).subscribe {
if (this::photoLiveData.isInitialized)
photoLiveData.value = it photoLiveData.value = it
} }
} }

+ 0
- 17
app/src/main/java/com/xkl/cdl/dialog/DialogEventAction.kt View File

package com.xkl.cdl.dialog

/**
* author suliang
* create 2022/4/6 9:54
* Describe: LearnDialog 按钮点击动作
*/
enum class DialogEventAction {
/**下一步*/
NEXT,
/** 开始学习 */
START_LEARN,
/** 课时学前测试 */
START_LESSON_BEFORE_TEST

// TODO: 2022/4/6 可以添加相应的动作action,来处理对应的动作
}

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

AppConstants.DIALOG_TYPE_EXAM_START -> when(learnDialogBean.examType){ AppConstants.DIALOG_TYPE_EXAM_START -> when(learnDialogBean.examType){
//课时学前测试开始弹窗 //课时学前测试开始弹窗
AppConstants.TEST_TYPE_BEFORE -> initLessonBeforeTestStart() AppConstants.TEST_TYPE_BEFORE -> initLessonBeforeTestStart()
//课时最后一个课时测试结束的下一步弹出此弹窗:学后总测试开始的弹窗
AppConstants.TEST_TYPE_AFTER_TOTAL -> initCourseAfterTestStart()
} }
//测试结束弹窗 //测试结束弹窗
AppConstants.DIALOG_TYPE_EXAM_OVER -> when (learnDialogBean.examType) { AppConstants.DIALOG_TYPE_EXAM_OVER -> when (learnDialogBean.examType) {
AppConstants.TEST_TYPE_BEFORE_TOTAL -> initCourseBeforeTotalTestOver() AppConstants.TEST_TYPE_BEFORE_TOTAL -> initCourseBeforeTotalTestOver()
//课时学前测试结束弹窗 //课时学前测试结束弹窗
AppConstants.TEST_TYPE_BEFORE-> initLessonBeforeTestOver() AppConstants.TEST_TYPE_BEFORE-> initLessonBeforeTestOver()
//课时后测试结束
AppConstants.TEST_TYPE_AFTER -> initLessonAfterTestOver()
//学后总测试结束弹窗
AppConstants.TEST_TYPE_AFTER_TOTAL -> initCourseAfterTestOver()
} }
//学习结束弹窗 //学习结束弹窗
AppConstants.DIALOG_TYPE_LEARN_OVER -> initLessonLearningOver() AppConstants.DIALOG_TYPE_LEARN_OVER -> initLessonLearningOver()
/**课时列表点击课时完全完成*/
AppConstants.DIALOG_TYPE_LESSON_ITEM_OVER -> initLessonItemClickLessonOver()
} }
} }
*/ */
private fun initLessonBeforeTestStart() { private fun initLessonBeforeTestStart() {
binding.run { binding.run {
ivClose.visibility = View.VISIBLE
tvTitle.visibility = View.VISIBLE tvTitle.visibility = View.VISIBLE
tvLessonName.visibility = View.VISIBLE tvLessonName.visibility = View.VISIBLE
tvCountTime.visibility = View.VISIBLE tvCountTime.visibility = View.VISIBLE
tvLessonName.text = learnDialogBean.chapter_lesson_name tvLessonName.text = learnDialogBean.chapter_lesson_name
tvCountTime.text = learnDialogBean.showTimeCount tvCountTime.text = learnDialogBean.showTimeCount
tvRight.text = "开始测试" tvRight.text = "开始测试"
imgIv.setImageResource(R.mipmap.girl_1)
imgIv.setImageResource(if (Random.nextBoolean()) R.mipmap.boy_1 else R.mipmap.girl_1)
//开始测试,进入课时学前测试界面 //开始测试,进入课时学前测试界面
binding.tvRight.click { binding.tvRight.click {
onDialogListener(AppConstants.DIALOG_START_TEST, this@LearnDialog) onDialogListener(AppConstants.DIALOG_START_TEST, this@LearnDialog)
} }
// tvLeft.visibility = View.VISIBLE
// vSplit.visibility = View.VISIBLE
// tvLeft.text = "跳过测试"
//跳过测试,直接进入学习界面
// binding.tvLeft.click {
// onDialogListener(AppConstants.DIALOG_START_LEARN, this@LearnDialog)
// }
binding.ivClose.click { dismissAllowingStateLoss() }
} }
} }
*/ */
private fun initLessonBeforeTestOver() { private fun initLessonBeforeTestOver() {
binding.run { binding.run {
imgIv.setImageResource(if (Random.nextBoolean()) R.mipmap.boy_2 else R.mipmap.girl_2)
tvScore.visibility = View.VISIBLE tvScore.visibility = View.VISIBLE
tvTip.visibility = View.VISIBLE tvTip.visibility = View.VISIBLE
tvTitle.visibility = View.VISIBLE tvTitle.visibility = View.VISIBLE
binding.run { binding.run {
imgIv.setImageResource(if (Random.nextBoolean()) R.mipmap.boy_2 else R.mipmap.girl_2) imgIv.setImageResource(if (Random.nextBoolean()) R.mipmap.boy_2 else R.mipmap.girl_2)
tvTitle.visibility = View.VISIBLE tvTitle.visibility = View.VISIBLE
tvTitle.text = "恭喜你,本课时学习完成"
tvTitle.text = "恭喜你,本课时学习完成!"
incStatisticsNumber.root.visibility = View.VISIBLE incStatisticsNumber.root.visibility = View.VISIBLE
tvLearnOverTip.visibility = View.VISIBLE tvLearnOverTip.visibility = View.VISIBLE
tvLearnOverForAfterCountTime.visibility = View.VISIBLE tvLearnOverForAfterCountTime.visibility = View.VISIBLE
tvLeft.visibility = View.VISIBLE tvLeft.visibility = View.VISIBLE
vSplit.visibility = View.VISIBLE vSplit.visibility = View.VISIBLE
tvTitle.text = "恭喜你,完成了课时学后测试" tvTitle.text = "恭喜你,完成了课时学后测试"
tvTop1.text = "重新学习"
tvTop.text = "重新学习"
tvLeft.text = "再测一次" tvLeft.text = "再测一次"
tvRight.text = "下一课时" tvRight.text = "下一课时"
onDialogListener(AppConstants.DIALOG_LESSON_AFTER_TEST_NEXT,this) onDialogListener(AppConstants.DIALOG_LESSON_AFTER_TEST_NEXT,this)
} }
} }
/**
* 课程学后总测试开始前提示弹窗
*/
private fun initCourseAfterTestStart() {
binding.run {
imgIv.setImageResource(if (Random.nextBoolean()) R.mipmap.boy_2 else R.mipmap.girl_2)
tvLessonName.visibility = View.INVISIBLE //占布局,避免布局变形
tvTitle.visibility = View.VISIBLE
tvTitle.text = "恭喜你,本课程全部学习完成!"
tvTip1.visibility = View.VISIBLE
tvTip1.text = "我的学习效果是?快去学后总测试吧!"
tvCountTime.visibility = View.VISIBLE
tvCountTime.text = learnDialogBean.showTimeCount
tvRight.text = "开始测试"
//开始测试,进入课时学前测试界面
binding.tvRight.click {
onDialogListener(AppConstants.DIALOG_START_TEST, this@LearnDialog)
}
tvLeft.visibility = View.VISIBLE
vSplit.visibility = View.VISIBLE
tvLeft.text = "完成"
binding.tvLeft.click {
onDialogListener(AppConstants.DIALOG_OVER, this@LearnDialog)
}
}
}
/**
* 课程学后总测试结束弹窗
*/
private fun initCourseAfterTestOver(){
initNumber()
initScore()
binding.run {
tvScore.visibility = View.VISIBLE
tvTip.visibility = View.VISIBLE
tvTitle.visibility = View.VISIBLE
tvTip1.visibility = View.VISIBLE
incStatisticsNumber.root.visibility = View.VISIBLE
tvTop.visibility = View.VISIBLE
tvLeft.visibility = View.VISIBLE
vSplit.visibility = View.VISIBLE
tvTitle.text = "恭喜你,完成了学后总测试"
tvTop.text = "继续学习"
tvLeft.text = "再测一次"
tvRight.text = "完成"
}
binding.tvTop.click {
onDialogListener(AppConstants.DIALOG_OVER, this)
}
binding.tvLeft.click {
onDialogListener(AppConstants.DIALOG_AFTER_TOTAL_TEST_AGAIN,this)
}
binding.tvRight.click {
onDialogListener(AppConstants.DIALOG_OVER, this)
}
}
/**
* 课时列表item点击时课程已完全学习完成(学前测、学后测、学习都已完成)的提示弹窗
* tv_title,inc_statistics_number,tv_learn_over_tip,tv_learn_over_for_after_count_time,tv_left,vSplit
*/
private fun initLessonItemClickLessonOver(){
initNumber()
binding.run {
imgIv.setImageResource(if (Random.nextBoolean()) R.mipmap.boy_2 else R.mipmap.girl_2)
ivClose.visibility = View.VISIBLE
tvTitle.visibility = View.VISIBLE
tvTitle.text = "恭喜你,本课时学习完成!"
incStatisticsNumber.root.visibility = View.VISIBLE
tvLearnOverTip.visibility = View.VISIBLE
tvLearnOverTip.text = "你可重新学习本课时,或重新进行课时学后测试"
tvLearnOverForAfterCountTime.visibility = View.VISIBLE
tvLearnOverForAfterCountTime.text = "(上次课时学后测试成绩:${learnDialogBean.score})"
tvLeft.visibility = View.VISIBLE
tvLeft.text = "重新学习"
vSplit.visibility = View.VISIBLE
tvRight.text = "课时学后测试"
}
binding.ivClose.click { dismissAllowingStateLoss() }
binding.tvLeft.click { onDialogListener(AppConstants.DIALOG_LESSON_RELEARN, this) }
binding.tvRight.click { onDialogListener(AppConstants.DIALOG_START_TEST, this) }
}
} }

+ 43
- 18
app/src/main/java/com/xkl/cdl/module/learn/LearnExamActivity.kt View File

import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator import androidx.recyclerview.widget.SimpleItemAnimator
import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieAnimationView
import com.jeremyliao.liveeventbus.LiveEventBus
import com.suliang.common.base.activity.BaseActivityVM import com.suliang.common.base.activity.BaseActivityVM
import com.suliang.common.eventbus.LiveDataBus
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.DrawableUti
} }
/** 查询到播放数据过来 */ /** 查询到播放数据过来 */
AudioCache.audioLiveData.observe(this) {
AudioCache.initAudioLiveData().observe(this) {
if (it == null) { if (it == null) {
//发音文件为空 //发音文件为空
showToast("未找到发音文件") showToast("未找到发音文件")
LearnDialog.newInstance(learnDialogBean).apply { LearnDialog.newInstance(learnDialogBean).apply {
onDialogListener = { action, dialog -> onDialogListener = { action, dialog ->
when (vm.intentData.examType) { when (vm.intentData.examType) {
//学前测试结束,只有一个按钮,开始学习 -> 发送消息切换到课时列表页
//学前测试结束,只有一个按钮,开始学习 -> 发送消息切换到课时列表页
AppConstants.TEST_TYPE_BEFORE_TOTAL -> when (action) { AppConstants.TEST_TYPE_BEFORE_TOTAL -> when (action) {
AppConstants.DIALOG_START_LEARN -> { AppConstants.DIALOG_START_LEARN -> {
dialog.dismissAllowingStateLoss() dialog.dismissAllowingStateLoss()
//发送动作 : 继续学习 //发送动作 : 继续学习
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value =
LearnEventData(vm.intentData.subjectId,
vm.intentData.courseId,
AppConstants.ACTION_COURSE_TEST_START_LEARN)
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_CHANGE_PAGE).post(LearnEventData(vm.intentData.subjectId,
vm.intentData.courseId,
AppConstants.ACTION_COURSE_TEST_START_LEARN))
finish() finish()
} }
} }
AppConstants.TEST_TYPE_BEFORE -> when (action) { AppConstants.TEST_TYPE_BEFORE -> when (action) {
AppConstants.DIALOG_START_LEARN -> { AppConstants.DIALOG_START_LEARN -> {
dialog.dismissAllowingStateLoss() dialog.dismissAllowingStateLoss()
//发送动作 : 继续学习
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value =
//发送动作 : 开始学习
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post(
LearnEventData(vm.intentData.subjectId, LearnEventData(vm.intentData.subjectId,
vm.intentData.courseId, vm.intentData.courseId,
AppConstants.ACTION_LESSON_BEFORE_TEST_OVER_START_LEARN).apply { AppConstants.ACTION_LESSON_BEFORE_TEST_OVER_START_LEARN).apply {
leesonPositionIndex = vm.intentData.lesson?.lessonPositionInList!! leesonPositionIndex = vm.intentData.lesson?.lessonPositionInList!!
}
})
finish() finish()
} }
} }
if (isRightClick){ if (isRightClick){
dialog.dismissAllowingStateLoss() dialog.dismissAllowingStateLoss()
//发送动作 : 重新学习 //发送动作 : 重新学习
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value =
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post(
LearnEventData(vm.intentData.subjectId, LearnEventData(vm.intentData.subjectId,
vm.intentData.courseId, vm.intentData.courseId,
AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN).apply { AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN).apply {
leesonPositionIndex = vm.intentData.lesson?.lessonPositionInList!! leesonPositionIndex = vm.intentData.lesson?.lessonPositionInList!!
}
})
finish()
} }
} }
}.show(childFragmentManager,"lesson_relearn_tip") }.show(childFragmentManager,"lesson_relearn_tip")
} }
//再测一次 //再测一次
AppConstants.DIALOG_LESSON_AFTER_TEST_AGAIN -> { AppConstants.DIALOG_LESSON_AFTER_TEST_AGAIN -> {
//发送动作 : 继续学习
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value =
//发送动作 : 再测一次
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post(
LearnEventData(vm.intentData.subjectId, LearnEventData(vm.intentData.subjectId,
vm.intentData.courseId, vm.intentData.courseId,
AppConstants.ACTION_LESSON_AFTER_TEST_AGAIN).apply { AppConstants.ACTION_LESSON_AFTER_TEST_AGAIN).apply {
leesonPositionIndex = vm.intentData.lesson?.lessonPositionInList!! leesonPositionIndex = vm.intentData.lesson?.lessonPositionInList!!
}
})
finish()
} }
//下一步 //下一步
AppConstants.DIALOG_LESSON_AFTER_TEST_NEXT -> { AppConstants.DIALOG_LESSON_AFTER_TEST_NEXT -> {
//发送动作 : 继续学习
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value =
//发送动作 : 下一步
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post(
LearnEventData(vm.intentData.subjectId, LearnEventData(vm.intentData.subjectId,
vm.intentData.courseId, vm.intentData.courseId,
AppConstants.ACTION_LESSON_AFTER_TEST_NEXT).apply { AppConstants.ACTION_LESSON_AFTER_TEST_NEXT).apply {
leesonPositionIndex = vm.intentData.lesson?.lessonPositionInList!! leesonPositionIndex = vm.intentData.lesson?.lessonPositionInList!!
}
})
finish()
}
}
//学后总测试结束
AppConstants.TEST_TYPE_AFTER_TOTAL -> when(action){
//测试完成,切换到目录页
AppConstants.DIALOG_OVER -> {
dialog.dismissAllowingStateLoss()
//发送动作 : 继续学习
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_CHANGE_PAGE).post(
LearnEventData(vm.intentData.subjectId,
vm.intentData.courseId,
AppConstants.ACTION_COURSE_TEST_AFTER_TOTAL_OVER))
finish()
}
//再测一次
AppConstants.DIALOG_AFTER_TOTAL_TEST_AGAIN -> {
dialog.dismissAllowingStateLoss()
//发送动作 : 再测一次
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_CHANGE_PAGE).post(
LearnEventData(vm.intentData.subjectId,
vm.intentData.courseId,
AppConstants.ACTION_COURSE_TEST_AFTER_TOTAL_AGAIN))
finish()
} }
} }
} }

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

import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.googlecode.protobuf.format.JsonFormat
import com.suliang.common.eventbus.LiveDataBus
import com.jeremyliao.liveeventbus.LiveEventBus
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
//倒计时结束 //倒计时结束
val currentCountingTimeOver = MutableLiveData<Boolean>() val currentCountingTimeOver = MutableLiveData<Boolean>()
init {
isRunValidTime = false
}
/** 修改获取下一题的标记为true,则在onResume时,自动获取第一题 */ /** 修改获取下一题的标记为true,则在onResume时,自动获取第一题 */
fun loadFirst(){ fun loadFirst(){
isGetNextIng = true isGetNextIng = true
//计算分数 //计算分数
calculateScore() calculateScore()
when(intentData.examType){ when(intentData.examType){
AppConstants.TEST_TYPE_BEFORE -> { //学前测为lesson添加错误数 与 分数
AppConstants.TEST_TYPE_BEFORE -> { //课时学前测为lesson添加错误数 与 分数
intentData.lesson?.let { intentData.lesson?.let {
it.errorNumber = it.errorNumber + newErrorMap.size
it.errorNumber += newErrorMap.size
it.beforeTestScore = scoreValue.toDouble() it.beforeTestScore = scoreValue.toDouble()
} }
} }
} else { //相当于新错 } else { //相当于新错
if (!newErrorMap.containsKey(key)) { if (!newErrorMap.containsKey(key)) {
newErrorMap[key] = true newErrorMap[key] = true
//课时前错误 ,则更新课时错误数量
if (intentData.examType == AppConstants.TEST_TYPE_BEFORE) {
intentData.lesson!!.errorNumber = intentData.lesson!!.errorNumber + 1
}
} }
} }
mLearnEntities.add(builder.build()) mLearnEntities.add(builder.build())
//其他测试 //其他测试
else -> { else -> {
val calculateCorrectNumber : Double = correctLiveData.value!! + unAnswerNumber * 0.25 val calculateCorrectNumber : Double = correctLiveData.value!! + unAnswerNumber * 0.25
val scoreValue = ((calculateCorrectNumber - testData.size * 0.25) / (testData.size * 0.25) * 100).toInt()
val scoreValue = ((calculateCorrectNumber - testData.size * 0.25) / (testData.size * 0.75) * 100).toInt()
this.scoreValue = when { this.scoreValue = when {
scoreValue < 0 -> 0 scoreValue < 0 -> 0
scoreValue > 100 -> 100 scoreValue > 100 -> 100
else -> scoreValue else -> scoreValue
} }
} }
} }
} }
private fun sendEventBus(){ private fun sendEventBus(){
when(intentData.examType){ when(intentData.examType){
AppConstants.TEST_TYPE_BEFORE_TOTAL -> { //学前总测发送数据、学前总测发送数据 AppConstants.TEST_TYPE_BEFORE_TOTAL -> { //学前总测发送数据、学前总测发送数据
LiveDataBus.withNonSticky<LearnEventData>(AppConstants.EVENT_COURSE).value = LearnEventData(
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_TOTAL_TEST).post( LearnEventData(
intentData.subjectId, intentData.subjectId,
intentData.courseId, intentData.courseId,
AppConstants.DATA_COURSE_BEFORE_TEST_OVER).apply { AppConstants.DATA_COURSE_BEFORE_TEST_OVER).apply {
this.scoreValue = this@LearnExamViewModel.scoreValue this.scoreValue = this@LearnExamViewModel.scoreValue
this.newErrorMap = this@LearnExamViewModel.newErrorMap this.newErrorMap = this@LearnExamViewModel.newErrorMap
}
})
} }
AppConstants.TEST_TYPE_BEFORE-> { // 课时学前测试结束发送数据 AppConstants.TEST_TYPE_BEFORE-> { // 课时学前测试结束发送数据
LiveDataBus.withNonSticky<LearnEventData>(AppConstants.EVENT_COURSE).value = LearnEventData(
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_DATA).post( LearnEventData(
intentData.subjectId, intentData.subjectId,
intentData.courseId, intentData.courseId,
AppConstants.DATA_LESSON_BEFORE_TEST_OVER).apply { AppConstants.DATA_LESSON_BEFORE_TEST_OVER).apply {
intentData.lesson?.let { intentData.lesson?.let {
leesonPositionIndex = it.lessonPositionInList leesonPositionIndex = it.lessonPositionInList
} }
}
})
} }
AppConstants.TEST_TYPE_AFTER -> { //课时学后测试结束发送数据 AppConstants.TEST_TYPE_AFTER -> { //课时学后测试结束发送数据
LiveDataBus.withNonSticky<LearnEventData>(AppConstants.EVENT_COURSE).value = LearnEventData(
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_DATA).post(LearnEventData(
intentData.subjectId, intentData.subjectId,
intentData.courseId, intentData.courseId,
AppConstants.DATA_LESSON_AFTER_TEST_OVER).apply { AppConstants.DATA_LESSON_AFTER_TEST_OVER).apply {
this.newErrorMap = this@LearnExamViewModel.newErrorMap
intentData.lesson?.let { intentData.lesson?.let {
leesonPositionIndex = it.lessonPositionInList leesonPositionIndex = it.lessonPositionInList
} }
}
})
}
AppConstants.TEST_TYPE_AFTER_TOTAL -> { //学后总测发送数据
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_TOTAL_TEST).post(LearnEventData(
intentData.subjectId,
intentData.courseId,
AppConstants.DATA_COURSE_AFTER_TEST_OVER).apply {
this.scoreValue = this@LearnExamViewModel.scoreValue
})
} }
} }
} }

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

import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.OnScrollListener import androidx.recyclerview.widget.RecyclerView.OnScrollListener
import androidx.recyclerview.widget.SimpleItemAnimator import androidx.recyclerview.widget.SimpleItemAnimator
import com.jeremyliao.liveeventbus.LiveEventBus
import com.suliang.common.base.activity.BaseActivityVM import com.suliang.common.base.activity.BaseActivityVM
import com.suliang.common.eventbus.LiveDataBus
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.LogUtil import com.suliang.common.util.LogUtil
vm.loadNext() vm.loadNext()
//发音数据 //发音数据
AudioCache.audioLiveData.observe(this) {
AudioCache.initAudioLiveData().observe(this) {
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.photoLiveData.observe(this) {
PhotoCache.initPhotoLiveData().observe(this) {
it?.run { ImageLoader.loadImage(bindingWord.imgWord, it) } ?: let { bindingWord.imgWord.visibility = View.GONE } it?.run { ImageLoader.loadImage(bindingWord.imgWord, it) } ?: let { bindingWord.imgWord.visibility = View.GONE }
} }
} }
private fun showLearnOverDialog() { private fun showLearnOverDialog() {
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_LEARN_OVER).apply {
correctNumber = vm.learnData.lesson.correctNumber + vm.learnRuleUtil.currentCorrectMap.size
errorNumber = vm.learnData.lesson.errorNumber + vm.learnRuleUtil.currentErrorMap.size
correctNumber = vm.learnData.lesson.correctNumber
errorNumber = vm.learnData.lesson.errorNumber
this.showTimeCount = showTimeCount this.showTimeCount = showTimeCount
}).apply { }).apply {
onDialogListener = { action, dialog -> onDialogListener = { action, dialog ->
if (isRightClick){ if (isRightClick){
dialog.dismissAllowingStateLoss() dialog.dismissAllowingStateLoss()
//发送动作 : 重新学习 //发送动作 : 重新学习
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value =
LearnEventData(vm.learnData.lesson.subjectId,
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post(
LearnEventData(vm.learnData.lesson.subjectId,
vm.learnData.lesson.courseId, vm.learnData.lesson.courseId,
AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN).apply { AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN).apply {
leesonPositionIndex = vm.learnData.lesson.lessonPositionInList leesonPositionIndex = vm.learnData.lesson.lessonPositionInList
}
})
finish()
} }
} }
}.show(childFragmentManager,"lesson_relearn_tip") }.show(childFragmentManager,"lesson_relearn_tip")
AppConstants.DIALOG_START_TEST -> { AppConstants.DIALOG_START_TEST -> {
dialog.dismissAllowingStateLoss() dialog.dismissAllowingStateLoss()
//发送动作 : 学后测试 //发送动作 : 学后测试
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value =
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post(
LearnEventData(vm.learnData.lesson.subjectId, LearnEventData(vm.learnData.lesson.subjectId,
vm.learnData.lesson.courseId, vm.learnData.lesson.courseId,
AppConstants.ACTION_LESSON_AFTER_TEST_AGAIN).apply { AppConstants.ACTION_LESSON_AFTER_TEST_AGAIN).apply {
leesonPositionIndex = vm.learnData.lesson.lessonPositionInList leesonPositionIndex = vm.learnData.lesson.lessonPositionInList
}
})
finish()
} }
} }
} }

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

import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.googlecode.protobuf.format.JsonFormat
import com.suliang.common.eventbus.LiveDataBus
import com.jeremyliao.liveeventbus.LiveEventBus
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
//数据上传完成监听: true 学习完成的上传 false可能是半途的返回事件,直接退出 //数据上传完成监听: true 学习完成的上传 false可能是半途的返回事件,直接退出
val saveDataLiveData = MutableLiveData<Boolean>() val saveDataLiveData = MutableLiveData<Boolean>()
/** 获取数据 */ /** 获取数据 */
fun loadNext() { fun loadNext() {
//修改标记 //修改标记
currentLearnWord.value = it currentLearnWord.value = it
} ?: let { //没有数据 即学习完成 } ?: let { //没有数据 即学习完成
isAllOver = true isAllOver = true
// 停止有效计时
isRunValidTime = false
//停止总计时 //停止总计时
stopTotalCountTing() stopTotalCountTing()
// TODO: 2022/4/26 停止有效计时
//封装保存数据 //封装保存数据
saveData() saveData()
} }
learnedIndex = currentLessonLearnedPosition learnedIndex = currentLessonLearnedPosition
correctNumber += learnRuleUtil.currentCorrectMap.size correctNumber += learnRuleUtil.currentCorrectMap.size
errorNumber += learnRuleUtil.currentErrorMap.size errorNumber += learnRuleUtil.currentErrorMap.size
learnIsOver = (correctNumber + errorNumber == totalNumber)
learnIsOver = learnedIndex == wordIds.size - 1
} }
//添加到错误本集合中,主要用于小游戏练习(学前总 课时都没有添加,从这里添加到集合后发送出去,添加到集合) //添加到错误本集合中,主要用于小游戏练习(学前总 课时都没有添加,从这里添加到集合后发送出去,添加到集合)
learnData.examErrorMap?.putAll(learnRuleUtil.currentErrorMap) learnData.examErrorMap?.putAll(learnRuleUtil.currentErrorMap)
/** 发送数据事件 */ /** 发送数据事件 */
private fun sendEventBus() { private fun sendEventBus() {
LiveDataBus.withNonSticky<LearnEventData>(AppConstants.EVENT_COURSE).value = LearnEventData(learnData.lesson.subjectId,
learnData.lesson.courseId,
AppConstants.DATA_LESSON_LEARN_OVER).apply {
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_DATA).post(LearnEventData(learnData.lesson.subjectId,
learnData.lesson.courseId,
AppConstants.DATA_LESSON_LEARN_OVER).apply {
this.leesonPositionIndex = learnData.lesson.lessonPositionInList this.leesonPositionIndex = learnData.lesson.lessonPositionInList
this.newErrorMap = learnData.examErrorMap this.newErrorMap = learnData.examErrorMap
}
})
} }

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

import com.xkl.cdl.adapter.AdapterAutoPlaySelectRepeat import com.xkl.cdl.adapter.AdapterAutoPlaySelectRepeat
import com.xkl.cdl.adapter.ViewPagerAdapter import com.xkl.cdl.adapter.ViewPagerAdapter
import com.xkl.cdl.data.AppConstants import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.bean.LearnDialogBean
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.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.CommonDialogBean
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


} }
} }
} }
//改变page后设置进度
//改变page后设置进度,获取该课程的进度进行设置
vm.currentCourseProgress.value = vm.coursePack.childrenCourses[position].courseLearnProgress vm.currentCourseProgress.value = vm.coursePack.childrenCourses[position].courseLearnProgress
} }
}) })
progressbar.progress = it.toInt() progressbar.progress = it.toInt()
tvProgress.text = if (binding.includeCourseTypeTab.root.visibility == View.VISIBLE) { tvProgress.text = if (binding.includeCourseTypeTab.root.visibility == View.VISIBLE) {
val format = when (binding.viewPager2.currentItem) { val format = when (binding.viewPager2.currentItem) {
0 -> "认读进度:%s"
1 -> "拼写进度:%s"
2 -> "辨音进度:%s"
else -> "进度:%s"
0 -> "认读进度: %s%%"
1 -> "拼写进度: %s%%"
2 -> "辨音进度: %s%%"
else -> "进度: %s%%"
} }
String.format(format, CourseManager.useToShowProgress(it)) String.format(format, CourseManager.useToShowProgress(it))
} else { } else {
String.format("进度:%s", CourseManager.useToShowProgress(it))
String.format("进度: %s%%", CourseManager.useToShowProgress(it))
} }
} }
} }
//更多按钮点击 //更多按钮点击
binding.includeCourseProgress.ivMore.click { binding.includeCourseProgress.ivMore.click {
if (vm.showMoreEnable)
showMoreDialog() showMoreDialog()
else
showToast("请先进行学习")
} }
} }


} }
moreBinding.tvClearLearnRecord.click { moreBinding.tvClearLearnRecord.click {
dismiss() dismiss()
// TODO: 2022/3/29 弹窗显示清除学习记录
showCourseRelearnDialog()
} }
} }
} }
moreDialog?.show() moreDialog?.show()
} }

/** 课程重学弹窗提示 */
fun showCourseRelearnDialog(){
//弹窗显示清除学习记录
val bean = CommonDialogBean(titleText = R.string.course_relearn_title,
contentText = R.string.course_relearn_content,
leftText = R.string.course_relearn_sure, rightText = R.string.cancel)
CommonDialog.newInstance(bean).apply {
onCommonDialogButtonClickListener = { dialog, isRightClick ->
dialog.dismissAllowingStateLoss()
if (!isRightClick){
((this@CoursePackMainActivity.binding.viewPager2.adapter as ViewPagerAdapter).getItem(this@CoursePackMainActivity.binding.viewPager2.currentItem) as CourseMainFragment).run {
this.courseRelearn()
}
}
}
}.show(supportFragmentManager,"course_relearn_dialog")
}
/** 自动播放次数选择: 仅单词会有该选项 */ /** 自动播放次数选择: 仅单词会有该选项 */
private fun showAutoPlaySelectDialog() { private fun showAutoPlaySelectDialog() {
if (autoPlaySeletDialog == null) { if (autoPlaySeletDialog == null) {
private fun startAutoPlay(time: Int) { private fun startAutoPlay(time: Int) {


} }
override fun onStop() {
super.onStop()
//如果是中文项目,更新课程包的进度
if (vm.coursePack.subjectId == AppConstants.SUBJECT_CHINESE){
vm.coursePack.learnProgress = vm.coursePack.childrenCourses[0].courseLearnProgress
}
}




/** ViewModel Factory工厂 */ /** ViewModel Factory工厂 */

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

* Describe: 课程主页 * Describe: 课程主页
*/ */
class CoursePackMainActivityViewModel(subjectId: Int , coursePackInPosition : Int) : BaseViewModel() { class CoursePackMainActivityViewModel(subjectId: Int , coursePackInPosition : Int) : BaseViewModel() {
//操作的课程包 //操作的课程包
val coursePack = CourseManager.subjectWithCoursePackMap[subjectId]!![coursePackInPosition] val coursePack = CourseManager.subjectWithCoursePackMap[subjectId]!![coursePackInPosition]


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


//更多点击是否有效
var showMoreEnable = false
} }

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

import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.jeremyliao.liveeventbus.LiveEventBus
import com.suliang.common.base.fragment.BaseFragmentVM import com.suliang.common.base.fragment.BaseFragmentVM
import com.suliang.common.eventbus.LiveDataBus
import com.suliang.common.util.DateUtil
import com.xkl.cdl.R import com.xkl.cdl.R
import com.xkl.cdl.adapter.AdapterLesson import com.xkl.cdl.adapter.AdapterLesson
import com.xkl.cdl.data.AppConstants import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.bean.intentdata.ExamData import com.xkl.cdl.data.bean.intentdata.ExamData
import com.xkl.cdl.data.event.LearnEventData import com.xkl.cdl.data.event.LearnEventData
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.FragmentCourseLessonBinding import com.xkl.cdl.databinding.FragmentCourseLessonBinding
import com.xkl.cdl.dialog.CommonDialog
import com.xkl.cdl.dialog.CommonDialogBean
import com.xkl.cdl.dialog.LearnDialog import com.xkl.cdl.dialog.LearnDialog
import com.xkl.cdl.module.learn.LearnWordActivity import com.xkl.cdl.module.learn.LearnWordActivity
import mqComsumerV1.Struct
import java.text.DateFormat


/** /**
* 课程章节目录 * 课程章节目录
layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
adapterLesson = AdapterLesson(vm).apply { adapterLesson = AdapterLesson(vm).apply {
onItemClick = onLessonClick onItemClick = onLessonClick
//默认选中位置,默认为0 ,如果学习完成则设置为-1
selectPos = vm.initSelectPosition()
} }
adapter = adapterLesson adapter = adapterLesson
} }
private fun listenerLiveBus() { private fun listenerLiveBus() {
//监听数据 //监听数据
LiveDataBus.withNonSticky<LearnEventData>(AppConstants.EVENT_COURSE).observe(this) { learnEventData ->
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_DATA).observe(this) { learnEventData ->
if (learnEventData.subjectId != vm.course.subjectId || learnEventData.courseId != vm.course.courseId) return@observe if (learnEventData.subjectId != vm.course.subjectId || learnEventData.courseId != vm.course.courseId) return@observe
when (learnEventData.actionFlag) { when (learnEventData.actionFlag) {
//课时学前测试结束,传递数据回来更新数据 //课时学前测试结束,传递数据回来更新数据
AppConstants.DATA_LESSON_BEFORE_TEST_OVER -> { AppConstants.DATA_LESSON_BEFORE_TEST_OVER -> {
// 更新Lesson 更新detail课时学前成绩,错误条目数,测试前错误列表 // 更新Lesson 更新detail课时学前成绩,错误条目数,测试前错误列表
} }
} }
} }
//课时学前测试结束,开始学习 -> 进入学习界面
AppConstants.ACTION_LESSON_BEFORE_TEST_OVER_START_LEARN -> {
startLearn(vm.allLesson[learnEventData.leesonPositionIndex])
}
//课时学习结束
//课时学习完成
AppConstants.DATA_LESSON_LEARN_OVER -> { AppConstants.DATA_LESSON_LEARN_OVER -> {
// 更新Lesson 更新detail错误条目数
if (learnEventData.subjectId != vm.course.subjectId || learnEventData.courseId != vm.course.courseId) return@observe
//更新Lesson 更新detail错误条目数
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.wrong.put(key, it.errorNumber) vm.courseDetail.wrong.put(key, it.errorNumber)
//更新正确数
vm.courseDetail.right.put(key, it.correctNumber)
//添加到错误本中,现在主要用于小游戏取值 //添加到错误本中,现在主要用于小游戏取值
learnEventData.newErrorMap?.forEach { learnEventData.newErrorMap?.forEach {
vm.courseDetail.temporary_words.put(it.key, "") vm.courseDetail.temporary_words.put(it.key, "")
} }
vm.courseDetail.courseLearnProgress = courseProgress vm.courseDetail.courseLearnProgress = courseProgress
vm.course.courseLearnProgress = courseProgress vm.course.courseLearnProgress = courseProgress
vm.coursePackMainActivityVM.currentCourseProgress.value = courseProgress
//项目总进度 //项目总进度
val subjectProgress = CourseManager.calculateSubjectProgress(vm.course.subjectId, vm.course.coursePackId, val subjectProgress = CourseManager.calculateSubjectProgress(vm.course.subjectId, vm.course.coursePackId,
vm.course.courseId, courseProgress) vm.course.courseId, courseProgress)
vm.courseDetail.after.put(key, it.beforeTestScore) vm.courseDetail.after.put(key, it.beforeTestScore)
} }
} }
//学后测试结束,弹窗动作
}
}
//监听课时学习的动作
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).observe(this) { learnEventData ->
if (learnEventData.subjectId != vm.course.subjectId || learnEventData.courseId != vm.course.courseId) return@observe
when (learnEventData.actionFlag) {
//课时学前测试结束,开始学习 -> 进入学习界面
AppConstants.ACTION_LESSON_BEFORE_TEST_OVER_START_LEARN -> {
startLearn(vm.allLesson[learnEventData.leesonPositionIndex])
}
//学后测试结束,弹窗动作 课时重新学习
AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN -> { AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN -> {
//课时学后测试,点击重学 //课时学后测试,点击重学
// TODO: 2022/4/22 清除当前课时数据,并更新当前课时,后进入学习界面 // TODO: 2022/4/22 清除当前课时数据,并更新当前课时,后进入学习界面
learnEventData.leesonPositionIndex
vm.allLesson[learnEventData.leesonPositionIndex].learnIsOver = false
vm.relearnLesson(learnEventData.leesonPositionIndex).observe(this) {
//item更新
adapterLesson.notifyItemChanged(learnEventData.leesonPositionIndex)
//进行点击,此时当前课时item应该处于显示中,可直接点击
(binding.recyclerView.layoutManager as LinearLayoutManager).findViewByPosition(
learnEventData.leesonPositionIndex)
?.findViewById<ConstraintLayout>(R.id.layout_content)
?.performClick()
}
} }
/**课时学后测试弹窗动作: 再测一次 ,学习结束弹窗: 开始学后测试,共同点:直接进入测试,没有弹窗提示*/ /**课时学后测试弹窗动作: 再测一次 ,学习结束弹窗: 开始学后测试,共同点:直接进入测试,没有弹窗提示*/
AppConstants.ACTION_LESSON_AFTER_TEST_AGAIN -> { AppConstants.ACTION_LESSON_AFTER_TEST_AGAIN -> {
//学后测试,再测一次 //学后测试,再测一次
loadLessonAfterTest(vm.allLesson[learnEventData.leesonPositionIndex]) loadLessonAfterTest(vm.allLesson[learnEventData.leesonPositionIndex])
} }
//下一步
AppConstants.ACTION_LESSON_AFTER_TEST_NEXT -> { AppConstants.ACTION_LESSON_AFTER_TEST_NEXT -> {
//下一课时 or 下一步(提示课程学习完成) //下一课时 or 下一步(提示课程学习完成)
// TODO: 2022/4/22 判断课程是否学习完成,学习完成,显示课程学习完成弹窗,否则进入下一个没有学习完成的课时,进行学习
when(vm.course.courseLearnProgress){
100.0 -> showCourseOverDialog()
when (vm.course.courseLearnProgress) {
100.0 -> {
adapterLesson.selectPos = -1
adapterLesson.notifyDataSetChanged()
showCourseOverDialog()
}
else -> { else -> {
var nextLessonPosition = -1 var nextLessonPosition = -1
//从下一课时开始到最后一个课时 //从下一课时开始到最后一个课时
for ( i in learnEventData.leesonPositionIndex + 1 until vm.allLesson.size){
for (i in learnEventData.leesonPositionIndex + 1 until vm.allLesson.size) {
if (!vm.allLesson[i].learnIsOver) { if (!vm.allLesson[i].learnIsOver) {
nextLessonPosition = i nextLessonPosition = i
break break
val linearLayoutManager = binding.recyclerView.layoutManager as LinearLayoutManager val linearLayoutManager = binding.recyclerView.layoutManager as LinearLayoutManager
val findFirstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition() //第一个可见位置 val findFirstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition() //第一个可见位置
val findLastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition() //最后一个可见位置 val findLastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition() //最后一个可见位置
if (nextLessonPosition in findFirstVisibleItemPosition .. findLastVisibleItemPosition){
linearLayoutManager.findViewByPosition(nextLessonPosition)?.findViewById<ConstraintLayout>(R.id.layout_content)?.performClick()
}else{
if (nextLessonPosition in findFirstVisibleItemPosition .. findLastVisibleItemPosition) {
linearLayoutManager.findViewByPosition(nextLessonPosition)
?.findViewById<ConstraintLayout>(R.id.layout_content)
?.performClick()
} else {
recycleViewScrollListener.lessonPosition = nextLessonPosition recycleViewScrollListener.lessonPosition = nextLessonPosition
binding.recyclerView.addOnScrollListener(recycleViewScrollListener) binding.recyclerView.addOnScrollListener(recycleViewScrollListener)
binding.recyclerView.smoothScrollToPosition(nextLessonPosition) binding.recyclerView.smoothScrollToPosition(nextLessonPosition)
var lessonPosition = 0 var lessonPosition = 0
override fun onScrollStateChanged(recyclerView : RecyclerView, newState : Int) { override fun onScrollStateChanged(recyclerView : RecyclerView, newState : Int) {
super.onScrollStateChanged(recyclerView, newState) super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE){
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
binding.recyclerView.removeOnScrollListener(this) binding.recyclerView.removeOnScrollListener(this)
val linearLayoutManager = binding.recyclerView.layoutManager as LinearLayoutManager val linearLayoutManager = binding.recyclerView.layoutManager as LinearLayoutManager
linearLayoutManager.findViewByPosition(lessonPosition)?.findViewById<ConstraintLayout>(R.id.layout_content)?.performClick()
linearLayoutManager.findViewByPosition(lessonPosition)
?.findViewById<ConstraintLayout>(R.id.layout_content)
?.performClick()
} }
} }
} }
private val onLessonClick : (v : View, position : Int, lesson : Lesson) -> Unit = { view, position, entity -> private val onLessonClick : (v : View, position : Int, lesson : Lesson) -> Unit = { view, position, entity ->
when (entity.lessonType) { when (entity.lessonType) {
AppConstants.LESSON_TYPE_WORD -> { AppConstants.LESSON_TYPE_WORD -> {
startLearn(entity)
/* if (entity.beforeTestScore == AppConstants.NOT_DOING){ //课时学前测试,没有做
//弹窗显示学前测试提示
showLessonBeforeTestStartDialog(entity)
}else if (!entity.learnIsOver){ //当前课时未学完,直接开始学习
startLearn(entity)
}else if (entity.afterTestScore != AppConstants.NOT_DOING){
loadLessonAfterTest(entity)
}else{ //当前课时学习完成的弹窗
showLessonAllOverDialog(entity)
}*/
if (entity.beforeTestScore == AppConstants.NOT_DOING) { //课时学前测试,没有做
//弹窗显示学前测试提示
showLessonBeforeTestStartDialog(entity)
} else if (!entity.learnIsOver) { //当前课时未学完,直接开始学习
startLearn(entity)
} else if (entity.afterTestScore == AppConstants.NOT_DOING) {
loadLessonAfterTest(entity)
} else { //当前课时学习完成的弹窗
showLessonAllOverDialog(entity)
}
} }
AppConstants.LESSON_TYPE_SENTENCE -> { AppConstants.LESSON_TYPE_SENTENCE -> {
* @param lesson Lesson 课时 * @param lesson Lesson 课时
*/ */
private fun startLearn(lesson : Lesson) { private fun startLearn(lesson : Lesson) {
vm.loadLessonLearnData(lesson).observe(this) {
DataTransferHolder.instance.putData(value = it)
startActivity(LearnWordActivity::class.java)
when (lesson.lessonType) {
AppConstants.LESSON_TYPE_WORD -> {
startLessonLearnForWord(lesson)
/* if (entity.beforeTestScore == AppConstants.NOT_DOING){ //课时学前测试,没有做
//弹窗显示学前测试提示
showLessonBeforeTestStartDialog(entity)
}else if (!entity.learnIsOver){ //当前课时未学完,直接开始学习
startLearn(entity)
}else if (entity.afterTestScore != AppConstants.NOT_DOING){
loadLessonAfterTest(entity)
}else{ //当前课时学习完成的弹窗
showLessonAllOverDialog(entity)
}*/
}
AppConstants.LESSON_TYPE_SENTENCE -> {
}
AppConstants.LESSON_TYPE_DIALOGUE -> {
}
AppConstants.LESSON_TYPE_COMPOSITION_VIDEO -> {
}
AppConstants.LESSON_TYPE_COMPOSITION_KNOWLEDGE -> {
}
AppConstants.LESSON_TYPE_COMPOSITION_EXAM -> {
}
AppConstants.LESSON_TYPE_COMPOSITION_READING -> {
}
AppConstants.LESSON_TYPE_COMPOSITION_TASK -> {
}
} }
} }
/** lesson的学习 lessonType 为 word类型 */ /** lesson的学习 lessonType 为 word类型 */
private fun startLessonLearnForWord() {
private fun startLessonLearnForWord(lesson : Lesson) {
vm.loadLessonLearnData(lesson).observe(this) {
DataTransferHolder.instance.putData(value = it)
startActivity(LearnWordActivity::class.java)
}
} }
/** /**
* 当前课时学前、学习、学后都完成了的弹窗 * 当前课时学前、学习、学后都完成了的弹窗
*/ */
private fun showLessonAllOverDialog(lesson : Lesson) { private fun showLessonAllOverDialog(lesson : Lesson) {
LearnDialog.newInstance(LearnDialogBean(AppConstants.DIALOG_TYPE_LESSON_ITEM_OVER).apply {
correctNumber = lesson.correctNumber
errorNumber = lesson.errorNumber
score = lesson.afterTestScore.toInt()
}).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()
//发送动作 : 学后测试
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post( LearnEventData(lesson.subjectId, lesson.courseId,
AppConstants.ACTION_LESSON_AFTER_TEST_AGAIN).apply {
leesonPositionIndex = lesson.lessonPositionInList
})
}
}
}
}.show(childFragmentManager, "learn_over_dialog")
} }
/** /**
* 显示课程学习完成的弹窗
* 显示课程学习完成的弹窗,在最后一个课时测试完成的下一步,相当于在课时目录页显示去学后总测试的弹窗
*/ */
private fun showCourseOverDialog() { private fun showCourseOverDialog() {
// TODO: 2022/4/29 课程学习完成弹窗
vm.loadTest(AppConstants.TEST_TYPE_AFTER_TOTAL).observe(this) {
val learnDialogBean = LearnDialogBean(AppConstants.DIALOG_TYPE_EXAM_START).apply {
examType = AppConstants.TEST_TYPE_AFTER_TOTAL
showTimeCount = CourseManager.expectedTestTime(vm.course.courseType, examType, it)
}
LearnDialog.newInstance(learnDialogBean).apply {
onDialogListener = { action, dialog ->
dialog.dismissAllowingStateLoss()
when (action) {
//开始是学后总测试
AppConstants.DIALOG_START_TEST -> {//生成数据
val examData = ExamData(vm.course.subjectId, learnDialogBean.examType, vm.course.courseTitle,
vm.course.courseTitle).apply {
coursePackId = vm.course.coursePackId
coursePackType = vm.course.coursePackType
courseId = vm.course.courseId
courseType = vm.course.courseType
this.testData = it
}
(parentFragment as CourseMainFragment).startExam(examData)
}
}
}
}.show(childFragmentManager, "course_learn_after_test_before")
}
} }

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

package com.xkl.cdl.module.m_center_learn.coursechildren package com.xkl.cdl.module.m_center_learn.coursechildren


import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.jeremyliao.liveeventbus.LiveEventBus
import com.suliang.common.AppConfig import com.suliang.common.AppConfig
import com.suliang.common.base.fragment.BaseFragmentVM import com.suliang.common.base.fragment.BaseFragmentVM
import com.suliang.common.eventbus.LiveDataBus
import com.suliang.common.extension.replaceFragment import com.suliang.common.extension.replaceFragment
import com.suliang.common.util.LogUtil
import com.xkl.cdl.R import com.xkl.cdl.R
import com.xkl.cdl.data.AppConstants import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.DataTransferHolder import com.xkl.cdl.data.DataTransferHolder
} }
} }
/**标记当前显示的fragment*/
private lateinit var currentFragment : Fragment
override fun initViewModel() : CourseMainFragmentViewModel { override fun initViewModel() : CourseMainFragmentViewModel {
return ViewModelProvider(this,
ViewModelFactory(requireArguments().getInt(AppConfig.INTENT_1)))[CourseMainFragmentViewModel::class.java].apply {
val vmmodel = ViewModelProvider(this, ViewModelFactory(
requireArguments().getInt(AppConfig.INTENT_1)))[CourseMainFragmentViewModel::class.java].apply {
coursePackMainActivityVM = ViewModelProvider(requireActivity())[CoursePackMainActivityViewModel::class.java] coursePackMainActivityVM = ViewModelProvider(requireActivity())[CoursePackMainActivityViewModel::class.java]
// LogUtil.e("CourseMainFragment coursePackMainActivityVM hashCode -> ${coursePackMainActivityVM.hashCode()}")
} }
lifecycle.addObserver(vmmodel)
return vmmodel
} }
override fun initFragment() { override fun initFragment() {
//设置课程 和 需要操作的类型 //设置课程 和 需要操作的类型
vm.course = vm.coursePackMainActivityVM.coursePack.childrenCourses[vm.courseIndex].apply { vm.course = vm.coursePackMainActivityVM.coursePack.childrenCourses[vm.courseIndex].apply {
vm.dbControlBase = DbControlBase(subjectId, coursePackId,coursePackType, courseId, courseType)
vm.dbControlBase = DbControlBase(subjectId, coursePackId, coursePackType, courseId, courseType)
} }
//监听动作 总测完成,切换到目录页 //监听动作 总测完成,切换到目录页
LiveDataBus.withNonSticky<LearnEventData>(AppConstants.EVENT_COURSE).observe(this) {
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_CHANGE_PAGE).observe(this) {
if (it.subjectId != vm.course.subjectId || it.courseId != vm.course.courseId) return@observe if (it.subjectId != vm.course.subjectId || it.courseId != vm.course.courseId) return@observe
when (it.actionFlag) { when (it.actionFlag) {
// 学前总测、学后总测 之继续学习 -> 切换到目录页 // 学前总测、学后总测 之继续学习 -> 切换到目录页
AppConstants.ACTION_COURSE_TEST_START_LEARN -> changeFragment(1)
AppConstants.ACTION_COURSE_TEST_START_LEARN -> if (currentFragment !is CourseLessonFragment) changeFragment(1)
}
}
//监听动作 总测完成
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_TOTAL_TEST).observe(this) {
if (it.subjectId != vm.course.subjectId || it.courseId != vm.course.courseId) return@observe
when (it.actionFlag) {
//学前总测结束,传递数据回来更新数据 //学前总测结束,传递数据回来更新数据
AppConstants.DATA_COURSE_BEFORE_TEST_OVER -> { AppConstants.DATA_COURSE_BEFORE_TEST_OVER -> {
vm.courseDetail.st_before = it.scoreValue.toDouble() //学前总分 vm.courseDetail.st_before = it.scoreValue.toDouble() //学前总分
} }
} }
} }
//学后总测结束传递数据回来。更新数据
AppConstants.DATA_COURSE_AFTER_TEST_OVER -> vm.courseDetail.st_after = it.scoreValue.toDouble() //学后总分
//学后总测试结束动作: 再测一次
AppConstants.ACTION_COURSE_TEST_AFTER_TOTAL_AGAIN -> {
vm.loadTest(AppConstants.TEST_TYPE_AFTER_TOTAL).observe(this) {
val examData = ExamData(vm.course.subjectId, AppConstants.TEST_TYPE_AFTER_TOTAL, vm.course.courseTitle,
vm.course.courseTitle).apply {
coursePackId = vm.course.coursePackId
coursePackType = vm.course.coursePackType
courseId = vm.course.courseId
courseType = vm.course.courseType
this.testData = it
}
startExam(examData)
}
}
} }
} }
} }
override fun loadData() { override fun loadData() {
LogUtil.e("CourseMainFragment 加载数据 --》 ${vm.courseIndex}")
vm.loadMain().observe(this) { vm.loadMain().observe(this) {
changeChildrenFragment() changeChildrenFragment()
} }
* 改变加载的子Fragment * 改变加载的子Fragment
*/ */
private fun changeChildrenFragment() { private fun changeChildrenFragment() {

// //加载复习
// replaceFragment(R.id.layout_root, CourseReviewFragment.newInstance())
// //学前总测
// replaceFragment(R.id.layout_root, CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_BEFORE_TOTAL))
//学后总测
// replaceFragment(R.id.layout_root, CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL))
//学习目录
// TODO: 2022/5/5 复习页面加载
vm.courseDetail.run { vm.courseDetail.run {
if (st_before == AppConstants.NOT_DOING) { //学前总测未做 if (st_before == AppConstants.NOT_DOING) { //学前总测未做
replaceFragment(R.id.layout_root, CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_BEFORE_TOTAL))
currentFragment = CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_BEFORE_TOTAL)
replaceFragment(R.id.layout_root, currentFragment)
} else if (courseLearnProgress != 100.0) { //学习未完成 } else if (courseLearnProgress != 100.0) { //学习未完成
replaceFragment(R.id.layout_root, CourseLessonFragment.newInstance())
currentFragment = CourseLessonFragment.newInstance()
replaceFragment(R.id.layout_root, currentFragment)
} else { //学习完成,学后总测试界面 } else { //学习完成,学后总测试界面
replaceFragment(R.id.layout_root, CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL))
currentFragment = CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL)
replaceFragment(R.id.layout_root,currentFragment)
} }
} }
} }
*/ */
fun changeFragment(position : Int) { fun changeFragment(position : Int) {
when (position) { when (position) {
1 -> replaceFragment(R.id.layout_root, CourseLessonFragment.newInstance())
2 -> replaceFragment(R.id.layout_root, CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL))
1 -> {
currentFragment = CourseLessonFragment.newInstance()
replaceFragment(R.id.layout_root,currentFragment )
}
2 -> {
currentFragment = CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL)
replaceFragment(R.id.layout_root, currentFragment)
}
} }
} }
LearnExamActivity.newInstance(requireContext()) LearnExamActivity.newInstance(requireContext())
} }
override fun onDestroy() {
super.onDestroy()
//退出本课程后移除该课程的事件监听
// LiveDataBus.remove(AppConstants.EVENT_COURSE)
/** 课程重学方法 */
fun courseRelearn() {
//调用重学,重学重新加载数据,重新加载界面
vm.relearnCourse().observe(this){
loadData()
}
} }

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

package com.xkl.cdl.module.m_center_learn.coursechildren package com.xkl.cdl.module.m_center_learn.coursechildren


import androidx.lifecycle.LifecycleOwner
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.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.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.bean.course.CourseDetail import com.xkl.cdl.data.bean.course.CourseDetail
import com.xkl.cdl.data.bean.course.ExamBean import com.xkl.cdl.data.bean.course.ExamBean
import com.xkl.cdl.data.bean.course.Lesson import com.xkl.cdl.data.bean.course.Lesson
import com.xkl.cdl.data.bean.intentdata.LearnData import com.xkl.cdl.data.bean.intentdata.LearnData
import com.xkl.cdl.data.manager.CourseManager
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 com.xkl.cdl.data.repository.DataRepository import com.xkl.cdl.data.repository.DataRepository
import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.functions.BiFunction import io.reactivex.rxjava3.functions.BiFunction
import mqComsumerV1.Struct import mqComsumerV1.Struct
import java.io.File
import java.util.* import java.util.*
import kotlin.collections.HashMap import kotlin.collections.HashMap


//获取课程的复习数据 //获取课程的复习数据
//获取课程的章节数据 //获取课程的章节数据
fun loadMain() : MutableLiveData<Boolean> { fun loadMain() : MutableLiveData<Boolean> {
// showHideLoading(true)
val mutableLiveData = MutableLiveData<Boolean>() val mutableLiveData = MutableLiveData<Boolean>()
Observable.zip(DataRepository.getCourseStatistics().flatMap {
Observable.zip(DataRepository.getCourseStatistics(course.subjectId,course.coursePackId,course.courseId).flatMap {
courseDetail = it courseDetail = it
course.courseLearnProgress = it.courseLearnProgress
return@flatMap DataRepository.getCourseAllLesson(dbControlBase, it) return@flatMap DataRepository.getCourseAllLesson(dbControlBase, it)
}.flatMap { }.flatMap {
allLesson = it allLesson = it
}, },
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 初始化需要复习的数据
showMoreIsEnable()
true true
}).compose(diskIo2Main()).subscribe { }).compose(diskIo2Main()).subscribe {
// showHideLoading(false)
mutableLiveData.value = it mutableLiveData.value = it
} }
return mutableLiveData return mutableLiveData
} }
/** 课程包主页上的更多按钮点击是否有效 */
private fun showMoreIsEnable(){
coursePackMainActivityVM.showMoreEnable = courseDetail.st_before != AppConstants.NOT_DOING
}
/** /**
* 获取课程的测试数据 * 获取课程的测试数据
* @param testType Int 测试类型 * @param testType Int 测试类型
.subscribe { .subscribe {
result.value = LearnData(lesson).apply { result.value = LearnData(lesson).apply {
learnWordList = it learnWordList = it
examErrorMap = courseDetail.exam_w_r_list
} }
} }
return result return result
}.compose(diskIo2DiskIo()).subscribe() }.compose(diskIo2DiskIo()).subscribe()
} }
/** 初始化默认选中位置 */
fun initSelectPosition() : Int {
//没有学习点,默认课程学习课时位置为第一个
var temposition = 0
when{
//学后总测试完成,默认无选中
courseDetail.st_after != AppConstants.NOT_DOING -> temposition = -1
//有学习点
courseDetail.course_learn_point.isNotEmpty() -> {
run m@{
allLesson.forEachIndexed { index, lesson ->
if ("${lesson.chapterId}_${lesson.lessonId}" == courseDetail.course_learn_point) {
temposition = index
return@m
}
}
}
}
}
return temposition
}
/**
* 课时重学
* @param lessonPositionIndex Int 重学课时所在位置
*/
fun relearnLesson(lessonPositionIndex : Int):MutableLiveData<Boolean> {
val result = MutableLiveData<Boolean>()
//重学课时后课程的进度
val courseProgress = CourseManager.calculateEnglishCourseProgress(allLesson,true,lessonPositionIndex)
//重学课时后项目的总进度
val subjectProgress = CourseManager.calculateSubjectProgressWithCourseLessonRelearn(course.subjectId,course.coursePackId,course.courseId,courseProgress)
Observable.fromCallable {
// TODO: 2022/5/5 进行数据清除和保存
}.compose(diskIo2DiskIo()).subscribe{
//更新lesson
val lesson = allLesson[lessonPositionIndex].apply {
errorNumber = 0
correctNumber = 0
learnedIndex = -1
learnIsOver = false
afterTestScore = AppConstants.NOT_DOING
}
//更新courseDetail
courseDetail.run {
val lessonKey = "${lesson.chapterId}_${lesson.lessonId}"
//正确错误数归0,不会清除备忘本数据
right[lessonKey] = 0
wrong[lessonKey] = 0
//移除学后测试成绩
after.remove(lessonKey)
//移除章节学习点
lesson_learn_point.remove(lessonKey)
val wordStartKey = "${lessonKey}_"
//移除总测、课时学前测试的学习错误数据
exam_w_r_list.filterKeys {
it.startsWith(wordStartKey)
}.forEach{
exam_w_r_list.remove(it.key)
}
//移除错误本中的记录
temporary_words.filterKeys { it.startsWith(wordStartKey) }.forEach{
temporary_words.remove(it.key)
}
//更新课程进度
courseLearnProgress = courseProgress
course.courseLearnProgress = courseProgress
coursePackMainActivityVM.currentCourseProgress.postValue(courseProgress)
//更新统计总进度
CourseManager.calculateSubjectProgress(course.subjectId, course.coursePackId,
course.courseId, courseProgress)
//完成
result.postValue(true)
}
}
return result
}
/**
* 课程重学方法
*/
fun relearnCourse(): MutableLiveData<Boolean> {
val result = MutableLiveData<Boolean>()
//重学需要重新计算进度
val subjectProgress = CourseManager.calculateSubjectProgressWithCourseRelearn(course.subjectId,course.coursePackId,course.courseId)
Observable.fromCallable {
// TODO: 2022/5/5 调用课程重学 重新请求详情
val file = File(FileUtil.getSaveDirPath("appcache"), "${course.subjectId}_${course.coursePackId}_${course.courseId}")
if (file.exists()){ file.delete() }
}.compose(diskIo2DiskIo())
.subscribe{
courseDetail.run {
courseLearnProgress = 0.0
st_before = AppConstants.NOT_DOING
st_after = AppConstants.NOT_DOING
before.clear()
after.clear()
right.clear()
wrong.clear()
lesson_learn_point.clear()
exam_w_r_list.clear()
course_learn_point = ""
rl += 1
temporary_words.clear()
vp.clear()
exercise_schedule.clear()
}
result.postValue(true)
}
return result
}
// TODO: 2022/5/6 这里在退出时进行了统计数据缓存保存,在loadMain的时候进行了read缓存 ,后期需要清除
override fun onDestroy(owner : LifecycleOwner) {
val objectToBytes = FileUtil.objectToBytes(courseDetail)
FileUtil.writeBytesToFile(FileUtil.getSaveDirPath("appcache"),"${course.subjectId}_${course.coursePackId}_${course.courseId}",objectToBytes)
super.onDestroy(owner)
}
} }

+ 25
- 37
app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseTotalTestFragment.kt View File

import com.xkl.cdl.data.bean.intentdata.ExamData import com.xkl.cdl.data.bean.intentdata.ExamData
import com.xkl.cdl.data.manager.CourseManager import com.xkl.cdl.data.manager.CourseManager
import com.xkl.cdl.databinding.FragmentCourseTotalTestBinding import com.xkl.cdl.databinding.FragmentCourseTotalTestBinding
import com.xkl.cdl.module.m_center_learn.CoursePackMainActivity


/** /**
* 课程总测试: 学前总,学后总 * 课程总测试: 学前总,学后总
* @property totalTestType 测试类型 * @property totalTestType 测试类型
*/ */
class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, CourseMainFragmentViewModel>() { class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, CourseMainFragmentViewModel>() {
companion object { companion object {
@JvmStatic @JvmStatic
fun newInstance(totalTestType: Int) = CourseTotalTestFragment().apply {
fun newInstance(totalTestType : Int) = CourseTotalTestFragment().apply {
arguments = Bundle().apply { arguments = Bundle().apply {
putInt(AppConfig.INTENT_1, totalTestType) putInt(AppConfig.INTENT_1, totalTestType)
} }
} }
} }
override fun initViewModel(): CourseMainFragmentViewModel {
override fun initViewModel() : CourseMainFragmentViewModel {
return ViewModelProvider(requireParentFragment())[CourseMainFragmentViewModel::class.java] return ViewModelProvider(requireParentFragment())[CourseMainFragmentViewModel::class.java]
} }
//由于使用的是与父类相同的loadingEvent,所以将此evnet移除 //由于使用的是与父类相同的loadingEvent,所以将此evnet移除
vm.loadingEvent.removeObservers(this) vm.loadingEvent.removeObservers(this)
} }
//传递过来的总测类型: 学前,学后 //传递过来的总测类型: 学前,学后
private var totalTestType = 0 private var totalTestType = 0
//测试的数据 //测试的数据
private var testData : List<ExamBean> = emptyList() private var testData : List<ExamBean> = emptyList()
override fun initFragment() { override fun initFragment() {
totalTestType = requireArguments().getInt(AppConfig.INTENT_1) totalTestType = requireArguments().getInt(AppConfig.INTENT_1)
} }
override fun loadData() { override fun loadData() {
vm.loadTest(totalTestType).observe(this){
vm.loadTest(totalTestType).observe(this) {
testData = it testData = it
initView() initView()
} }
} }
private fun initView(){
binding.tvCountTip.text = CourseManager.expectedTestTime(vm.course.courseType,totalTestType,testData!!)
private fun initView() {
binding.tvCountTip.text = CourseManager.expectedTestTime(vm.course.courseType, totalTestType, testData!!)
when (totalTestType) { when (totalTestType) {
AppConstants.TEST_TYPE_BEFORE_TOTAL -> { AppConstants.TEST_TYPE_BEFORE_TOTAL -> {
// TODO: 2022/4/21 需要把开始学习给取消了 // TODO: 2022/4/21 需要把开始学习给取消了
tvTitle.setText(R.string.tips_current_course_learn_over) tvTitle.setText(R.string.tips_current_course_learn_over)
tvMainTip.visibility = View.VISIBLE tvMainTip.visibility = View.VISIBLE
//根据测试成绩处理 //根据测试成绩处理
vm.courseDetail.st_before = 81.0
vm.courseDetail.st_before.let { vm.courseDetail.st_before.let {
when {
it == -1.0 -> { //未测
when (it) {
AppConstants.NOT_DOING -> { //未测
tvMainTip.setText(R.string.test_total_after_tip_1) tvMainTip.setText(R.string.test_total_after_tip_1)
button2.run { button2.run {
setText(R.string.test_type_after_total) setText(R.string.test_type_after_total)
} }
} }
} }
it < AppConstants.TEST_SCORE_LEVEL_1 -> { //测试未通过
tvMainTip.setHtml("你还未通过学后总测试,上次得分: <font color=\'#F26255\'>$it</font>")

button1.run {
visibility = View.VISIBLE
setText(R.string.continue_learn)
click { view ->
continueLearn(view)
}
}
button2.run {
setText(R.string.start_test)
click { view ->
startTest(view)
}
else -> { //有成绩,直接完成
if (it >= AppConstants.TEST_SCORE_LEVEL_2) {
tvMainTip.setHtml("你已完成学后总测试,上次得分: <font color=\'#5082E6\'>$it</font>")
}else {
tvMainTip.setHtml("你已完成学后总测试,上次得分: <font color=\'#F26255\'>$it</font>")
} }
}

it >= AppConstants.TEST_SCORE_LEVEL_1 -> { //测试通过
tvMainTip.setHtml("你已通过学后总测试,上次得分: <font color=\'#5082E6\'>$it</font>")
tvTips.visibility = View.VISIBLE tvTips.visibility = View.VISIBLE
tvCountTip.visibility = View.GONE tvCountTip.visibility = View.GONE
button1.run { button1.run {
} }
} }
tvClearLearnRecord.visibility = View.VISIBLE tvClearLearnRecord.visibility = View.VISIBLE
tvClearLearnRecord.click {
(activity as CoursePackMainActivity).showCourseRelearnDialog()
}
} }
} }
} }
} }
} }

/**继续学习*/ /**继续学习*/
private fun continueLearn(view: View) {
private fun continueLearn(view : View) {
(requireParentFragment() as CourseMainFragment).changeFragment(1) (requireParentFragment() as CourseMainFragment).changeFragment(1)
} }
/** 开始测试 : 生成测试题,跳转到测试 /** 开始测试 : 生成测试题,跳转到测试
* *
*/ */
private fun startTest(view: View) {
private fun startTest(view : View) {
//生成数据 //生成数据
val examData = ExamData(vm.course.subjectId, totalTestType, vm.course.courseTitle, vm.course.courseTitle).apply { val examData = ExamData(vm.course.subjectId, totalTestType, vm.course.courseTitle, vm.course.courseTitle).apply {
coursePackId = vm.course.coursePackId coursePackId = vm.course.coursePackId
} }
(parentFragment as CourseMainFragment).startExam(examData) (parentFragment as CourseMainFragment).startExam(examData)
} }
} }

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

import android.os.Bundle import android.os.Bundle
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.suliang.common.base.activity.BaseActivityVM import com.suliang.common.base.activity.BaseActivityVM
import com.suliang.common.eventbus.LiveDataBus
import com.suliang.common.extension.loadFragment import com.suliang.common.extension.loadFragment
import com.suliang.common.extension.showHideFragment import com.suliang.common.extension.showHideFragment
import com.suliang.common.util.LogUtil
import com.xkl.cdl.R import com.xkl.cdl.R
import com.xkl.cdl.databinding.ActivityMainBinding import com.xkl.cdl.databinding.ActivityMainBinding
import com.xkl.cdl.module.m_center_learn.LearnCenterFragment import com.xkl.cdl.module.m_center_learn.LearnCenterFragment
import com.xkl.cdl.module.m_my.MyFragment import com.xkl.cdl.module.m_my.MyFragment
import com.xkl.cdl.module.m_service_center.ServiceCenterFragment import com.xkl.cdl.module.m_service_center.ServiceCenterFragment
import com.xkl.cdl.module.m_statics.StaticsFragment import com.xkl.cdl.module.m_statics.StaticsFragment
import com.xkl.cdl.module.splash.SplashActivity


/** /**
* 主界面、控制切换子Fragment * 主界面、控制切换子Fragment

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

package com.xkl.cdl.util package com.xkl.cdl.util


import com.suliang.common.util.LogUtil
import com.xkl.cdl.data.bean.BaseWord import com.xkl.cdl.data.bean.BaseWord
import java.io.* import java.io.*
import java.util.* import java.util.*
currentCorrectMap[key] = true currentCorrectMap[key] = true
} }
if (isLearnFirst) currentIsError = false if (isLearnFirst) currentIsError = false
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")
//开始进行数据扩容替换 //开始进行数据扩容替换
replaceErrorToNull(item) //替换已有错误为空 replaceErrorToNull(item) //替换已有错误为空
insertErrorWord(item, false) //开始插入 insertErrorWord(item, false) //开始插入
* @return true在 false不存在 * @return true在 false不存在
*/ */
fun isInExamErrorMap(key : String) : Boolean { fun isInExamErrorMap(key : String) : Boolean {
return examErrorsMap?.getOrDefault(key, null) ?: false
return examErrorsMap?.getOrDefault(key, false) ?: false
} }
/** 是否是在学习的错误列表中 */ /** 是否是在学习的错误列表中 */

+ 18
- 12
app/src/main/res/layout/dialog_lesson_learn.xml View File

android:visibility="gone" android:visibility="gone"
/> />
<!--所有布局id--> <!--所有布局id-->
<!-- <androidx.constraintlayout.widget.Group-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- app:constraint_referenced_ids="iv_close,tv_score,tv_tip,tv_title,-->
<!-- tv_lesson_name,tv_count_time,,tv_tip_1,-->
<!-- inc_statistics_number,-->
<!-- tv_learn_over_tip,-->
<!-- tv_learn_over_for_after_count_time,-->
<!-- tv_top,tv_top_1,tv_left,vSplit"-->
<!-- tools:visibility="gone"-->
<!-- />-->
<!-- <androidx.constraintlayout.widget.Group
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="iv_close,tv_score,tv_tip,tv_title,
tv_lesson_name,tv_count_time,,tv_tip_1,
inc_statistics_number,
tv_learn_over_tip,
tv_learn_over_for_after_count_time,
tv_top,tv_top_1,tv_left,vSplit"
tools:visibility="gone"
/>
<androidx.constraintlayout.widget.Group
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="tv_title,tv_tip_1,tv_lesson_name,tv_count_time,tv_left,vSplit"
/>-->

<!-- &lt;!&ndash;学前总测试控制显示的布局id&ndash;&gt;--> <!-- &lt;!&ndash;学前总测试控制显示的布局id&ndash;&gt;-->
<!-- <androidx.constraintlayout.widget.Group--> <!-- <androidx.constraintlayout.widget.Group-->
<!-- android:id="@+id/group_total_test"--> <!-- android:id="@+id/group_total_test"-->
/> --> /> -->




<!--课时正常学习结束的布局组 -->
<!-- 课时完全结束的点击显示弹窗的布局组 -->
<!-- <androidx.constraintlayout.widget.Group--> <!-- <androidx.constraintlayout.widget.Group-->
<!-- android:layout_width="wrap_content"--> <!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"--> <!-- android:layout_height="wrap_content"-->

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

android:layout_marginStart="28dp" android:layout_marginStart="28dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:text="@string/error" android:text="@string/error"
android:textColor="@color/gray_2"
android:textColor="@color/main_text_color"
android:textSize="@dimen/smallerSize" android:textSize="@dimen/smallerSize"
app:layout_constraintStart_toEndOf="@+id/guideline_1" app:layout_constraintStart_toEndOf="@+id/guideline_1"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />

+ 3
- 0
app/src/main/res/values/strings.xml View File

<string name="auto_playing">自动播放中···</string> <string name="auto_playing">自动播放中···</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_content">清空后你的学习记录将不可恢复,请谨慎操作</string>
<string name="course_relearn_sure">确认清空</string>


</resources> </resources>

+ 2
- 0
build.gradle View File

grpc_android: "io.grpc:grpc-android:1.27.0", grpc_android: "io.grpc:grpc-android:1.27.0",
grpc_protobuf: "io.grpc:grpc-protobuf:1.27.0", grpc_protobuf: "io.grpc:grpc-protobuf:1.27.0",
grpc_stub: "io.grpc:grpc-stub:1.27.0", grpc_stub: "io.grpc:grpc-stub:1.27.0",
//liveEventBus https://github.com/JeremyLiao/LiveEventBus
liveEventBus : "io.github.jeremyliao:live-event-bus-x:1.8.0"
] ]





+ 2
- 0
lib/common/build.gradle View File

coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
//MMKV //MMKV
api customDependencies.MMKV api customDependencies.MMKV
// liveEventBus
api customDependencies.liveEventBus







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

* Describe: 打印生命周期的Activity * Describe: 打印生命周期的Activity
*/ */
open class LifecycleLogActivity : AppCompatActivity() { open class LifecycleLogActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LogUtil.e("onCreate()")
}
override fun onRestart() {
super.onRestart()
LogUtil.e("onRestart()")
}
override fun onStart() {
super.onStart()
LogUtil.e("onStart()")
}
override fun onResume() {
super.onResume()
LogUtil.e("onResume()")
}
override fun onPause() {
super.onPause()
LogUtil.e("onPause()")
}
override fun onStop() {
super.onStop()
LogUtil.e("onStop()")
}
override fun onDestroy() {
super.onDestroy()
LogUtil.e("onDestroy()")
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
LogUtil.e("onSaveInstanceState()")
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
LogUtil.e("onRestoreInstanceState()")
}
// override fun onCreate(savedInstanceState: Bundle?) {
// super.onCreate(savedInstanceState)
// LogUtil.e("onCreate()")
// }
//
// override fun onRestart() {
// super.onRestart()
// LogUtil.e("onRestart()")
// }
//
// override fun onStart() {
// super.onStart()
// LogUtil.e("onStart()")
// }
//
// override fun onResume() {
// super.onResume()
// LogUtil.e("onResume()")
// }
//
// override fun onPause() {
// super.onPause()
// LogUtil.e("onPause()")
// }
//
// override fun onStop() {
// super.onStop()
// LogUtil.e("onStop()")
// }
//
// override fun onDestroy() {
// super.onDestroy()
// LogUtil.e("onDestroy()")
// }
//
// override fun onSaveInstanceState(outState: Bundle) {
// super.onSaveInstanceState(outState)
// LogUtil.e("onSaveInstanceState()")
// }
//
// override fun onRestoreInstanceState(savedInstanceState: Bundle) {
// super.onRestoreInstanceState(savedInstanceState)
// LogUtil.e("onRestoreInstanceState()")
// }
} }

+ 1
- 0
lib/common/src/main/java/com/suliang/common/base/fragment/BaseFragmentVM.kt View File



override fun initFirst() { override fun initFirst() {
vm = initViewModel() vm = initViewModel()
lifecycle.addObserver(vm)
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) {

+ 7
- 7
lib/common/src/main/java/com/suliang/common/base/viewmodel/BaseViewModel.kt View File

open class BaseViewModel : ViewModel(), ViewModelLifecycle, ViewBehavior { open class BaseViewModel : ViewModel(), ViewModelLifecycle, ViewBehavior {
override fun onCreate(owner: LifecycleOwner) { override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner) super.onCreate(owner)
LogUtil.i("${javaClass.name} onCreate() ")
LogUtil.i("${javaClass.name}_${hashCode()} onCreate() ")
} }


override fun onStart(owner: LifecycleOwner) { override fun onStart(owner: LifecycleOwner) {
super.onStart(owner) super.onStart(owner)
LogUtil.i("${javaClass.name} onStart() ")
LogUtil.i("${javaClass.name}_${hashCode()} onStart() ")
} }


override fun onResume(owner: LifecycleOwner) { override fun onResume(owner: LifecycleOwner) {
super.onResume(owner) super.onResume(owner)
LogUtil.i("${javaClass.name} onResume() ")
LogUtil.i("${javaClass.name}_${hashCode()} onResume() ")
} }


override fun onPause(owner: LifecycleOwner) { override fun onPause(owner: LifecycleOwner) {
super.onPause(owner) super.onPause(owner)
LogUtil.i("${javaClass.name} onPause() ")
LogUtil.i("${javaClass.name}_${hashCode()} onPause() ")
} }


override fun onStop(owner: LifecycleOwner) { override fun onStop(owner: LifecycleOwner) {
super.onStop(owner) super.onStop(owner)
LogUtil.i("${javaClass.name} onStop() ")
LogUtil.i("${javaClass.name}_${hashCode()} onStop() ")
} }


override fun onDestroy(owner: LifecycleOwner) { override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner) super.onDestroy(owner)
LogUtil.i("${javaClass.name} onDestroy() ")
LogUtil.i("${javaClass.name}_${hashCode()} onDestroy() ")
} }


override fun onCleared() { override fun onCleared() {
super.onCleared() super.onCleared()
LogUtil.i("${javaClass.name} onCleared() ")
LogUtil.i("${javaClass.name}_${hashCode()} onCleared() ")
} }

+ 87
- 47
lib/common/src/main/java/com/suliang/common/eventbus/LiveDataBus.kt View File

package com.suliang.common.eventbus package com.suliang.common.eventbus


import android.os.Looper
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData


/** /**
* author suliang * author suliang
* create 2022/3/15 10:01 * create 2022/3/15 10:01
* Describe: 事件总线,这条总线把任何类中的数据直接传递到activity 或是 fragment 上 * Describe: 事件总线,这条总线把任何类中的数据直接传递到activity 或是 fragment 上
* https://blog.csdn.net/lp131452064/article/details/82736296
* https://juejin.cn/post/6844903768459329544
*/ */
@Deprecated("this class is deprecated" ,replaceWith = ReplaceWith("LiveEventBus"),level = DeprecationLevel.ERROR)
object LiveDataBus { object LiveDataBus {
private val bus = mutableMapOf<String, MutableLiveData<Any>>()
/**
* 发送事件
* @param key String
* @param value T
* @param isSticky 是否是粘性事件,默认为false
*/
fun <T : Any> postEvent(key:String, value:T, isSticky : Boolean = false){
if (Looper.getMainLooper().thread == Thread.currentThread()){
subscribeEvent<T>(key,isSticky).value = value
}else{
subscribeEvent<T>(key,isSticky).postValue(value)
}
}
/**
* 订阅事件
* @param key bus中的key
* @param isSticky 是否是粘性事件,默认为false
*/
fun <T:Any> subscribeEvent(key:String,isSticky:Boolean = false): MutableLiveData<T> {
if (!bus.containsKey(key)){
if (isSticky){
bus[key] = MutableLiveData()
}else{
bus[key] = NonStickyMutableLivedata()
}
}
return bus[key] as MutableLiveData<T>
}
fun remove(key : String) {
bus.remove(key)
}
fun clearAll() {
bus.clear()
}




// @SuppressWarnings("UNCHECKED_CAST")
// @Synchronized
// fun <T> with(key : String) : MutableLiveData<T> {
// if (!bus.containsKey(key)) {
// bus[key] = MutableLiveData()
// }
// return bus[key] as MutableLiveData<T>
// }
//
// @SuppressWarnings("UNCHECKED_CAST")
// @Synchronized
// fun <T> withNonSticky(key : String) : MutableLiveData<T> {
// if (!bus.containsKey(key)) {
// bus[key] = NonStickyMutableLivedata()
// }
// return bus[key] as MutableLiveData<T>
// }




private val bus = mutableMapOf<String,MutableLiveData<Any>>()


// /** // /**
// * 注册订阅者 // * 注册订阅者


//java使用传入泛型class, kotlin使用可直接传入泛型,不需要class类型 //java使用传入泛型class, kotlin使用可直接传入泛型,不需要class类型


@SuppressWarnings("UNCHECKED_CAST")
@Synchronized
fun <T> with(key:String) : MutableLiveData<T>{
if (!bus.containsKey(key)){
bus[key] = MutableLiveData()
}
return bus[key] as MutableLiveData<T>
}
@SuppressWarnings("UNCHECKED_CAST")
@Synchronized
fun <T> withNonSticky(key:String) : MutableLiveData<T>{
if (!bus.containsKey(key)){
bus[key] = NonStickyMutableLiveData()
}
return bus[key] as MutableLiveData<T>
}



@SuppressWarnings("UNCHECKED_CAST")
@Synchronized
fun <T> with(key:String,type: Class<T>) : MutableLiveData<T>{
if (!bus.containsKey(key)){
bus[key] = MutableLiveData()
}
return bus[key] as MutableLiveData<T>
}
@SuppressWarnings("UNCHECKED_CAST")
@Synchronized
fun <T> withNonSticky(key:String,type: Class<T>) : MutableLiveData<T>{
if (!bus.containsKey(key)){
bus[key] = NonStickyMutableLiveData()
}
return bus[key] as MutableLiveData<T>
}

fun remove(key:String){
bus.remove(key)
}


fun clearAll(){
bus.clear()
}




// @SuppressWarnings("UNCHECKED_CAST")
// @Synchronized
// fun <T> with(key:String,type: Class<T>) : MutableLiveData<T>{
// if (!bus.containsKey(key)){
// bus[key] = MutableLiveData()
// }
// return bus[key] as MutableLiveData<T>
// }
// @SuppressWarnings("UNCHECKED_CAST")
// @Synchronized
// fun <T> withNonSticky(key:String,type: Class<T>) : MutableLiveData<T>{
// if (!bus.containsKey(key)){
// bus[key] = NonStickyMutableLiveData()
// }
// return bus[key] as MutableLiveData<T>
// }


} }

lib/common/src/main/java/com/suliang/common/eventbus/NonStickyMutableLiveData.kt → lib/common/src/main/java/com/suliang/common/eventbus/NonStickyMutableLivedata.kt View File

* author suliang * author suliang
* create 2022/3/15 10:51 * create 2022/3/15 10:51
* Describe: 非粘性LiveData ,需要先设置监听再发送数据,即后注册的监听收不到以前的监听 * Describe: 非粘性LiveData ,需要先设置监听再发送数据,即后注册的监听收不到以前的监听
* 事件总线的LiveData 非粘性事件传false(刚注册时如果有事件则不发送) 粘性事件传true
* @param isSticky false非粘性事件 true粘性事件
*/ */
class NonStickyMutableLiveData<T> : MutableLiveData<T>() {

// private var stickFlag : Boolean = false

internal class NonStickyMutableLivedata<T> : MutableLiveData<T>() {
// override fun observe(owner : LifecycleOwner, observer : Observer<in T>) {
// super.observe(owner, ObserverWrapper<T>(observer))
// }
//
// class ObserverWrapper<T>(var observer:Observer<in T>) : Observer<T> {
// var isSticky = false
// override fun onChanged(t : T) {
// if (isSticky){
// observer.onChanged(t)
// }else{
// isSticky = true
// }
// }
// }
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner, observer) super.observe(owner, observer)
hook(observer) hook(observer)
// if (!stickFlag){
// hook(observer)
// stickFlag = true
// }
} }

private fun hook(observer: Observer<in T>) = try {
override fun removeObserver(observer : Observer<in T>) {
super.removeObserver(observer)
}
private fun hook(observer : Observer<in T>) = try {
//得到 mLastVersion //得到 mLastVersion
//获取到LiveData的类中的mObserver对象 //获取到LiveData的类中的mObserver对象
// SafeIterableMap<Observer<? super T> , ObserverWrapper> mObservers // SafeIterableMap<Observer<? super T> , ObserverWrapper> mObservers
//得到map对应的class对象 //得到map对应的class对象
val mObserversClass = mObserVersObject.javaClass val mObserversClass = mObserVersObject.javaClass
//获取到mObservers对象的get方法 //获取到mObservers对象的get方法
val get:Method = mObserversClass.getDeclaredMethod("get", Any::class.java)
val get : Method = mObserversClass.getDeclaredMethod("get", Any::class.java)
get.isAccessible = true get.isAccessible = true
//执行get方法,获取到对象 //执行get方法,获取到对象
val invokeEntry = get.invoke(mObserVersObject, observer) val invokeEntry = get.invoke(mObserVersObject, observer)
val observerWrapper = if (invokeEntry is Map.Entry<*, *>) { val observerWrapper = if (invokeEntry is Map.Entry<*, *>) {
invokeEntry.value invokeEntry.value
} else throw NullPointerException("observerWrapper is null") } else throw NullPointerException("observerWrapper is null")
//得到observerWrapper的对象,编译擦除问题会引起多态冲突,所以用getSupperClass //得到observerWrapper的对象,编译擦除问题会引起多态冲突,所以用getSupperClass
//getClass 返回对应的当前正在运行是的类所对应的 //getClass 返回对应的当前正在运行是的类所对应的
val mLastVersion = observerWrapper!!::class.java.superclass.getDeclaredField("mLastVersion") val mLastVersion = observerWrapper!!::class.java.superclass.getDeclaredField("mLastVersion")
mVersion.isAccessible = true mVersion.isAccessible = true
//把mVersion数据填入到mLastVersion中 //把mVersion数据填入到mLastVersion中
val mVersionValue = mVersion.get(this) val mVersionValue = mVersion.get(this)
mLastVersion.set(observerWrapper,mVersionValue)
}catch (e: Exception){
mLastVersion.set(observerWrapper, mVersionValue)
} catch (e : Exception) {
e.printStackTrace() e.printStackTrace()
} }


} }

+ 1
- 1
lib/common/src/main/java/com/suliang/common/extension/ViewClickExtension.kt View File

} }
this.setOnTouchListener{ view , motionEvent -> this.setOnTouchListener{ view , motionEvent ->
when(motionEvent.action){ when(motionEvent.action){
MotionEvent.ACTION_DOWN -> view.alpha = 0.7f
MotionEvent.ACTION_DOWN -> view.alpha = 0.8f
MotionEvent.ACTION_UP -> view.alpha = 1f MotionEvent.ACTION_UP -> view.alpha = 1f
else -> view.alpha = 1f else -> view.alpha = 1f
} }

+ 17
- 2
lib/common/src/main/java/com/suliang/common/util/file/FileUtil.kt View File

closeStream(out) closeStream(out)
return result return result
} }

/***
* 从对象获取字节数组
* @param data Any 对象,需要实现序列化
* @return ByteArray 字节数组
* @throws Exception
*/
@Throws(Exception::class) @Throws(Exception::class)
fun objectToBytes(data: Any): ByteArray {
fun objectToBytes(data: Serializable): ByteArray {
var oos: ObjectOutputStream? = null var oos: ObjectOutputStream? = null
return try { return try {
val bos = ByteArrayOutputStream() val bos = ByteArrayOutputStream()
oos?.close() oos?.close()
} }
} }
/**
* 从字节数组获取对象
* @param data ByteArray
* @return Any
*/
fun bytesToObject(data:ByteArray): Any{
return ObjectInputStream(ByteArrayInputStream(data)).readObject()
}


/** /**
* 获取文件大小 * 获取文件大小

+ 1
- 2
lib/common/src/main/res/values/strings.xml View File

<resources> <resources>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>

</resources> </resources>

Loading…
Cancel
Save