Browse Source

数据对接

master
suliang 2 years ago
parent
commit
72459a5e77
47 changed files with 1048 additions and 314 deletions
  1. 12
    2
      .idea/misc.xml
  2. 43
    0
      README.md
  3. BIN
      app/libs/mobile_cache.aar
  4. 48
    0
      app/src/main/java/com/xkl/cdl/HookMobileCache.java
  5. 2
    2
      app/src/main/java/com/xkl/cdl/adapter/AdapterCoursePackWithLearCenter.kt
  6. 170
    0
      app/src/main/java/com/xkl/cdl/adapter/AdapterCoursePackWithMemo.kt
  7. 16
    0
      app/src/main/java/com/xkl/cdl/data/bean/MemoCoursePack.kt
  8. 7
    7
      app/src/main/java/com/xkl/cdl/data/bean/course/CourseDetail.kt
  9. 13
    10
      app/src/main/java/com/xkl/cdl/data/bean/course/CoursePack.kt
  10. 47
    0
      app/src/main/java/com/xkl/cdl/data/manager/CourseManager.kt
  11. 2
    2
      app/src/main/java/com/xkl/cdl/data/manager/db/DBCourseManager.kt
  12. 75
    19
      app/src/main/java/com/xkl/cdl/data/repository/DataRepository.kt
  13. 15
    5
      app/src/main/java/com/xkl/cdl/module/XKLApplication.kt
  14. 1
    2
      app/src/main/java/com/xkl/cdl/module/learn/LearnCReadingViewModel.kt
  15. 23
    3
      app/src/main/java/com/xkl/cdl/module/learn/LearnCVideoViewModel.kt
  16. 0
    1
      app/src/main/java/com/xkl/cdl/module/learn/LearnExamActivity.kt
  17. 3
    8
      app/src/main/java/com/xkl/cdl/module/learn/LearnExamViewModel.kt
  18. 1
    1
      app/src/main/java/com/xkl/cdl/module/learn/LearnWordActivity.kt
  19. 2
    8
      app/src/main/java/com/xkl/cdl/module/learn/LearnWordViewModel.kt
  20. 1
    12
      app/src/main/java/com/xkl/cdl/module/m_center_learn/CoursePackMainActivity.kt
  21. 9
    0
      app/src/main/java/com/xkl/cdl/module/m_center_learn/CoursePackMainActivityViewModel.kt
  22. 2
    17
      app/src/main/java/com/xkl/cdl/module/m_center_learn/LearnCenterFragment.kt
  23. 6
    7
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseLessonFragment.kt
  24. 4
    4
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseMainFragment.kt
  25. 70
    49
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseMainFragmentViewModel.kt
  26. 84
    15
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseReviewFragment.kt
  27. 31
    51
      app/src/main/java/com/xkl/cdl/module/m_memo/MemoFragment.kt
  28. 56
    0
      app/src/main/java/com/xkl/cdl/module/m_memo/MemoFragmentViewModel.kt
  29. 3
    6
      app/src/main/java/com/xkl/cdl/module/main/MainActivity.kt
  30. 5
    14
      app/src/main/java/com/xkl/cdl/module/main/MainActivityViewModel.kt
  31. 30
    50
      app/src/main/java/com/xkl/cdl/module/splash/SplashActivity.kt
  32. 24
    3
      app/src/main/res/layout/fragment_course_review.xml
  33. 66
    8
      app/src/main/res/layout/fragment_memo.xml
  34. 3
    3
      app/src/main/res/layout/include_main_learn_center_course_type_title.xml
  35. 74
    0
      app/src/main/res/layout/item_memo.xml
  36. 3
    3
      app/src/main/res/layout/main_item_coursepack.xml
  37. 1
    0
      app/src/main/res/values/strings.xml
  38. 12
    0
      app/svg/drawable/ic_course_composition.xml
  39. 0
    0
      app/svg/drawable/ic_course_discern.xml
  40. 12
    0
      app/svg/drawable/ic_course_literacy.xml
  41. 14
    0
      app/svg/drawable/ic_course_pinying.xml
  42. 18
    0
      app/svg/drawable/ic_course_soundmark.xml
  43. 0
    0
      app/svg/drawable/ic_course_spell.xml
  44. 1
    2
      app/svg/drawable/ic_course_spoken.xml
  45. 0
    0
      app/svg/drawable/ic_course_voice.xml
  46. 26
    0
      lib/common/src/main/java/com/suliang/common/extension/RxScheduler.kt.kt
  47. 13
    0
      lib/common/src/main/java/com/suliang/common/util/DateUtil.kt

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

<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_lesson.xml" value="0.33" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_lesson.xml" value="0.33" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_main.xml" value="0.4979166666666667" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_main.xml" value="0.4979166666666667" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_pack.xml" value="0.34427083333333336" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_pack.xml" value="0.34427083333333336" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_review.xml" value="0.12802768166089964" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_review.xml" value="0.67" />
<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_memo.xml" value="0.25" />
<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" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_course_lesson.xml" value="0.4785615491009682" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_course_lesson.xml" value="0.4785615491009682" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_empty.xml" value="0.4979166666666667" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_empty.xml" value="0.4979166666666667" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_historical_route.xml" value="0.4859375" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_historical_route.xml" value="0.4859375" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_memo.xml" value="0.478125" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_spell_single_word.xml" value="0.23632218844984804" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_spell_single_word.xml" value="0.23632218844984804" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_task_image.xml" value="0.23353596757852077" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_task_image.xml" value="0.23353596757852077" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_video_adapter.xml" value="0.67" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_video_adapter.xml" value="0.67" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/textview_only.xml" value="0.49773550724637683" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/textview_only.xml" value="0.49773550724637683" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/statedrawable/drawable/ic_nav_learn_center.xml" value="0.44166666666666665" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/statedrawable/drawable/ic_nav_learn_center.xml" value="0.44166666666666665" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_arrow_right.xml" value="0.29814814814814816" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_arrow_right.xml" value="0.29814814814814816" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_course_composition.xml" value="0.30092592592592593" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_course_discern.xml" value="0.30092592592592593" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_course_literacy.xml" value="0.2972222222222222" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_course_pinying.xml" value="0.30092592592592593" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_course_soundmark.xml" value="0.35555555555555557" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_course_spell.xml" value="0.30092592592592593" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_course_spoken.xml" value="0.30092592592592593" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_course_voice.xml" value="0.35555555555555557" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_discern.xml" value="0.5061538461538462" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_discern.xml" value="0.5061538461538462" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_icon___.xml" value="0.287962962962963" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_icon___.xml" value="0.287962962962963" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_menu.xml" value="0.4036458333333333" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_menu.xml" value="0.4036458333333333" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_more.xml" value="0.35555555555555557" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_learn_center.xml" value="0.44166666666666665" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_learn_center.xml" value="0.44166666666666665" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_memo.xml" value="0.21574074074074073" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_memo.xml" value="0.21574074074074073" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_my.xml" value="0.44166666666666665" /> <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_my.xml" value="0.44166666666666665" />

+ 43
- 0
README.md View File

// jniLibs.srcDirs = ['libs'] // jniLibs.srcDirs = ['libs']
// } // }
// } // }


自建数据库
实现口语收藏、作文笔记收藏
CourseMainFragmentViewModel.loadmain()


计算当前词汇量,课程完成/重学的时候调用一下;重学直接程序帮忙调用
public native long calcCurrentVocabulary(long projectID) throws Exception;
计算学习效率(综合学习效率和课程效率)
public native void calcEfficiency(projectID, packID, courseID) throws Exception;

课程学习、时长信息
public native String courseDetail(projectID, packID, courseID ) throws Exception;
速记课程排序列表 进度
public native String courseSorted(long projectID ) throws Exception;

词条数统计 近90天词条数统计
public native String entityCountList(long projectID, long gradeID ) throws Exception;
获取已学课程ID列表
public native String getLearnedCourseIDList() throws Exception;
获取正确/错误词汇列表
public native String getWordList(long projectID, long packID, long courseID, long categroy ) throws Exception;

保存数据struct.Record
public native void parseData(String var1) throws Exception;
重学
public native void relearn(long var1, long var3, long var5, long var7, long var9, double var11, double var13, double var15) throws Exception;
保存已学课程ID
public native void setLearnedCourseID(long var1, long var3, long var5) throws Exception;
设置视频播放点
public native void setVideoPoint(long var1, long var3, long var5, long var7, String var9) throws Exception;
统计信息
public native String statisticsCenter(long var1, long var3) throws Exception;

设置课程学习点,同时会更新对应lesson的进度点
public native void setLearnPoint(long var1, long var3, long var5, long var7, long var9, long var11) throws Exception;
GetVideoPoint 获取视频播放点
public native String getVideoPoint(long var1, long var3, long var5, long var7) throws Exception;
public native void gameFinished(long var1, long var3, long var5, long var7, long var9) throws Exception;
public native void changeLocker(long var, long var3, long var5, long var7, long var9, boolean var11) throws Exception;
public native void delLearnedCourseID(long var1, long var3, long var5) throws Exception;
public native void destroy();

BIN
app/libs/mobile_cache.aar View File


+ 48
- 0
app/src/main/java/com/xkl/cdl/HookMobileCache.java View File

package com.xkl.cdl;

import com.suliang.common.util.LogUtil;
import com.xkl.cdl.module.XKLApplication;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import mobile_cache.MobileCache;

/**
* author suliang
* create 2022/6/1 10:58
* Describe:
*/
public class HookMobileCache {

public void hook(){
try{
MobileCache mobileCache = XKLApplication.Companion.getMobileCache();
Han han = new Han(mobileCache);
Object o = Proxy.newProxyInstance(han.getClass().getClassLoader(), mobileCache.getClass().getInterfaces(), han);

o.hashCode();

}catch (Exception e){
e.printStackTrace();
}
}
class Han implements InvocationHandler{

private Object mobileObject ;

public Han(Object mobileObject) {
this.mobileObject = mobileObject;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理前添加操作
LogUtil.INSTANCE.e(method.getName());
Object value = method.invoke(mobileObject,args);
LogUtil.INSTANCE.e(value.toString());
return value;
}
}
}

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

AppConstants.COURSE_TYPE_ENGLISH_VOICE -> { AppConstants.COURSE_TYPE_ENGLISH_VOICE -> {
imageViewTypeVoice.run { imageViewTypeVoice.run {
visibility = View.VISIBLE visibility = View.VISIBLE
setImageResource(R.drawable.ic_voice)
setImageResource(R.drawable.ic_course_voice)
} }
} }
AppConstants.COURSE_TYPE_ENGLISH_SPOKEN -> { AppConstants.COURSE_TYPE_ENGLISH_SPOKEN -> {
imageViewTypeVoice.run{ imageViewTypeVoice.run{
visibility = View.VISIBLE visibility = View.VISIBLE
setImageResource(R.drawable.ic_spoken)
setImageResource(R.drawable.ic_course_spoken)
} }
} }
} }

+ 170
- 0
app/src/main/java/com/xkl/cdl/adapter/AdapterCoursePackWithMemo.kt View File

package com.xkl.cdl.adapter

import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.graphics.Color
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.core.content.ContextCompat
import com.google.android.material.badge.BadgeDrawable
import com.google.android.material.badge.BadgeUtils
import com.google.android.material.imageview.ShapeableImageView
import com.suliang.common.base.adapter.BaseAdapterViewHolder
import com.suliang.common.base.adapter.BaseRVAdapterVM
import com.suliang.common.databinding.ItemEmptyBinding
import com.xkl.cdl.R
import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.bean.MemoCoursePack
import com.xkl.cdl.data.bean.course.Course
import com.xkl.cdl.data.binding.BindingAdapter
import com.xkl.cdl.databinding.ItemMemoBinding
import com.xkl.cdl.module.m_memo.MemoFragmentViewModel

/**
* author suliang
* create 2022/5/30 16:56
* Describe: 备忘本适配器
*/
class AdapterCoursePackWithMemo(viewModel : MemoFragmentViewModel) :
BaseRVAdapterVM<MemoCoursePack, MemoFragmentViewModel>(viewModel) {
private val badgeDrawable : BadgeDrawable by lazy {
BadgeDrawable.create(context).apply {
badgeGravity = BadgeDrawable.TOP_END
maxCharacterCount = 3
backgroundColor = ContextCompat.getColor(context, R.color.red_1)
badgeTextColor = ContextCompat.getColor(context, R.color.white)
}
}
override fun onBindEmptyViewHolder(holder : BaseAdapterViewHolder) {
(holder.binding as ItemEmptyBinding).run {
//根据监听,显示具体的内容
vm.etSearchLiveData.value?.let {
if (it.isNotEmpty()) {
imgEmpty.setImageResource(R.mipmap.empty_nothing_search)
tvContent.text = "没有搜索到任何内容"
} else {
imgEmpty.setImageResource(R.mipmap.empty_nothing)
tvContent.text = "没有数据"
}
}
}
}
override fun coverViewHolder(parent : ViewGroup, viewType : Int) : BaseAdapterViewHolder {
return BaseAdapterViewHolder(inflateBinding(parent, R.layout.item_memo))
}
override fun onBindVH(holder : BaseAdapterViewHolder, position : Int) {
getItem(position).let { item ->
(holder.binding as ItemMemoBinding).run {
//图片
BindingAdapter.imageByteArray(imgCoursePackCover, item.coursePack.cover)
//文字
tvCoursePackName.text = item.coursePack.coursePackName
//图标与角标文字
item.coursePack.childrenCourses.forEachIndexed { index : Int, course : Course ->
when (course.courseType) {
AppConstants.COURSE_TYPE_ENGLISH_DISCERN -> setImageIcon(imgCourseIcon1,course.courseType,item.coursePackChildrenReviewNumber[index].size)
AppConstants.COURSE_TYPE_ENGLISH_SPELL -> setImageIcon(imgCourseIcon2,course.courseType,item.coursePackChildrenReviewNumber[index].size)
AppConstants.COURSE_TYPE_ENGLISH_VOICE -> setImageIcon(imgCourseIcon3,course.courseType,item.coursePackChildrenReviewNumber[index].size)
//其他只有一个课程类型
else -> {
setImageIcon(imgCourseIcon1,course.courseType,item.coursePackChildrenReviewNumber[index].size)
imgCourseIcon2.visibility = View.INVISIBLE
imgCourseIcon3.visibility = View.INVISIBLE
imgCourseIcon1.visibility = View.VISIBLE
}
}
}
}
}
}
/**
* 设置图片 角标 与 背景,有复习number数据需要设置角标与背景,否则不需要
* @param imageView ShapeableImageView 图片
* @param courseType Int 课程类型
* @param reviewNumber Int 复习数量
*/
@SuppressLint("UnsafeOptInUsageError")
private fun setImageIcon(imageView : ShapeableImageView, courseType : Int, reviewNumber : Int) {
var color : Int = Color.parseColor("#00000000")
//取消角标
BadgeUtils.detachBadgeDrawable(badgeDrawable,imageView)
when (reviewNumber) {
//没有数据
0 -> {
imageView.strokeColor = ColorStateList.valueOf(color)
//设置图标: 灰色,没有背景
imageView.setImageResource(getCourseIcon(courseType))
imageView.imageTintList = ColorStateList.valueOf(Color.parseColor("#9A9EB3"))
}
//有复习数据
else -> {
when (courseType) {
AppConstants.COURSE_TYPE_ENGLISH_DISCERN -> color = Color.parseColor("#1A5082E6")
AppConstants.COURSE_TYPE_ENGLISH_SPELL -> color = Color.parseColor("#1AF26255")
AppConstants.COURSE_TYPE_ENGLISH_SPOKEN, AppConstants.COURSE_TYPE_ENGLISH_VOICE -> color = Color.parseColor(
"#1A52CC52")
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION, AppConstants.COURSE_TYPE_ENGLISH_SOUNDMARK -> color = Color.parseColor(
"#1A8757E6")
AppConstants.COURSE_TYPE_CHINESE_PINYIN -> color = Color.parseColor("#1AEB54D8")
AppConstants.COURSE_TYPE_CHINESE_LITERACY -> color = Color.parseColor("#1AFF8B52")
}
imageView.strokeColor = ColorStateList.valueOf(color)
//设置图标:原图颜色
imageView.setImageResource(getCourseIcon(courseType))
//添加角标
imageView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
@SuppressLint("UnsafeOptInUsageError")
override fun onGlobalLayout() {
BadgeDrawable.create(context).apply {
badgeGravity = BadgeDrawable.TOP_END
number = reviewNumber
maxCharacterCount = 3
backgroundColor = ContextCompat.getColor(context, R.color.red_1)
badgeTextColor = ContextCompat.getColor(context, R.color.white)
BadgeUtils.attachBadgeDrawable(this, imageView)
}
imageView.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
}
}
}
private fun getCourseIcon(courseType : Int) : Int {
return when (courseType) {
AppConstants.COURSE_TYPE_ENGLISH_DISCERN -> R.drawable.ic_course_discern
AppConstants.COURSE_TYPE_ENGLISH_SPELL -> R.drawable.ic_course_spell
AppConstants.COURSE_TYPE_ENGLISH_SPOKEN -> R.drawable.ic_course_spoken
AppConstants.COURSE_TYPE_ENGLISH_VOICE -> R.drawable.ic_course_voice
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> R.drawable.ic_course_composition
AppConstants.COURSE_TYPE_ENGLISH_SOUNDMARK -> R.drawable.ic_course_soundmark
AppConstants.COURSE_TYPE_CHINESE_PINYIN -> R.drawable.ic_course_pinying
AppConstants.COURSE_TYPE_CHINESE_LITERACY -> R.drawable.ic_course_literacy
else -> 0
}
}
private fun getCourseIconOriginColor(courseType : Int) : Int {
return when (courseType) {
AppConstants.COURSE_TYPE_ENGLISH_DISCERN -> Color.parseColor("#5082E6")
AppConstants.COURSE_TYPE_ENGLISH_SPELL -> Color.parseColor("#F26255")
AppConstants.COURSE_TYPE_ENGLISH_SPOKEN, AppConstants.COURSE_TYPE_ENGLISH_VOICE -> Color.parseColor("#52CC52")
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION, AppConstants.COURSE_TYPE_ENGLISH_SOUNDMARK -> Color.parseColor(
"#8757E6")
AppConstants.COURSE_TYPE_CHINESE_PINYIN -> Color.parseColor("#EB54D8")
AppConstants.COURSE_TYPE_CHINESE_LITERACY -> Color.parseColor("#FF8B52")
else -> 0
}
}
}

+ 16
- 0
app/src/main/java/com/xkl/cdl/data/bean/MemoCoursePack.kt View File

package com.xkl.cdl.data.bean

import com.xkl.cdl.data.bean.course.CoursePack

/**
* author suliang
* create 2022/5/31 9:53
* Describe: 课程包备忘本实体
*/
class MemoCoursePack(val coursePack: CoursePack){
//对应课程的备忘本数据
var coursePackChildrenMemoNumber = mutableListOf<MutableList<String>>()
//对应需要复习的数据
var coursePackChildrenReviewNumber = mutableListOf<MutableList<String>>()
}

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

var st_after: 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 wrong: HashMap<String, Int> = hashMapOf() //错误条目数,key=>{chapter_id}_{lesson_id} value=>条目数量
var right = hashMapOf<String,Long>() //正确条目数,key=>{chapter_id}_{lesson_id} value=>条目数量
var wrong: HashMap<String, Long> = hashMapOf() //错误条目数,key=>{chapter_id}_{lesson_id} value=>条目数量
var lesson_learn_point: HashMap<String, Long> = hashMapOf() //课时学习点 key=>{chapter_id}_{lesson_id} value=>{entity_id) var lesson_learn_point: HashMap<String, Long> = hashMapOf() //课时学习点 key=>{chapter_id}_{lesson_id} value=>{entity_id)
var exam_w_r_list: HashMap<String, Boolean> = hashMapOf() //课程/课时学前测试正确错误列表 key=> {chapter_id}_{lesson_id}_{entity_id} value=>正确 true;错误 false var exam_w_r_list: HashMap<String, Boolean> = hashMapOf() //课程/课时学前测试正确错误列表 key=> {chapter_id}_{lesson_id}_{entity_id} value=>正确 true;错误 false
var course_learn_point: String = "" //课程学习进度点 {chapter_id}_{lesson_id}_{entity_id} var course_learn_point: String = "" //课程学习进度点 {chapter_id}_{lesson_id}_{entity_id}
var vp: HashMap<Long, String> = hashMapOf() //视频播放点,记录最新的就行 时间点 key=>video_id value=>{时间点} var vp: HashMap<Long, String> = hashMapOf() //视频播放点,记录最新的就行 时间点 key=>video_id value=>{时间点}
var exercise_schedule: HashMap<Long, Double> = hashMapOf() //课程练习进度(仅作文有效)

var rl: Int = 0 //课程重学次数;有值表示是重新学,默认不是重新学习,对应数值表示第几次重学 用于辨音,拼写判断是否解锁,
var chapter_rl: HashMap<String, Long> = hashMapOf() //章节重学次数,key =>{chapter_id}_{lesson_id} value=> relearn times
var ct: Long = 0 //课程已学词汇量 var ct: Long = 0 //课程已学词汇量
var e = 0.0 //学习效率 var e = 0.0 //学习效率
var valid: HashMap<Long, Long> = hashMapOf() //有效学习时长,单位毫秒 key=>分时标记,value=>时长 var valid: HashMap<Long, Long> = hashMapOf() //有效学习时长,单位毫秒 key=>分时标记,value=>时长
var current_week_total_durations: Long = 0 var current_week_total_durations: Long = 0
var last_e: Double = 0.0 //上周期学习效率 var last_e: Double = 0.0 //上周期学习效率
var temporary_words: HashMap<String, String> = hashMapOf() //课程错误本,重学会删除 key=>{chapter_id}_{lesson_id}_{entity_id} value=> first learn time var temporary_words: HashMap<String, String> = hashMapOf() //课程错误本,重学会删除 key=>{chapter_id}_{lesson_id}_{entity_id} value=> first learn time


// var exercise_schedule: HashMap<Long, Double> = hashMapOf() //课程练习进度(仅作文有效)
// var rl: Int = 0 //课程重学次数;有值表示是重新学,默认不是重新学习,对应数值表示第几次重学 用于辨音,拼写判断是否解锁,
// var chapter_rl: HashMap<String, Long> = hashMapOf() //章节重学次数,key =>{chapter_id}_{lesson_id} value=> relearn times


} }

+ 13
- 10
app/src/main/java/com/xkl/cdl/data/bean/course/CoursePack.kt View File

field = value field = value
notifyPropertyChanged(BR.learnProgress) notifyPropertyChanged(BR.learnProgress)
} }


override fun equals(other: Any?): Boolean {
override fun equals(other : Any?) : Boolean {
if (this === other) return true if (this === other) return true
if (javaClass != other?.javaClass) return false if (javaClass != other?.javaClass) return false
other as CoursePack other as CoursePack
if (coursePackId != other.coursePackId) return false if (coursePackId != other.coursePackId) return false
if (coursePackName != other.coursePackName) return false if (coursePackName != other.coursePackName) return false
if (!cover.contentEquals(other.cover)) return false if (!cover.contentEquals(other.cover)) return false
if (summary != other.summary) return false if (summary != other.summary) return false
if (subjectId != other.subjectId) return false if (subjectId != other.subjectId) return false
if (coursePackType != other.coursePackType) return false if (coursePackType != other.coursePackType) return false
if (inCoursePackPosition != other.inCoursePackPosition) return false
if (childrenCourses != other.childrenCourses) return false if (childrenCourses != other.childrenCourses) return false

if (learnProgress != other.learnProgress) return false
return true return true
} }
override fun hashCode(): Int {
override fun hashCode() : Int {
var result = coursePackId.hashCode() var result = coursePackId.hashCode()
result = 31 * result + coursePackName.hashCode() result = 31 * result + coursePackName.hashCode()
result = 31 * result + cover.contentHashCode() result = 31 * result + cover.contentHashCode()
result = 31 * result + summary.hashCode() result = 31 * result + summary.hashCode()
result = 31 * result + subjectId result = 31 * result + subjectId
result = 31 * result + coursePackType result = 31 * result + coursePackType
result = 31 * result + inCoursePackPosition
result = 31 * result + childrenCourses.hashCode() result = 31 * result + childrenCourses.hashCode()
result = 31 * result + learnProgress.hashCode()
return result return result
} }
} }

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



import appApi.AppApi import appApi.AppApi
import com.suliang.common.util.AppGlobals import com.suliang.common.util.AppGlobals
import com.suliang.common.util.DateUtil
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
import com.xkl.cdl.data.bean.course.CoursePack import com.xkl.cdl.data.bean.course.CoursePack
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 java.io.* import java.io.*
import java.lang.Exception
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode import java.math.RoundingMode
import java.text.DateFormat
import java.text.DecimalFormat import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.*
import java.util.zip.ZipFile import java.util.zip.ZipFile
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream


} ?: 0 } ?: 0
} }
// TODO: 2022/6/2 时间需改为day 开发期间用于调式,故意设此值
//间隔时间单位
private val reviewIntervalUnit = "day"
//间隔单位时间
private val reviewTime = IntArray(7){
when(it){
0 -> 1
1 -> 2
2 -> 3
3 -> 5
4 -> 7
5 -> 10
else -> 15
}
}
/**
* 计算是否需要仅需复习
* @param currentReviewNumber String 当前词复习的次数
* @param currentReviewTime String 当前词复习的时间
* @param currentTime Long 用以计算的当前时间
* @return Boolean true:需要复习 false:不需要复习
*/
fun calculateIsNeedInReview(calendar:Calendar,currentReviewNumber : Int, currentReviewTime : String, currentTime : Long) : Boolean {
if (currentReviewNumber >= reviewTime.size) return false
try {
//转换设置当前的复习时间
calendar.time = DateUtil.format(currentReviewTime,DateUtil.FORMAT_1)
//与当前时间的间隔
val intervalTime = currentTime - calendar.timeInMillis
//单位转换 : 给一个提前量的时间,提前一分钟
return intervalTime >= when(reviewIntervalUnit){
"minute" -> reviewTime[currentReviewNumber] * 60000
"hour" -> reviewTime[currentReviewNumber] * 60 * 60000
else -> reviewTime[currentReviewNumber] * 24 * 60 * 60000
} - 60000
}catch (e:Exception){
e.printStackTrace()
}
return false
}
} }

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

lessonPositionInList = positionIndex lessonPositionInList = positionIndex
this.wordIds = wordIds //内容 this.wordIds = wordIds //内容
totalNumber = this.wordIds.size //总数 totalNumber = this.wordIds.size //总数
correctNumber = detail.right.getOrElse(key, { 0 }) //正确数
errorNumber = detail.wrong.getOrElse(key, { 0 }) //错误数
correctNumber = detail.right.getOrElse(key, { 0 }).toInt() //正确数
errorNumber = detail.wrong.getOrElse(key, { 0 }).toInt() //错误数
beforeTestScore = detail.before.getOrElse(key, { AppConstants.NOT_DOING }) //课时学前测试成绩 beforeTestScore = detail.before.getOrElse(key, { AppConstants.NOT_DOING }) //课时学前测试成绩
afterTestScore = detail.after.getOrElse(key, { AppConstants.NOT_DOING }) //课时学后测试成绩 afterTestScore = detail.after.getOrElse(key, { AppConstants.NOT_DOING }) //课时学后测试成绩
this.lessonType = lessonType this.lessonType = lessonType

+ 75
- 19
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 appApi.AppApi
import com.google.protobuf.ProtocolStringList
import com.googlecode.protobuf.format.JsonFormat import com.googlecode.protobuf.format.JsonFormat
import com.suliang.common.extension.io2Io
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
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 com.xkl.cdl.module.XKLApplication
import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Observable
import mqComsumerV1.Struct import mqComsumerV1.Struct
import java.io.File import java.io.File
* Describe: 数据提供 * Describe: 数据提供
*/ */
object DataRepository { object DataRepository {

// TODO: 2022/6/1 需自己实现获取课程收藏数据,同时需自己处理保存数据
/** 获取课程的收藏本 */ /** 获取课程的收藏本 */
fun getCourseCollect() : Observable<HashMap<String,Long>>{ fun getCourseCollect() : Observable<HashMap<String,Long>>{
return Observable.create{ return Observable.create{
} }


/** 获取复习的内容 */ /** 获取复习的内容 */
fun getReviewData() : Observable<Array<String>> {
fun getReviewData(subjectId : Int,coursePackId : Long,courseId : Long) : Observable<ProtocolStringList> {
return Observable.create{ return Observable.create{
mutableListOf<String>()
it.onNext(Array<String>(1){"1"})
val wordList = XKLApplication.mobileCache.getWordList(subjectId.toLong(), coursePackId, courseId, 2)
val parseFrom = wordList?.let {
AppApi.GetWordListResponse.parseFrom(wordList)
}?: AppApi.GetWordListResponse.newBuilder().build()
it.onNext(parseFrom.wrongList)
it.onComplete() it.onComplete()
} }
} }


/** 获取课程的详情 */ /** 获取课程的详情 */
fun getCourseStatistics(subjectId:Int,coursePackId:Long,courseId:Long): Observable<CourseDetail>{
fun getCourseDetails(subjectId:Int, coursePackId:Long, courseId:Long): Observable<CourseDetail>{
return Observable.create{ return Observable.create{
// 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()
val pbCourseDetail = XKLApplication.mobileCache.courseDetail(subjectId.toLong(), coursePackId, courseId).let {
AppApi.CourseDetailResponse .parseFrom(it).detail
}
val courseDetail = CourseDetail().apply {
courseLearnProgress = pbCourseDetail.s //课程学习进度
st_before = pbCourseDetail.stBefore //学前总成绩 : -1代表没有进行学前总测试
st_after = pbCourseDetail.stAfter //学后总成绩 : -1代表没有进行学后总测试
before = HashMap(pbCourseDetail.beforeMap) //章节学前测试成绩,key=>{chapter_id}_{lesson_id} value=>成绩
after = HashMap(pbCourseDetail.afterMap) //章节学后测试成绩,key=>{chapter_id}_{lesson_id} value=>成绩
right = HashMap(pbCourseDetail.rightMap) //正确条目数,key=>{chapter_id}_{lesson_id} value=>条目数量
wrong = HashMap(pbCourseDetail.wrongMap) //错误条目数,key=>{chapter_id}_{lesson_id} value=>条目数量
lesson_learn_point = HashMap(
pbCourseDetail.chapterLpMap) //课时学习点 key=>{chapter_id}_{lesson_id} value=>{entity_id)
exam_w_r_list = HashMap(
pbCourseDetail.examWRListMap) //课程/课时学前测试正确错误列表 key=> {chapter_id}_{lesson_id}_{entity_id} value=>正确 true;错误 false
course_learn_point = pbCourseDetail.lp //课程学习进度点 {chapter_id}_{lesson_id}_{entity_id}
vp = HashMap(pbCourseDetail.vpMap) //视频播放点,记录最新的就行 时间点 key=>video_id value=>{时间点}
ct = pbCourseDetail.ct //课程已学词汇量
e = pbCourseDetail.e //学习效率
valid = HashMap(pbCourseDetail.validMap) //有效学习时长,单位毫秒 key=>分时标记,value=>时长
review = HashMap(pbCourseDetail.reviewMap) //有效复习时长,单位毫秒 key=>分时标记,value=>时长
total = HashMap(pbCourseDetail.totalMap) //总时长,单位毫秒 key=>分时标记,value=>时长
current_week_total_durations = pbCourseDetail.currentWeekTotalDurations
last_e = pbCourseDetail.lastE //上周期学习效率
temporary_words = HashMap(
pbCourseDetail.temporaryWordsMap) //课程错误本,重学会删除 key=>{chapter_id}_{lesson_id}_{entity_id} value=> first learn time

// exercise_schedule: HashMap<Long, Double> = HashMap(pbCourseDetail.exerciseScheduleMap) //课程练习进度(仅作文有效)
// rl: Int = 0 //课程重学次数;有值表示是重新学,默认不是重新学习,对应数值表示第几次重学 用于辨音,拼写判断是否解锁,
// chapter_rl: HashMap<String, Long> = hashMapOf() //章节重学次数,key =>{chapter_id}_{lesson_id} value=> relearn times
} }
it.onNext(courseDetail) it.onNext(courseDetail)
it.onComplete() it.onComplete()
// T这里自己使用的缓存
// val file = File(FileUtil.getSaveDirPath("appcache"), "${subjectId}_${coursePackId}_${courseId}")
// val courseDetail = if (file.exists()){
// FileUtil.bytesToObject(FileUtil.readFile(file)) as CourseDetail
// }else{
// CourseDetail()
// }

} }
} }


//计算词汇量和学习效率 //计算词汇量和学习效率
calcCourseVocabularyAndEfficiency(subjectId,coursePackId,courseId) calcCourseVocabularyAndEfficiency(subjectId,coursePackId,courseId)
} }
// TODO: 2022/4/29 调用保存方法
try {
XKLApplication.mobileCache.parseData(record.build().toByteArray())
} catch (e : Exception) {
e.printStackTrace()
return false
}


var fileName = ""
/*var fileName = ""
//写入文件 //写入文件
if (record.examCount > 0 ){ //当前为测试 if (record.examCount > 0 ){ //当前为测试
val s = when (record.getExam(0).type.toInt()) { //测试类型 val s = when (record.getExam(0).type.toInt()) { //测试类型
//序列化输出 //序列化输出
val string = JsonFormat.printToString(record.build()) val string = JsonFormat.printToString(record.build())
val saveArray = record.build().toByteArray() val saveArray = record.build().toByteArray()
val saveStringName = "${fileName}_jsonString.txt" val saveStringName = "${fileName}_jsonString.txt"
FileUtil.writeBytesToFile(it,saveStringName,string.toByteArray()) FileUtil.writeBytesToFile(it,saveStringName,string.toByteArray())
val saveByteArrayName = "${fileName}_byteArray.txt" val saveByteArrayName = "${fileName}_byteArray.txt"
FileUtil.writeBytesToFile(it,saveByteArrayName,saveArray) FileUtil.writeBytesToFile(it,saveByteArrayName,saveArray)
}
}*/
return true return true
} }
// TODO: 2022/6/1 计算词汇量和效率可以在需要的时候进行计算后再获取
/** /**
* 计算当前词汇量 与 学习效率 * 计算当前词汇量 与 学习效率
*/ */
public fun calcCourseVocabularyAndEfficiency( projectId:Long, packId:Long, courseId:Long) {
private fun calcCourseVocabularyAndEfficiency( projectId:Long, packId:Long, courseId:Long) {
Observable.create<Boolean> {
try {
XKLApplication.mobileCache.calcCurrentVocabulary(projectId) //词汇量
XKLApplication.mobileCache.calcEfficiency(projectId,packId,courseId) //效率
} catch (e : Exception) {
e.printStackTrace()
}
it.onComplete()
}.compose(io2Io()).subscribe()
} }
} }

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



import android.app.Application import android.app.Application
import com.suliang.common.util.LogUtil import com.suliang.common.util.LogUtil
import com.suliang.common.util.file.FileUtil
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import io.reactivex.rxjava3.exceptions.UndeliverableException import io.reactivex.rxjava3.exceptions.UndeliverableException
import io.reactivex.rxjava3.functions.Consumer import io.reactivex.rxjava3.functions.Consumer
import io.reactivex.rxjava3.plugins.RxJavaPlugins import io.reactivex.rxjava3.plugins.RxJavaPlugins
import mobile_cache.MobileCache
import mobile_cache.Mobile_cache
import net.sqlcipher.database.SQLiteDatabase import net.sqlcipher.database.SQLiteDatabase
import java.io.File
import java.io.IOException import java.io.IOException
import java.lang.IllegalArgumentException
import java.lang.IllegalStateException
import java.lang.NullPointerException
import java.util.* import java.util.*


/** /**
* Describe: * Describe:
*/ */
class XKLApplication : Application() { class XKLApplication : Application() {

companion object{
val mobileCache:MobileCache by lazy {
val file : String = File(FileUtil.getSaveDirFile("db"), "mydb").absolutePath
Mobile_cache.new_(file)
}
}
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
SQLiteDatabase.loadLibs(this) SQLiteDatabase.loadLibs(this)
// ImageLoader.mStrategy = GlideLoaderStrategy()
LogUtil.e(UUID.randomUUID().toString().replace("-","")) LogUtil.e(UUID.randomUUID().toString().replace("-",""))
//初始MMKV存储 //初始MMKV存储
val rootDir = MMKV.initialize(this) val rootDir = MMKV.initialize(this)
LogUtil.e(rootDir) LogUtil.e(rootDir)
setRxJavaErrorHandler() setRxJavaErrorHandler()
// HookMobileCache().hook()
} }
/*** /***
* 避免 调用多次onError。正常来说第一次onError会走正常的Observer处理,其他会走ErrorHandler。通过此方法捕捉多次的error * 避免 调用多次onError。正常来说第一次onError会走正常的Observer处理,其他会走ErrorHandler。通过此方法捕捉多次的error
*/ */

+ 1
- 2
app/src/main/java/com/xkl/cdl/module/learn/LearnCReadingViewModel.kt View File

} }
/** /**
* 添加到收藏
* 取消收藏
* @return MutableLiveData<Long> * @return MutableLiveData<Long>
*/ */
fun removeCollect() : MutableLiveData<Long> { fun removeCollect() : MutableLiveData<Long> {
} }
fun saveData() { fun saveData() {
// TODO: 2022/5/20 保存记录点 如果学习完成,则设置进度点和修改为学习完成
Observable.create<Boolean> { emitter -> Observable.create<Boolean> { emitter ->
val record = Struct.Record.newBuilder().apply { val record = Struct.Record.newBuilder().apply {
addEntity(Struct.LearnEntity.newBuilder().apply { addEntity(Struct.LearnEntity.newBuilder().apply {

+ 23
- 3
app/src/main/java/com/xkl/cdl/module/learn/LearnCVideoViewModel.kt View File

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 com.xkl.cdl.module.XKLApplication
import com.xkl.videoplayer.bean.PineMediaPlayerBean import com.xkl.videoplayer.bean.PineMediaPlayerBean
import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.functions.BiFunction
import mqComsumerV1.Struct import mqComsumerV1.Struct




uploadDate.value = true uploadDate.value = true
return return
} }
// TODO: 2022/5/26 保存视频的播放时间
Observable.create<Boolean> { emitter ->
Observable.zip(
Observable.create<Boolean> {
try {
//保存视频播放时间
XKLApplication.mobileCache.setVideoPoint(lesson.subjectId.toLong(), lesson.coursePackId, lesson.courseId, lesson.wordIds[0], currentPlayTime.toString())
it.onNext(true)
} catch (e : Exception) {
e.printStackTrace()
it.onNext(false)
}
it.onComplete()
},
Observable.create<Boolean> { emitter ->
//保存数据
val record = Struct.Record.newBuilder().apply { val record = Struct.Record.newBuilder().apply {
//进度点 //进度点
addEntity(Struct.LearnEntity.newBuilder().apply { addEntity(Struct.LearnEntity.newBuilder().apply {
} }
emitter.onNext(DataRepository.saveRecord(record)) emitter.onNext(DataRepository.saveRecord(record))
emitter.onComplete() emitter.onComplete()
}.compose(diskIo2Main()).subscribe {
}, { t1, t2 ->
t1 && t2
}).compose(diskIo2Main()).subscribe {
if (!it){
LogUtil.e("视频数据保存有误")
return@subscribe
}
lesson.videoPlayTime = currentPlayTime lesson.videoPlayTime = currentPlayTime
LogUtil.e("当前播放时间:$currentPlayTime") LogUtil.e("当前播放时间:$currentPlayTime")
when{ when{

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

/** 测试完成 : 弹窗显示 */ /** 测试完成 : 弹窗显示 */
private fun testOver() { private fun testOver() {
// TODO: 2022/5/17 作文测试弹窗
//对话框信息实体 //对话框信息实体
val learnDialogBean = LearnDialogBean(AppConstants.DIALOG_TYPE_EXAM_OVER).apply { val learnDialogBean = LearnDialogBean(AppConstants.DIALOG_TYPE_EXAM_OVER).apply {
examType = vm.intentData.examType examType = vm.intentData.examType

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

private fun saveData() { private fun saveData() {
showHideLoading(true) showHideLoading(true)
Observable.create<Boolean> { Observable.create<Boolean> {
viewModelScope.launch {
delay(1000)
DataRepository.saveRecord(record)
it.onNext(true)
it.onComplete()
}
// TODO: 2022/4/14 传递保存record信息
val saveRecord = DataRepository.saveRecord(record)
it.onNext(saveRecord)
it.onComplete()
}.compose(diskIo2Main()).subscribe({ }.compose(diskIo2Main()).subscribe({
showHideLoading(false) showHideLoading(false)
sendEventBus() //返回发送数据 sendEventBus() //返回发送数据

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

/** 横幅 自动播放 与 复习时使用 */ /** 横幅 自动播放 与 复习时使用 */
private fun initBanner() { private fun initBanner() {
// TODO: 2022/4/25 初始化横幅,默认隐藏
// 初始化横幅,默认隐藏
if (vm.learnData.isAutoPlay) { if (vm.learnData.isAutoPlay) {
binding.tvBanner.apply { binding.tvBanner.apply {
visibility = View.VISIBLE visibility = View.VISIBLE

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

fun saveData() { fun saveData() {
showHideLoading(true) showHideLoading(true)
Observable.create<Boolean> { Observable.create<Boolean> {
viewModelScope.launch {
delay(1000)
it.onNext(true)
}
// TODO: 2022/4/14 传递保存record信息
// record 已经实例化并已经将数据保存 // record 已经实例化并已经将数据保存
if (!saveInit) { if (!saveInit) {
//自动播放不修改课时信息 //自动播放不修改课时信息
record.addDuration(saveCurrentLearnDuration()) record.addDuration(saveCurrentLearnDuration())
saveInit = true saveInit = true
} }
DataRepository.saveRecord(record)
// LogUtil.e(JsonFormat.printToString(record.build()))
it.onNext(DataRepository.saveRecord(record))
it.onComplete()
}.compose(diskIo2Main()).subscribe({ }.compose(diskIo2Main()).subscribe({
showHideLoading(false) showHideLoading(false)
sendEventBus() //返回发送数据 sendEventBus() //返回发送数据

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

import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly


/** /**
* 课程中心
* 课程包主页中心
*/ */
class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CoursePackMainActivityViewModel>() { class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CoursePackMainActivityViewModel>() {
//子课程对应的Fragment //子课程对应的Fragment
private var childFragments = mutableListOf<Fragment>() private var childFragments = mutableListOf<Fragment>()
// //更多按钮的弹窗显示
// private var moreDialog : BottomSheetDialog? = null
//自动播放次数的弹窗选择 //自动播放次数的弹窗选择
private var autoPlaySeletDialog : BottomSheetDialog? = null private var autoPlaySeletDialog : BottomSheetDialog? = null
} }
} }
override fun onStop() {
super.onStop()
//如果是中文项目,更新课程包的进度
if (vm.coursePack.subjectId == AppConstants.SUBJECT_CHINESE) {
vm.coursePack.learnProgress = vm.coursePack.childrenCourses[0].courseLearnProgress
}
}
/** ViewModel Factory工厂 */ /** ViewModel Factory工厂 */
inner class ViewModelFactory(private val subjectId : Int, private val coursePackInPosition : Int) : inner class ViewModelFactory(private val subjectId : Int, private val coursePackInPosition : Int) :

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

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


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.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.manager.CourseManager import com.xkl.cdl.data.manager.CourseManager


/** /**
//设置显示当前课程的进度值和显示内容 //设置显示当前课程的进度值和显示内容
val currentCourseProgress = MutableLiveData<Double>() val currentCourseProgress = MutableLiveData<Double>()
override fun onStop(owner : LifecycleOwner) {
super.onStop(owner)
//如果是中文项目,更新课程包的进度
if (coursePack.subjectId == AppConstants.SUBJECT_CHINESE) {
coursePack.learnProgress = coursePack.childrenCourses[0].courseLearnProgress
}
}
} }

+ 2
- 17
app/src/main/java/com/xkl/cdl/module/m_center_learn/LearnCenterFragment.kt View File



//TabLayout 与 ViewPager2 进行关联, 并进行tab设值 //TabLayout 与 ViewPager2 进行关联, 并进行tab设值
TabLayoutMediator(binding.tabLayout, binding.viewPager2) { tab, position -> TabLayoutMediator(binding.tabLayout, binding.viewPager2) { tab, position ->
tab.setCustomView(layoutInflater.inflate(R.layout.textview_only,binding.tabLayout,false))
tab.customView?.let {
(it as TextView).setText(vm.mProjectTitles[position])
tab.customView = layoutInflater.inflate(R.layout.textview_only, binding.tabLayout, false).apply {
(this as TextView).setText(vm.mProjectTitles[position])
} }
}.attach() }.attach()


} }
} }


// /** 搜索框 输入监听 */
// binding.editTextSearch.setOnEditorActionListener { v, actionId, event ->
// if (actionId == EditorInfo.IME_ACTION_SEARCH){
// //隐藏键盘
// KeyboardUtil.hideKeyboard(v)
// //赋值,通知 子 Fragment 进行更新
// v.text.toString()
//
// true
// }else {
// false
// }
// }

} }


override fun loadData() { override fun loadData() {

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

vm.allLesson[learnEventData.leesonPositionIndex].let { vm.allLesson[learnEventData.leesonPositionIndex].let {
val key = "${it.chapterId}_${it.lessonId}" val key = "${it.chapterId}_${it.lessonId}"
vm.courseDetail.before.put(key, it.beforeTestScore) vm.courseDetail.before.put(key, it.beforeTestScore)
vm.courseDetail.wrong.put(key, it.errorNumber)
vm.courseDetail.wrong.put(key, it.errorNumber.toLong())
learnEventData.newErrorMap?.let { it1 -> learnEventData.newErrorMap?.let { it1 ->
vm.courseDetail.exam_w_r_list.putAll(it1) vm.courseDetail.exam_w_r_list.putAll(it1)
} }
//单词类 //单词类
AppConstants.LESSON_TYPE_WORD ->{ AppConstants.LESSON_TYPE_WORD ->{
vm.courseDetail.run { vm.courseDetail.run {
wrong[key] = it.errorNumber //更新错误数
right[key] = it.correctNumber //更新正确数
wrong[key] = it.errorNumber.toLong() //更新错误数
right[key] = it.correctNumber.toLong() //更新正确数
lesson_learn_point[key] = it.wordIds[it.learnedIndex] //更新课时学习点 lesson_learn_point[key] = it.wordIds[it.learnedIndex] //更新课时学习点
course_learn_point = "${key}_${it.wordIds[it.learnedIndex]}" //更新课程学习点 course_learn_point = "${key}_${it.wordIds[it.learnedIndex]}" //更新课程学习点
} }
} }
//知识点 //知识点
AppConstants.LESSON_TYPE_COMPOSITION_KNOWLEDGE -> vm.courseDetail.run { AppConstants.LESSON_TYPE_COMPOSITION_KNOWLEDGE -> vm.courseDetail.run {
wrong[key] = it.errorNumber //更新错误数
right[key] = it.correctNumber //更新正确数
wrong[key] = it.errorNumber.toLong() //更新错误数
right[key] = it.correctNumber.toLong() //更新正确数
lesson_learn_point[key] = it.wordIds[it.learnedIndex] //更新课时学习点 lesson_learn_point[key] = it.wordIds[it.learnedIndex] //更新课时学习点
course_learn_point = "${key}_${it.wordIds[it.learnedIndex]}" //更新课程学习点 course_learn_point = "${key}_${it.wordIds[it.learnedIndex]}" //更新课程学习点
} }
//学后测试结束,弹窗动作 课时重新学习 //学后测试结束,弹窗动作 课时重新学习
AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN -> { AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN -> {
//课时学后测试,点击重学 //课时学后测试,点击重学
// TODO: 2022/4/22 清除当前课时数据,并更新当前课时,后进入学习界面
vm.allLesson[learnEventData.leesonPositionIndex].learnIsOver = false vm.allLesson[learnEventData.leesonPositionIndex].learnIsOver = false
vm.relearnLesson(learnEventData.leesonPositionIndex).observe(this) { vm.relearnLesson(learnEventData.leesonPositionIndex).observe(this) {
//item更新 //item更新
dialog.dismissAllowingStateLoss() dialog.dismissAllowingStateLoss()
when (action) { when (action) {
AppConstants.DIALOG_START_LEARN -> { //跳过测试 继续学习 AppConstants.DIALOG_START_LEARN -> { //跳过测试 继续学习
// TODO: 2022/4/21 进入学习界面
// TODO: 2022/4/21 进入学习界面 ,不能跳过测试,必须学习
} }
// 开始测试 // 开始测试
AppConstants.DIALOG_START_TEST -> startLessonTest(lesson, AppConstants.TEST_TYPE_BEFORE, it) AppConstants.DIALOG_START_TEST -> startLessonTest(lesson, AppConstants.TEST_TYPE_BEFORE, it)

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

* 改变加载的子Fragment 单词类 * 改变加载的子Fragment 单词类
*/ */
private fun changeChildrenFragmentForWord() { private fun changeChildrenFragmentForWord() {
// TODO: 2022/5/5 复习页面加载
currentFragment = vm.courseDetail.let { currentFragment = vm.courseDetail.let {
when{ when{
vm.reviewDataList.size > 0 -> CourseReviewFragment.newInstanceFromLearningCenter() //有复习
it.st_before == AppConstants.NOT_DOING -> CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_BEFORE_TOTAL) //学前总测未做 it.st_before == AppConstants.NOT_DOING -> CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_BEFORE_TOTAL) //学前总测未做
it.courseLearnProgress != AppConstants.DOING_OVER -> CourseLessonFragment.newInstance() //学习未完成 it.courseLearnProgress != AppConstants.DOING_OVER -> CourseLessonFragment.newInstance() //学习未完成
else -> CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL) //学习完成,学后总测试界面 else -> CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL) //学习完成,学后总测试界面
* 改变加载的子Fragment 作文 * 改变加载的子Fragment 作文
*/ */
private fun changeChildrenFragmentForComposition() { private fun changeChildrenFragmentForComposition() {
// TODO: 2022/5/5 复习页面加载 CourseReviewFragment.newInstance()
currentFragment = vm.courseDetail.let {
CourseLessonFragment.newInstance()
currentFragment = when {
vm.reviewDataList.size > 0 -> CourseReviewFragment.newInstanceFromLearningCenter() //复习
else -> CourseLessonFragment.newInstance() //目录
} }
replaceFragment(R.id.layout_root, currentFragment) replaceFragment(R.id.layout_root, currentFragment)
} }

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

import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.google.common.base.Joiner import com.google.common.base.Joiner
import com.google.protobuf.ProtocolStringList
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.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 com.xkl.cdl.module.XKLApplication
import com.xkl.cdl.module.m_center_learn.CoursePackMainActivityViewModel import com.xkl.cdl.module.m_center_learn.CoursePackMainActivityViewModel
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Scheduler
import io.reactivex.rxjava3.functions.BiFunction import io.reactivex.rxjava3.functions.BiFunction
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import mqComsumerV1.Struct import mqComsumerV1.Struct
import java.io.File import java.io.File
import java.util.* import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap import kotlin.collections.HashMap


/** /**
lateinit var collectList : HashMap<String, Long> lateinit var collectList : HashMap<String, Long>
/** 复习的数据 */ /** 复习的数据 */
lateinit var reviewDataList : MutableList<String>
lateinit var reviewDataList : MutableList<LearnWord>
/* 复习数据是否进行了内容赋值,如果进行了赋值,则直接复习,如果没有进行赋值,需要在复习前,进行查询赋值 */
var isAssignmentReviewData = false
/** 课程所有课时 */ /** 课程所有课时 */
lateinit var allLesson : List<Lesson> lateinit var allLesson : List<Lesson>
//获取课程的章节数据 //获取课程的章节数据
fun loadMain() : MutableLiveData<Boolean> { fun loadMain() : MutableLiveData<Boolean> {
val mutableLiveData = MutableLiveData<Boolean>() val mutableLiveData = MutableLiveData<Boolean>()
Observable.zip(DataRepository.getCourseStatistics(course.subjectId, course.coursePackId, course.courseId).flatMap {
Observable.zip(DataRepository.getCourseDetails(course.subjectId, course.coursePackId, course.courseId).flatMap {
courseDetail = it courseDetail = it
course.courseLearnProgress = it.courseLearnProgress course.courseLearnProgress = it.courseLearnProgress
coursePackMainActivityVM.currentCourseProgress.postValue(course.courseLearnProgress) coursePackMainActivityVM.currentCourseProgress.postValue(course.courseLearnProgress)
}.flatMap { }.flatMap {
allLesson = it allLesson = it
return@flatMap DataRepository.getCourseCollect() return@flatMap DataRepository.getCourseCollect()
}, DataRepository.getReviewData(),
BiFunction<HashMap<String, Long>, Array<String>, Boolean> { t1 : HashMap<String, Long>?, t2 : Array<String>? ->
// TODO: 2022/5/5 初始化需要复习的数据
}, DataRepository.getReviewData(course.subjectId, course.coursePackId, course.courseId),
BiFunction<HashMap<String, Long>, ProtocolStringList, Boolean> { t1 : HashMap<String, Long>, t2 : ProtocolStringList ->
//保存课程的收藏本数据
collectList = t1
//初始化保存需要复习的数据
initReviewData(t2)
true true
}).compose(diskIo2Main()).subscribe { }).compose(diskIo2Main()).subscribe {
mutableLiveData.value = it mutableLiveData.value = it
return mutableLiveData return mutableLiveData
} }
/**
* 初始化复习的数据
* @param rev Array<String> 需要复习的数据 value 格式 {project_id}_{pack_id}_{course_id}_{chapter_id}_{lesson_id}_{entity_id}_{review_num}_{Y-m-d H:i:s}_{Y-m-d H:i:s}
*/
private fun initReviewData(rev : ProtocolStringList) {
if (!this::reviewDataList.isInitialized) {
reviewDataList = mutableListOf()
}
reviewDataList.clear()
//当前时间戳
val currentTime = System.currentTimeMillis()
//日历
val calendar = Calendar.getInstance(Locale.CHINA)
//循环赋值保存需要复习的数据
rev.forEach {
val splitValue = it.split("_") //拆分为数组
//判断是否能够进入复习
val isNeedInReview = CourseManager.calculateIsNeedInReview(calendar, splitValue[6].toInt(), splitValue[7],
currentTime)
if (isNeedInReview) {
// lessonType为非准确的赋值,需要在查询具体详情数据的时候,在对lessonType进行具体赋值
reviewDataList.add(
LearnWord(splitValue[0].toInt(), splitValue[1].toLong(), splitValue[2].toLong(), course.coursePackType,
course.courseType, splitValue[3].toLong(), splitValue[4].toLong(), splitValue[5].toLong(), true,
0).apply {
reviewNum = splitValue[6].toInt()
})
}
}
}
/** 课程包主页上的更多按钮点击是否有效 */ /** 课程包主页上的更多按钮点击是否有效 */
fun showMoreIsEnable() : Boolean { fun showMoreIsEnable() : Boolean {
return when (course.courseType) { return when (course.courseType) {
fun modifyLessonErrorNumber() { fun modifyLessonErrorNumber() {
//重设detail中的错误数, exam_w_r_list为所有的测试前错误,因为时学前总测,所以,可以根据这个字段来计算每个课时的错误数 //重设detail中的错误数, exam_w_r_list为所有的测试前错误,因为时学前总测,所以,可以根据这个字段来计算每个课时的错误数
//记录错误数: key :chapterId_lessonId value :错误数 //记录错误数: key :chapterId_lessonId value :错误数
val errorNumber = hashMapOf<String, Int>()
val errorNumber = hashMapOf<String, Long>()
courseDetail.exam_w_r_list.keys.forEach { courseDetail.exam_w_r_list.keys.forEach {
val split = it.split("_") val split = it.split("_")
if (split.size == 3) { if (split.size == 3) {
//更新lesson中的错误数 //更新lesson中的错误数
allLesson.forEach { allLesson.forEach {
val key = "${it.chapterId}_${it.lessonId}" val key = "${it.chapterId}_${it.lessonId}"
it.errorNumber = errorNumber.getOrElse(key, { 0 })
it.errorNumber = errorNumber.getOrElse(key, { 0 }).toInt()
} }
} }
val subjectProgress = CourseManager.calculateSubjectProgressWithCourseLessonRelearn(course.subjectId, course.coursePackId, val subjectProgress = CourseManager.calculateSubjectProgressWithCourseLessonRelearn(course.subjectId, course.coursePackId,
course.courseId, courseProgress) course.courseId, courseProgress)
Observable.fromCallable { Observable.fromCallable {
// TODO: 2022/5/5 进行数据清除和保存
XKLApplication.mobileCache.relearn(course.subjectId.toLong(),course.coursePackId,course.courseId,allLesson[lessonPositionIndex].chapterId,
allLesson[lessonPositionIndex].lessonId,
courseProgress,0.0,subjectProgress)
}.compose(diskIo2DiskIo()).subscribe { }.compose(diskIo2DiskIo()).subscribe {
//更新lesson //更新lesson
val lesson = allLesson[lessonPositionIndex].apply { val lesson = allLesson[lessonPositionIndex].apply {
val subjectProgress = CourseManager.calculateSubjectProgressWithCourseRelearn(course.subjectId, course.coursePackId, val subjectProgress = CourseManager.calculateSubjectProgressWithCourseRelearn(course.subjectId, course.coursePackId,
course.courseId) course.courseId)
Observable.fromCallable { Observable.fromCallable {
// TODO: 2022/5/5 调用课程重学 重新请求详情
val file = File(FileUtil.getSaveDirPath("appcache"), "${course.subjectId}_${course.coursePackId}_${course.courseId}")
if (file.exists()) {
file.delete()
}
// 调用课程重学 后会再次重新调用loadMain
XKLApplication.mobileCache.relearn(course.subjectId.toLong(),course.coursePackId,course.courseId,0,
0, 0.0,0.0,subjectProgress)
}.compose(diskIo2DiskIo()).subscribe { }.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) result.postValue(true)
} }
return result return result
} }
// TODO: 2022/5/6 这里在退出时进行了统计数据缓存保存,在loadMain的时候进行了read缓存 ,后期需要清除
override fun onDestroy(owner : LifecycleOwner) {
if (owner is CourseMainFragment) {
LogUtil.e("${this.toString().substringAfterLast(".")} 保存文件 onDestroy")
try {
val objectToBytes = FileUtil.objectToBytes(courseDetail)
FileUtil.writeBytesToFile(FileUtil.getSaveDirPath("appcache"),
"${course.subjectId}_${course.coursePackId}_${course.courseId}", objectToBytes)
} catch (e : Exception) {
e.printStackTrace()
LogUtil.e("${javaClass} 保存文件异常失败")
}
}
super.onDestroy(owner)
}
/* // 这里在退出时进行了统计数据缓存保存,在loadMain的时候进行了read缓存 ,后期需要清除
override fun onDestroy(owner : LifecycleOwner) {
if (owner is CourseMainFragment) {
LogUtil.e("${this.toString().substringAfterLast(".")} 保存文件 onDestroy")
try {
val objectToBytes = FileUtil.objectToBytes(courseDetail)
FileUtil.writeBytesToFile(FileUtil.getSaveDirPath("appcache"),
"${course.subjectId}_${course.coursePackId}_${course.courseId}", objectToBytes)
} catch (e : Exception) {
e.printStackTrace()
LogUtil.e("${javaClass} 保存文件异常失败")
}
}
super.onDestroy(owner)
}*/
/** /**
* 单词类,自动播放,查询自动播放使用的单词 * 单词类,自动播放,查询自动播放使用的单词
// TODO: 2022/5/19 查询课堂练习的收藏数据 需要给 CompositionReadingBean设置收藏id // TODO: 2022/5/19 查询课堂练习的收藏数据 需要给 CompositionReadingBean设置收藏id
Observable.create<List<CompositionReadingBean>> { Observable.create<List<CompositionReadingBean>> {
result.postValue(LearnData(entity).apply { result.postValue(LearnData(entity).apply {
readingList = DBCourseManager.queryCompositionReading(dbControlBase,entity)
readingList = DBCourseManager.queryCompositionReading(dbControlBase, entity)
}) })
it.onComplete() it.onComplete()
}.compose(diskIo2Main()).subscribe() }.compose(diskIo2Main()).subscribe()

+ 84
- 15
app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseReviewFragment.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.view.View
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.suliang.common.AppConfig
import com.suliang.common.base.fragment.BaseFragmentVM import com.suliang.common.base.fragment.BaseFragmentVM
import com.suliang.common.extension.click import com.suliang.common.extension.click
import com.xkl.cdl.data.AppConstants import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.databinding.FragmentCourseReviewBinding import com.xkl.cdl.databinding.FragmentCourseReviewBinding


/** /**
* 课程章节目录
* @property param1 String?
* @property param2 String?
* 复习界面
* 加载此界面,需要传参 从备忘本进入的主页加载此界面,还是从学习中心进入的主页加载此界面,其显示将不同
*/ */
class CourseReviewFragment : BaseFragmentVM<FragmentCourseReviewBinding, CourseMainFragmentViewModel>() { class CourseReviewFragment : BaseFragmentVM<FragmentCourseReviewBinding, CourseMainFragmentViewModel>() {
companion object { companion object {
/**
* 新建实例
* @param pageSource Int 页面来源,默认为0,从学习中心加载, 1为从备忘本加载
* @return CourseReviewFragment
*/
@JvmStatic
fun newInstanceFromLearningCenter() : CourseReviewFragment {
return CourseReviewFragment()
}
/**
* 新建实例
* @param pageSource Int 页面来源,默认为0,从学习中心加载, 1为从备忘本加载
* @return CourseReviewFragment
*/
@JvmStatic @JvmStatic
fun newInstance() = CourseReviewFragment()
fun newInstanceFromMemo() : CourseReviewFragment {
val args = Bundle()
args.putInt(AppConfig.INTENT_1, 1)
val fragment = CourseReviewFragment()
fragment.arguments = args
return fragment
}
} }

override fun initFragment() { override fun initFragment() {
//获取页面来源 0 : 学习中心进入的加载复习界面 其他为备忘本进入加载的复习数据界面
val pageSource : Int = arguments?.getInt(AppConfig.INTENT_1) ?: 0
when (vm.reviewDataList.size) {
0 -> if (pageSource != 0) {
//备忘本无复习数据加载
initMemoData()
binding.tvStartReview.visibility = View.GONE
binding.tvSeeMemo.visibility = View.VISIBLE
}
else -> { //有复习数据加载
initReviewData()
when (pageSource) {
0 -> {
binding.tvSeeMemo.visibility = View.GONE
binding.tvStartReview.visibility = View.VISIBLE
}
else -> {
binding.tvStartReview.visibility = View.VISIBLE
binding.tvSeeMemo.visibility = View.VISIBLE
}
}
}
}
//开始复习
binding.tvStartReview.click {
showToast("开始复习")
if (vm.isAssignmentReviewData){ //已经赋值,直接赋值跳转
}else{ //没有赋值,需要进行
}
}
//查看备忘本
binding.tvSeeMemo.click {
showToast("查看备忘本")
}
}
private fun initReviewData() {
//根据课程类型进行显示 //根据课程类型进行显示
binding.tvTips.text = when(vm.course.courseType){
binding.tvTips.text = when (vm.course.courseType) {
AppConstants.COURSE_TYPE_ENGLISH_DISCERN -> "你将进行 [认读] 课程智能复习" AppConstants.COURSE_TYPE_ENGLISH_DISCERN -> "你将进行 [认读] 课程智能复习"
AppConstants.COURSE_TYPE_ENGLISH_VOICE -> "你将进行 [辨音] 课程智能复习" AppConstants.COURSE_TYPE_ENGLISH_VOICE -> "你将进行 [辨音] 课程智能复习"
AppConstants.COURSE_TYPE_ENGLISH_SPELL -> "你将进行 [拼写] 课程智能复习" AppConstants.COURSE_TYPE_ENGLISH_SPELL -> "你将进行 [拼写] 课程智能复习"
else -> "你将进行课程智能复习" else -> "你将进行课程智能复习"
} }
binding.tvReviewCount.text = String.format("本次智能复习数%d",vm.course.courseReviewNumber)
//开始测试事件
binding.tvStartReview.click {
showToast("开始复习")
binding.tvReviewCount.text = String.format("本次智能复习数%d", vm.reviewDataList.size)
}
private fun initMemoData() {
binding.tvTips.text = when (vm.course.courseType) {
AppConstants.COURSE_TYPE_ENGLISH_DISCERN -> "[认读] 课程加入备忘本数据共123条"
AppConstants.COURSE_TYPE_ENGLISH_VOICE -> "[辨音] 课程加入备忘本数据共123条"
AppConstants.COURSE_TYPE_ENGLISH_SPELL -> "[拼写] 课程加入备忘本数据共123条"
else -> "课程加入备忘本数据共123条"
} }
binding.tvReviewCount.visibility = View.GONE
} }

override fun loadData() { override fun loadData() {

} }

override fun initViewModel(): CourseMainFragmentViewModel {
override fun initViewModel() : CourseMainFragmentViewModel {
return ViewModelProvider(requireParentFragment())[CourseMainFragmentViewModel::class.java] return ViewModelProvider(requireParentFragment())[CourseMainFragmentViewModel::class.java]
} }
} }

+ 31
- 51
app/src/main/java/com/xkl/cdl/module/m_memo/MemoFragment.kt View File

package com.xkl.cdl.module.m_memo package com.xkl.cdl.module.m_memo


import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.suliang.common.base.fragment.BaseFragmentVM import com.suliang.common.base.fragment.BaseFragmentVM
import com.xkl.cdl.R
import com.xkl.cdl.databinding.FragmentMemoBinding import com.xkl.cdl.databinding.FragmentMemoBinding
import com.xkl.cdl.databinding.FragmentMyBinding
import com.xkl.cdl.module.main.MainActivityViewModel import com.xkl.cdl.module.main.MainActivityViewModel


// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

/** /**
* A simple [Fragment] subclass.
* Use the [MemoFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class MemoFragment : BaseFragmentVM<FragmentMemoBinding, MainActivityViewModel>(){
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_memo, container, false)
}

* author suliang
* create 2022/5/30 16:08
* Describe: 备忘本
*/
class MemoFragment : BaseFragmentVM<FragmentMemoBinding, MemoFragmentViewModel>(){
companion object { companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment MemoFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic @JvmStatic
fun newInstance() = MemoFragment() fun newInstance() = MemoFragment()
} }

override fun initViewModel(): MemoFragmentViewModel {
return ViewModelProvider(this)[MemoFragmentViewModel::class.java].apply {
mainActivityViewModel = ViewModelProvider(requireActivity())[MainActivityViewModel::class.java]
}
}
override fun initFragment() { override fun initFragment() {
binding.rv.run {
layoutManager = StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL)
adapter = vm.adapter
}


} }


override fun loadData() { override fun loadData() {

/** 搜索内容变化监听 */
vm.etSearchLiveData.observe(this){
}
} }

override fun initViewModel(): MainActivityViewModel {
return ViewModelProvider(requireActivity())[MainActivityViewModel::class.java]
override fun onResume() {
super.onResume()
//查询数据
vm.queryData()
} }


} }

+ 56
- 0
app/src/main/java/com/xkl/cdl/module/m_memo/MemoFragmentViewModel.kt View File

package com.xkl.cdl.module.m_memo

import androidx.lifecycle.MutableLiveData
import appApi.AppApi
import com.googlecode.protobuf.format.JsonFormat
import com.suliang.common.base.viewmodel.BaseViewModel
import com.suliang.common.extension.diskIo2DiskIo
import com.suliang.common.util.LogUtil
import com.xkl.cdl.adapter.AdapterCoursePackWithMemo
import com.xkl.cdl.data.bean.MemoCoursePack
import com.xkl.cdl.module.XKLApplication
import com.xkl.cdl.module.main.MainActivity
import com.xkl.cdl.module.main.MainActivityViewModel
import io.reactivex.rxjava3.core.Observable

/**
* author suliang
* create 2022/5/30 16:47
* Describe: 备忘本的VM 持有activity的viewmodel
*/
class MemoFragmentViewModel : BaseViewModel() {
lateinit var mainActivityViewModel : MainActivityViewModel
/* 课程包的备忘本数据 */
val memoCoursePackList = mutableListOf<MemoCoursePack>()
val etSearchLiveData = MutableLiveData<String>()
val adapter = AdapterCoursePackWithMemo(this).apply {
needShowEmptyView = true
}
/**
* 查询课程的错误数据结果
*/
fun queryData() {
Observable.create<ByteArray> {
//参数: 科目 课程包id 课程id category 1 获取正确的,2获取错误的,3正确错误都获取
val wordList = XKLApplication.mobileCache.getWordList(0, 0, 0, 2)
it.onNext(wordList)
it.onComplete()
}.compose(diskIo2DiskIo()).subscribe({
AppApi.GetWordListResponse.parseFrom(it).wrongList?.forEach { wrong ->
}
//赋值集合
//列表刷新
}, {
it.printStackTrace()
})
}
}

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





override fun initActivity(savedInstanceState: Bundle?) { override fun initActivity(savedInstanceState: Bundle?) {
//加载Fragment并显示
loadFragment(R.id.layout_container, -1, mMemo, mStatistics, mLearnCenter, mServiceCenter, mMy)
//点击事件 //点击事件
binding.layoutNavContainer.setOnCheckedChangeListener { group, checkedId -> binding.layoutNavContainer.setOnCheckedChangeListener { group, checkedId ->
when (checkedId) { when (checkedId) {
} }


override fun loadData() { override fun loadData() {
//加载初始化数据
vm.loadInit().observe(this){
binding.layoutNavContainer.check(R.id.nav_learnCenter)
}
//加载Fragment并显示
loadFragment(R.id.layout_container, 2, mMemo, mStatistics, mLearnCenter, mServiceCenter, mMy)
binding.layoutNavContainer.check(R.id.nav_learnCenter)
} }


} }

+ 5
- 14
app/src/main/java/com/xkl/cdl/module/main/MainActivityViewModel.kt View File

package com.xkl.cdl.module.main package com.xkl.cdl.module.main


import androidx.fragment.app.Fragment
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.suliang.common.base.viewmodel.BaseViewModel import com.suliang.common.base.viewmodel.BaseViewModel
import com.suliang.common.util.LogUtil
import com.xkl.cdl.R
import com.xkl.cdl.module.m_center_learn.LearnCenterFragment
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch


/** /**
* author suliang * author suliang
* 进行用户信息的判断,加载用户课程 * 进行用户信息的判断,加载用户课程
*/ */
class MainActivityViewModel: BaseViewModel() { class MainActivityViewModel: BaseViewModel() {

/** 进入程序加载完毕监听 */
val mainInitLiveData = MutableLiveData<Boolean>()

/*** /***
* 进行初始化加载 * 进行初始化加载
*/ */
fun loadInit(): MutableLiveData<Boolean> {
// fun loadInit(): MutableLiveData<Boolean> {
//1、查询用户的课程数据 //1、查询用户的课程数据
//设置进入课程管理类中 //设置进入课程管理类中
// viewModelScope.launch { // viewModelScope.launch {
// showHideLoading(false) // showHideLoading(false)
// //通知Activity 加载完成 // //通知Activity 加载完成
// } // }
return MutableLiveData<Boolean>().apply { postValue(true) }
}
// return MutableLiveData<Boolean>().apply { postValue(true) }
// }


//2、计算用户的复习数据 //2、计算用户的复习数据
//3、统计数据 //3、统计数据

+ 30
- 50
app/src/main/java/com/xkl/cdl/module/splash/SplashActivity.kt View File



import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import appApi.AppApi
import com.googlecode.protobuf.format.JsonFormat
import com.suliang.common.base.activity.BaseActivity import com.suliang.common.base.activity.BaseActivity
import com.suliang.common.util.LogUtil
import com.suliang.common.util.thread.AppExecutors import com.suliang.common.util.thread.AppExecutors
import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.manager.CourseManager import com.xkl.cdl.data.manager.CourseManager
import com.xkl.cdl.data.manager.db.DbCoursePackManager import com.xkl.cdl.data.manager.db.DbCoursePackManager
import com.xkl.cdl.databinding.ActivitySplashBinding import com.xkl.cdl.databinding.ActivitySplashBinding
import com.xkl.cdl.module.XKLApplication
import com.xkl.cdl.module.main.MainActivity import com.xkl.cdl.module.main.MainActivity
import io.reactivex.rxjava3.core.Observable
import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit


@SuppressLint("CustomSplashScreen") @SuppressLint("CustomSplashScreen")
class SplashActivity : BaseActivity<ActivitySplashBinding>(){
override fun onCreate(savedInstanceState: Bundle?) {
class SplashActivity : BaseActivity<ActivitySplashBinding>() {
override fun onCreate(savedInstanceState : Bundle?) {
if (!isTaskRoot) { if (!isTaskRoot) {
finish() finish()
return return
} }
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
} }
override fun initActivity(savedInstanceState: Bundle?) {
override fun initActivity(savedInstanceState : Bundle?) {
} }
override fun loadData() { override fun loadData() {

// try {
// val file: String = File(FileUtil.getSaveDirFile("db"), "mydb").absolutePath
// val mobile = Mobile_cache.new_(file)
// mobile.delete()
// println(" -------------> " + mobile.get("abc"))
//
// } catch (e: Exception) {
// e.printStackTrace()
// }

// learnDialog = LearnDialog().apply {
// show(supportFragmentManager,javaClass.name)
// }
// LogUtil.e("Dialog -- > ${learnDialog.hashCode()}")
// SpUtils.instance.encode("my","abcdefgxxxxx")
// SpUtils.instance.remove("my")
// println("---------------" + SpUtils.instance.decode("my",String::class.java))
//
// if (true) return


showHideLoading(true) showHideLoading(true)
AppExecutors.io.execute {

AppExecutors.diskIO.execute {
//读取课程数据 //读取课程数据
// TODO: 2022/5/16 读取课程的sort信息进行保存,针对作文课程,同时需要设置其课程包的进度
//用户的sort信息
XKLApplication.mobileCache.courseSorted(AppConstants.SUBJECT_ENGLISH.toLong())?.let { value ->
val parseFrom = AppApi.CourseSortedResponse.parseFrom(value).toBuilder()
CourseManager.mSortInfoList.put(AppConstants.SUBJECT_ENGLISH, parseFrom.listBuilderList)
}
XKLApplication.mobileCache.courseSorted(AppConstants.SUBJECT_CHINESE.toLong())?.let { value ->
val parseFrom = AppApi.CourseSortedResponse.parseFrom(value).toBuilder()
CourseManager.mSortInfoList.put(AppConstants.SUBJECT_CHINESE, parseFrom.listBuilderList)
}
// TODO: 2022/3/22 读取当前app绑定的课程数据, // TODO: 2022/3/22 读取当前app绑定的课程数据,
DbCoursePackManager().queryBindingCoursePack("262,261,264,136,547,615,516,411") DbCoursePackManager().queryBindingCoursePack("262,261,264,136,547,615,516,411")
//复制课程的数据库到对应位置 //复制课程的数据库到对应位置
}, 1, TimeUnit.SECONDS) }, 1, TimeUnit.SECONDS)
} }
} }


// lateinit var learnDialog :LearnDialog
// lateinit var learnDialog1: LearnDialog

// override fun rightClick() {
// if (!this::learnDialog1.isInitialized) {
// learnDialog1 = LearnDialog()
// }
// LogUtil.e("Dialog 1 -- > ${learnDialog1.hashCode()}")
// learnDialog1.show(supportFragmentManager,javaClass.name)
// }
//
// override fun leftClick() {
// learnDialog1.dismiss()
// }


} }

+ 24
- 3
app/src/main/res/layout/fragment_course_review.xml View File

android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:textColor="@color/main_text_color"
android:textColor="@color/theme_color"
android:textSize="@dimen/smallSize" android:textSize="@dimen/smallSize"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_tips" app:layout_constraintTop_toBottomOf="@+id/tv_tips"
tools:text="本次智能复习数%d" /> tools:text="本次智能复习数%d" />


<Button
<com.google.android.material.button.MaterialButton
android:id="@+id/tv_start_review" android:id="@+id/tv_start_review"
style="@style/common_button_style" style="@style/common_button_style"
android:text="@string/start_review" android:text="@string/start_review"
app:cornerRadius="8dp" app:cornerRadius="8dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/tv_see_memo"
app:layout_constraintTop_toBottomOf="@+id/tv_tips"
android:layout_marginStart="12dp"
app:layout_goneMarginStart="38dp"
/>

<com.google.android.material.button.MaterialButton
android:id="@+id/tv_see_memo"
style="@style/common_button_style"
android:text="@string/start_see_memo"
app:cornerRadius="8dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_tips" />
app:layout_constraintEnd_toStartOf="@+id/tv_start_review"
app:layout_constraintTop_toBottomOf="@+id/tv_tips"
android:layout_marginEnd="0dp"
app:layout_goneMarginEnd="38dp"
android:backgroundTint="@color/translation"
app:strokeColor="@color/theme_color"
app:strokeWidth="@dimen/line_height"
android:textColor="@color/theme_color"
android:visibility="gone"
tools:visibility="visible"
/>




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

+ 66
- 8
app/src/main/res/layout/fragment_memo.xml View File

<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".module.m_memo.MemoFragment">
xmlns:app="http://schemas.android.com/apk/res-auto">


<!-- TODO: Update blank fragment layout -->
<TextView
<data>
<variable
name="vm"
type="com.xkl.cdl.module.m_memo.MemoFragmentViewModel" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
tools:context=".module.m_memo.MemoFragment">

<View
android:id="@+id/v_top"
android:layout_width="match_parent"
android:layout_height="@dimen/title_bar_height"
app:layout_constraintTop_toTopOf="parent"
android:background="@color/white" />

<TextView
android:id="@+id/tv_memo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="@+id/v_top"
app:layout_constraintBottom_toBottomOf="@+id/v_top"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="@dimen/global_spacing"
android:textColor="@color/main_text_color"
android:textStyle="bold"
android:text="@string/memo" />

<com.suliang.common.widget.InputSearchEditText
android:id="@+id/edit_text_search"
android:layout_width="0dp"
android:layout_height="36dp"
app:layout_constraintStart_toEndOf="@+id/tv_memo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/v_top"
app:layout_constraintBottom_toBottomOf="@+id/v_top"
android:layout_marginStart="@dimen/global_spacing"
android:layout_marginEnd="@dimen/global_spacing"
android:background="@drawable/et_search_bg"
android:paddingStart="@dimen/global_spacing"
android:paddingEnd="@dimen/global_spacing"
android:gravity="center_vertical"
android:maxLines="1"
android:singleLine="true"
android:drawablePadding="4dp"
android:textColor="@color/main_text_color"
android:textSize="@dimen/smallSize"
android:hint="请输入课程名称..."
android:text="@={vm.etSearchLiveData}"
/>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@+id/v_top"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="@color/white_1"
android:overScrollMode="never"
android:scrollbars="none" />


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

+ 3
- 3
app/src/main/res/layout/include_main_learn_center_course_type_title.xml View File

android:id="@+id/tab_discern" android:id="@+id/tab_discern"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:drawableStart="@drawable/ic_discern"
android:drawableStart="@drawable/ic_course_discern"
android:text="认读" android:text="认读"
bind:svgColor="@{@color/theme_color}" bind:svgColor="@{@color/theme_color}"
android:textColor="@color/theme_color" android:textColor="@color/theme_color"
android:id="@+id/tab_spell" android:id="@+id/tab_spell"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:drawableStart="@drawable/ic_spell"
android:drawableStart="@drawable/ic_course_spell"
bind:svgColor="@{@color/theme_color}" bind:svgColor="@{@color/theme_color}"
android:textColor="@color/theme_color" android:textColor="@color/theme_color"
android:text="拼写" android:text="拼写"
android:id="@+id/tab_voice" android:id="@+id/tab_voice"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:drawableStart="@drawable/ic_voice"
android:drawableStart="@drawable/ic_course_voice"
android:text="辨音" android:text="辨音"
bind:svgColor="@{@color/theme_color}" bind:svgColor="@{@color/theme_color}"
android:textColor="@color/theme_color" android:textColor="@color/theme_color"

+ 74
- 0
app/src/main/res/layout/item_memo.xml View File

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:background="@drawable/shape_rounder_12_white"
android:padding="8dp"
android:paddingBottom="12dp">

<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/img_course_pack_cover"
style="@style/roundedCornerStyle"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_min="128dp"
android:layout_marginEnd="8dp"
android:scaleType="centerCrop"
app:layout_constraintDimensionRatio="h,102:136"
app:layout_constraintEnd_toStartOf="@+id/barrier"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="1dp"
android:layout_height="wrap_content"
app:barrierDirection="right"
app:constraint_referenced_ids="img_course_pack_cover" />

<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/img_course_icon_1"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/barrier"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/img_course_icon_2"
app:strokeWidth="10dp"
style="@style/roundedCornerStyle"/>

<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/img_course_icon_2"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/barrier"
app:layout_constraintTop_toBottomOf="@+id/img_course_icon_1"
app:layout_constraintBottom_toTopOf="@+id/img_course_icon_3"
app:strokeWidth="10dp"
android:visibility="invisible"
style="@style/roundedCornerStyle"/>

<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/img_course_icon_3"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/barrier"
app:layout_constraintTop_toBottomOf="@+id/img_course_icon_2"
app:layout_constraintBottom_toBottomOf="@+id/img_course_pack_cover"
app:strokeWidth="10dp"
android:visibility="invisible"
style="@style/roundedCornerStyle"
/>

<TextView
android:id="@+id/tv_course_pack_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/img_course_pack_cover" />


</androidx.constraintlayout.widget.ConstraintLayout>

+ 3
- 3
app/src/main/res/layout/main_item_coursepack.xml View File

android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:padding="3dp" android:padding="3dp"
android:src="@drawable/ic_discern"
android:src="@drawable/ic_course_discern"
android:visibility="gone" android:visibility="gone"
app:layout_constraintEnd_toStartOf="@+id/image_view_type_spell" app:layout_constraintEnd_toStartOf="@+id/image_view_type_spell"
app:layout_constraintHorizontal_bias="0" app:layout_constraintHorizontal_bias="0"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:padding="3dp" android:padding="3dp"
android:src="@drawable/ic_spell"
android:src="@drawable/ic_course_spell"
android:visibility="gone" android:visibility="gone"
app:layout_constraintEnd_toStartOf="@+id/image_view_type_voice" app:layout_constraintEnd_toStartOf="@+id/image_view_type_voice"
app:layout_constraintStart_toEndOf="@+id/image_view_type_discern" app:layout_constraintStart_toEndOf="@+id/image_view_type_discern"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:padding="3dp" android:padding="3dp"
android:src="@drawable/ic_voice"
android:src="@drawable/ic_course_voice"
android:visibility="gone" android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/image_view_type_spell" app:layout_constraintStart_toEndOf="@+id/image_view_type_spell"

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

<string name="quit_auto_play_title">你确定要退出自动播放吗?</string> <string name="quit_auto_play_title">你确定要退出自动播放吗?</string>
<string name="quit_auto_play_title_over">本课程自动播放完毕</string> <string name="quit_auto_play_title_over">本课程自动播放完毕</string>
<string name="course_introduction">课程简介</string> <string name="course_introduction">课程简介</string>
<string name="start_see_memo">查看备忘本</string>


</resources> </resources>

+ 12
- 0
app/svg/drawable/ic_course_composition.xml View File

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M24.1111,1.8667C25.228,1.8667 26.1333,2.772 26.1333,3.8889L26.1333,24.1111C26.1333,25.228 25.228,26.1333 24.1111,26.1333L3.8889,26.1333C2.772,26.1333 1.8667,25.228 1.8667,24.1111L1.8667,3.8889C1.8667,2.772 2.772,1.8667 3.8889,1.8667L24.1111,1.8667ZM10.0247,11.2095L5.9111,11.2095L5.9111,12.9379L8.279,12.9379L8.279,19.0737C8.279,19.4367 8.1235,19.7478 7.8123,20.007L8.5037,21.6317C9.8346,20.8194 11.0963,19.9033 12.2716,18.8663L11.7877,16.9823C11.1654,17.5527 10.5778,18.054 10.0247,18.5033L10.0247,11.2095ZM20.758,6.7675L12.2543,6.7675L12.2543,8.4959L19.0296,8.4959L19.0296,11.9527L12.963,11.9527L12.963,19.7996C12.963,20.9577 13.516,21.5453 14.6568,21.5453L19.1679,21.5453C20.2741,21.5453 20.9827,21.2688 21.3111,20.7502C21.6568,20.1972 21.916,19.0219 22.0889,17.2416L20.2914,16.6539L20.257,17.1922C20.1895,18.1274 20.0972,18.7799 19.9802,19.1601C19.842,19.6095 19.4617,19.8515 18.8741,19.8515L15.4519,19.8515C14.9679,19.8169 14.7259,19.5577 14.7259,19.0737L14.7259,13.6811L20.758,13.6811L20.758,6.7675ZM8.3136,6.2144L7.0346,7.4589C8.2617,8.3577 9.2123,9.2046 9.8691,9.9996L11.1136,8.7379C10.3358,7.9083 9.4025,7.0614 8.3136,6.2144Z"
android:strokeWidth="1"
android:fillColor="#8757E6"
android:fillType="evenOdd"
android:strokeColor="#00000000"/>
</vector>

app/svg/drawable/ic_discern.xml → app/svg/drawable/ic_course_discern.xml View File


+ 12
- 0
app/svg/drawable/ic_course_literacy.xml View File

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="22"
android:viewportHeight="22">
<path
android:fillColor="#F7874F"
android:fillType="evenOdd"
android:pathData="M18.9444,1.4667C19.822,1.4667 20.5333,2.178 20.5333,3.0556L20.5333,18.9444C20.5333,19.822 19.822,20.5333 18.9444,20.5333L3.0556,20.5333C2.178,20.5333 1.4667,19.822 1.4667,18.9444L1.4667,3.0556C1.4667,2.178 2.178,1.4667 3.0556,1.4667L18.9444,1.4667ZM11.2645,12.5261C10.6269,13.9912 9.8537,15.2529 8.9584,16.2974L10.2064,17.2063C11.1017,16.0668 11.8614,14.6967 12.5126,13.123L11.2645,12.5261ZM14.968,12.4583L13.8149,13.1909C14.7916,14.6288 15.5649,15.9447 16.1346,17.1385L17.3556,16.2839C16.7994,15.1986 15.999,13.9234 14.968,12.4583ZM7.846,8.5649L4.6444,8.5649L4.6444,9.9215L6.4758,9.9215L6.4758,14.7374C6.4758,15.0222 6.3537,15.2664 6.1231,15.4699L6.6657,16.7451C7.6018,16.1075 8.4836,15.3885 9.3246,14.5746L8.9312,13.0959C8.5514,13.4757 8.1851,13.8285 7.846,14.1405L7.846,8.5649ZM16.4873,5.2142L9.8537,5.2142L9.8537,11.97L16.4873,11.97L16.4873,5.2142ZM15.0629,6.5843L15.0629,10.6134L11.2645,10.6134L11.2645,6.5843L15.0629,6.5843ZM6.5437,4.6444L5.5533,5.6212C6.503,6.3266 7.2491,6.9778 7.7646,7.6018L8.7277,6.625C8.1308,5.9739 7.3983,5.3092 6.5437,4.6444Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

+ 14
- 0
app/svg/drawable/ic_course_pinying.xml View File

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="112"
android:viewportHeight="112">
<path
android:fillColor="#EB54D8"
android:pathData="M10.5,10.5l-2.5,2.4 0,43.1 0,43.1 2.5,2.4 2.4,2.5 43.1,-0 43.1,-0 2.4,-2.5 2.5,-2.4 0,-43.1 0,-43.1 -2.5,-2.4 -2.4,-2.5 -43.1,-0 -43.1,-0 -2.4,2.5zM64,28.5c-5.3,5.4 -7,6.5 -9.7,6.5 -1.8,-0 -3.3,-0.2 -3.3,-0.5 0,-0.3 1.7,-3.2 3.7,-6.5l3.8,-6 6,-0 5.9,-0 -6.4,6.5zM68.7,40c7.5,3.4 8.3,5.8 8.3,25.7l0,17.3 -4.5,-0c-3.8,-0 -4.5,-0.3 -4.5,-1.9 0,-1.8 -0.3,-1.8 -4.6,0.6 -5.6,3.1 -15.1,3.6 -20.1,1 -6.3,-3.3 -8.3,-12.5 -4,-18.7 3.5,-5.2 8.7,-7.3 18.9,-7.8 9.8,-0.5 10.8,-1.4 6.7,-6.6 -1.7,-2.2 -2.8,-2.6 -7,-2.6 -4.9,-0 -8.9,2.3 -8.9,5.1 0,1.1 -9.4,0.8 -10.6,-0.3 -0.8,-0.9 2.1,-7.1 4.4,-9.1 5.3,-4.8 18.4,-6.2 25.9,-2.7z"
android:strokeColor="#00000000" />
<path
android:fillColor="#EB54D8"
android:pathData="M53,64.7c-3,1.1 -5,3.7 -5,6.4 0,7.2 13.7,6.3 17.5,-1.1 2.7,-5.3 2.2,-6 -4.7,-5.9 -3.5,-0 -7,0.3 -7.8,0.6z"
android:strokeColor="#00000000" />
</vector>

+ 18
- 0
app/svg/drawable/ic_course_soundmark.xml View File

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="112"
android:viewportHeight="112">
<path
android:fillColor="#8757E6"
android:pathData="M10,10c-1.9,1.9 -2,3.3 -2,46 0,42.7 0.1,44.1 2,46 1.9,1.9 3.3,2 46,2 42.7,-0 44.1,-0.1 46,-2 1.9,-1.9 2,-3.3 2,-46 0,-42.7 -0.1,-44.1 -2,-46 -1.9,-1.9 -3.3,-2 -46,-2 -42.7,-0 -44.1,0.1 -46,2zM54.3,34.8l3.5,2 4.9,-2.5c4,-2.1 5.8,-2.4 11,-2 7.7,0.6 12.9,3.2 17,8.6 5.4,7 5.6,15.1 0.3,17.8 -1.6,0.8 -7,1.3 -15.2,1.3 -12.2,-0 -12.8,0.1 -12.8,2 0,9.8 12.9,13.3 20.9,5.7 4.3,-4.1 6.4,-5 8.4,-3.4 2.5,2.1 2.6,4.5 0.4,7.9 -6.3,9.3 -23.4,11.6 -33.9,4.4l-3.6,-2.4 -6.1,3.1c-8.2,4 -15.5,4.7 -21.3,1.8 -5.4,-2.6 -7.8,-6.5 -7.8,-12.6 0,-4.1 0.5,-5.2 3.5,-8 3.4,-3.3 5.1,-3.9 22.5,-7.6 5.1,-1.1 5.1,-1.1 4.5,-4.2 -0.9,-4.1 -3,-5.7 -7.6,-5.7 -5.2,-0 -7.9,1.5 -11.1,6.1 -3.3,4.7 -6.9,5.2 -9.4,1.4 -1.5,-2.2 -1.5,-3 -0.3,-6.2 2.7,-6.9 10.5,-10.5 21.6,-10 4.9,0.3 8.2,1 10.6,2.5z"
android:strokeColor="#00000000" />
<path
android:fillColor="#8757E6"
android:pathData="M68.5,42.2c-3,1.6 -5.5,5 -5.5,7.5 0,2.2 0.3,2.3 10,2.3 6.6,-0 10,-0.4 10,-1.1 0,-0.6 -0.7,-2.6 -1.5,-4.4 -2.4,-4.9 -8.3,-6.9 -13,-4.3z"
android:strokeColor="#00000000" />
<path
android:fillColor="#8757E6"
android:pathData="M43,59.2c-10.6,2.7 -13.3,5.8 -9.2,10.6 2.9,3.4 8.8,3.3 13.1,-0.3 2.2,-1.9 3.2,-3.8 3.7,-7.1 0.3,-2.5 0.5,-4.6 0.3,-4.8 -0.2,-0.2 -3.8,0.6 -7.9,1.6z"
android:strokeColor="#00000000" />
</vector>

app/svg/drawable/ic_spell.xml → app/svg/drawable/ic_course_spell.xml View File


app/svg/drawable/ic_spoken.xml → app/svg/drawable/ic_course_spoken.xml View File

<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp" android:width="18dp"
android:height="18dp" android:height="18dp"
android:tint="#40A540"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="@android:color/white"
android:fillColor="#40A540"
android:pathData="M12,15c1.66,0 2.99,-1.34 2.99,-3L15,6c0,-1.66 -1.34,-3 -3,-3S9,4.34 9,6v6c0,1.66 1.34,3 3,3zM17.3,12c0,3 -2.54,5.1 -5.3,5.1S6.7,15 6.7,12L5,12c0,3.42 2.72,6.23 6,6.72L11,22h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z" /> android:pathData="M12,15c1.66,0 2.99,-1.34 2.99,-3L15,6c0,-1.66 -1.34,-3 -3,-3S9,4.34 9,6v6c0,1.66 1.34,3 3,3zM17.3,12c0,3 -2.54,5.1 -5.3,5.1S6.7,15 6.7,12L5,12c0,3.42 2.72,6.23 6,6.72L11,22h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z" />
</vector> </vector>

app/svg/drawable/ic_voice.xml → app/svg/drawable/ic_course_voice.xml View File


+ 26
- 0
lib/common/src/main/java/com/suliang/common/extension/RxScheduler.kt.kt View File

* create 2022/4/1 18:02 * create 2022/4/1 18:02
* Describe: https://yuzhiqiang.blog.csdn.net/article/details/88233525 * Describe: https://yuzhiqiang.blog.csdn.net/article/details/88233525
*/ */

/**
* 单线程 : 只有diskIo单线程
* @return ObservableTransformer<T, T>
*/
fun <T> diskIo2DiskIo(): ObservableTransformer<T, T> { fun <T> diskIo2DiskIo(): ObservableTransformer<T, T> {
return ObservableTransformer { upstream -> return ObservableTransformer { upstream ->
upstream!!.subscribeOn(Schedulers.from(AppExecutors.diskIO)) upstream!!.subscribeOn(Schedulers.from(AppExecutors.diskIO))
} }
} }


/**
* 多线程
* @return ObservableTransformer<T, T>
*/
fun <T> io2Io(): ObservableTransformer<T, T> {
return ObservableTransformer { upstream ->
upstream!!.subscribeOn(Schedulers.from(AppExecutors.io))
.observeOn(Schedulers.from(AppExecutors.io))
}
}
/**
* 多线程
* @return ObservableTransformer<T, T>
*/
fun <T> io2Main(): ObservableTransformer<T, T> {
return ObservableTransformer { upstream ->
upstream!!.subscribeOn(Schedulers.from(AppExecutors.io))
.observeOn(Schedulers.from(AppExecutors.mainThread))
}
}

fun <T> diskIo2Main(): ObservableTransformer<T, T> { fun <T> diskIo2Main(): ObservableTransformer<T, T> {
return ObservableTransformer { upstream -> return ObservableTransformer { upstream ->
upstream!!.subscribeOn(Schedulers.from(AppExecutors.diskIO)) upstream!!.subscribeOn(Schedulers.from(AppExecutors.diskIO))

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

fun format(formatValue : Long, format : String) : String { fun format(formatValue : Long, format : String) : String {
return SimpleDateFormat(format).format(formatValue) return SimpleDateFormat(format).format(formatValue)
} }
/**
* 格式化字符串格式时间为日期格式
* @param formatValue String 字符串格式的日期
* @param format String 字符串格式样式,如 "yyyy-MM-dd HH:mm:ss"
* @return Date 日期格式
*/
@SuppressLint("SimpleDateFormat")
@Throws(Exception::class)
@JvmStatic
fun format(formatValue:String, format : String):Date{
return SimpleDateFormat(format).parse(formatValue)
}
/** /**
* 获取时间段 * 获取时间段

Loading…
Cancel
Save