@@ -67,11 +67,11 @@ | |||
<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_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_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_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/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" /> | |||
@@ -92,6 +92,7 @@ | |||
<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_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_task_image.xml" value="0.23353596757852077" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_video_adapter.xml" value="0.67" /> | |||
@@ -103,9 +104,18 @@ | |||
<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/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_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_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_memo.xml" value="0.21574074074074073" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_my.xml" value="0.44166666666666665" /> |
@@ -70,3 +70,46 @@ Material Design Components 组件使用 https://www.jianshu.com/p/bc71b4179cb2 | |||
// 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(); |
@@ -0,0 +1,48 @@ | |||
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; | |||
} | |||
} | |||
} |
@@ -64,13 +64,13 @@ class AdapterCoursePackWithLearCenter(vm: CoursePackFragmentViewModel) : | |||
AppConstants.COURSE_TYPE_ENGLISH_VOICE -> { | |||
imageViewTypeVoice.run { | |||
visibility = View.VISIBLE | |||
setImageResource(R.drawable.ic_voice) | |||
setImageResource(R.drawable.ic_course_voice) | |||
} | |||
} | |||
AppConstants.COURSE_TYPE_ENGLISH_SPOKEN -> { | |||
imageViewTypeVoice.run{ | |||
visibility = View.VISIBLE | |||
setImageResource(R.drawable.ic_spoken) | |||
setImageResource(R.drawable.ic_course_spoken) | |||
} | |||
} | |||
} |
@@ -0,0 +1,170 @@ | |||
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 | |||
} | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
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>>() | |||
} |
@@ -15,17 +15,13 @@ class CourseDetail:Serializable{ | |||
var st_after: Double = AppConstants.NOT_DOING //学后总成绩 : -1代表没有进行学后总测试 | |||
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 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 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 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 e = 0.0 //学习效率 | |||
var valid: HashMap<Long, Long> = hashMapOf() //有效学习时长,单位毫秒 key=>分时标记,value=>时长 | |||
@@ -35,6 +31,10 @@ class CourseDetail:Serializable{ | |||
var current_week_total_durations: Long = 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 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 | |||
} |
@@ -37,35 +37,38 @@ data class CoursePack( | |||
field = value | |||
notifyPropertyChanged(BR.learnProgress) | |||
} | |||
override fun equals(other: Any?): Boolean { | |||
override fun equals(other : Any?) : Boolean { | |||
if (this === other) return true | |||
if (javaClass != other?.javaClass) return false | |||
other as CoursePack | |||
if (coursePackId != other.coursePackId) return false | |||
if (coursePackName != other.coursePackName) return false | |||
if (!cover.contentEquals(other.cover)) return false | |||
if (summary != other.summary) return false | |||
if (subjectId != other.subjectId) return false | |||
if (coursePackType != other.coursePackType) return false | |||
if (inCoursePackPosition != other.inCoursePackPosition) return false | |||
if (childrenCourses != other.childrenCourses) return false | |||
if (learnProgress != other.learnProgress) return false | |||
return true | |||
} | |||
override fun hashCode(): Int { | |||
override fun hashCode() : Int { | |||
var result = coursePackId.hashCode() | |||
result = 31 * result + coursePackName.hashCode() | |||
result = 31 * result + cover.contentHashCode() | |||
result = 31 * result + summary.hashCode() | |||
result = 31 * result + subjectId | |||
result = 31 * result + coursePackType | |||
result = 31 * result + inCoursePackPosition | |||
result = 31 * result + childrenCourses.hashCode() | |||
result = 31 * result + learnProgress.hashCode() | |||
return result | |||
} | |||
} |
@@ -2,15 +2,20 @@ package com.xkl.cdl.data.manager | |||
import appApi.AppApi | |||
import com.suliang.common.util.AppGlobals | |||
import com.suliang.common.util.DateUtil | |||
import com.suliang.common.util.file.FileUtil | |||
import com.xkl.cdl.data.AppConstants | |||
import com.xkl.cdl.data.bean.course.CoursePack | |||
import com.xkl.cdl.data.bean.course.ExamBean | |||
import com.xkl.cdl.data.bean.course.Lesson | |||
import java.io.* | |||
import java.lang.Exception | |||
import java.math.BigDecimal | |||
import java.math.RoundingMode | |||
import java.text.DateFormat | |||
import java.text.DecimalFormat | |||
import java.text.SimpleDateFormat | |||
import java.util.* | |||
import java.util.zip.ZipFile | |||
import java.util.zip.ZipInputStream | |||
@@ -402,4 +407,46 @@ object CourseManager { | |||
} ?: 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 | |||
} | |||
} |
@@ -111,8 +111,8 @@ object DBCourseManager { | |||
lessonPositionInList = positionIndex | |||
this.wordIds = wordIds //内容 | |||
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 }) //课时学前测试成绩 | |||
afterTestScore = detail.after.getOrElse(key, { AppConstants.NOT_DOING }) //课时学后测试成绩 | |||
this.lessonType = lessonType |
@@ -1,12 +1,16 @@ | |||
package com.xkl.cdl.data.repository | |||
import appApi.AppApi | |||
import com.google.protobuf.ProtocolStringList | |||
import com.googlecode.protobuf.format.JsonFormat | |||
import com.suliang.common.extension.io2Io | |||
import com.suliang.common.util.file.FileUtil | |||
import com.xkl.cdl.data.AppConstants | |||
import com.xkl.cdl.data.bean.course.CourseDetail | |||
import com.xkl.cdl.data.bean.course.Lesson | |||
import com.xkl.cdl.data.manager.db.DBCourseManager | |||
import com.xkl.cdl.data.manager.db.DbControlBase | |||
import com.xkl.cdl.module.XKLApplication | |||
import io.reactivex.rxjava3.core.Observable | |||
import mqComsumerV1.Struct | |||
import java.io.File | |||
@@ -18,7 +22,8 @@ import java.io.FileOutputStream | |||
* Describe: 数据提供 | |||
*/ | |||
object DataRepository { | |||
// TODO: 2022/6/1 需自己实现获取课程收藏数据,同时需自己处理保存数据 | |||
/** 获取课程的收藏本 */ | |||
fun getCourseCollect() : Observable<HashMap<String,Long>>{ | |||
return Observable.create{ | |||
@@ -28,27 +33,63 @@ object DataRepository { | |||
} | |||
/** 获取复习的内容 */ | |||
fun getReviewData() : Observable<Array<String>> { | |||
fun getReviewData(subjectId : Int,coursePackId : Long,courseId : Long) : Observable<ProtocolStringList> { | |||
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() | |||
} | |||
} | |||
/** 获取课程的详情 */ | |||
fun getCourseStatistics(subjectId:Int,coursePackId:Long,courseId:Long): Observable<CourseDetail>{ | |||
fun getCourseDetails(subjectId:Int, coursePackId:Long, courseId:Long): Observable<CourseDetail>{ | |||
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.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() | |||
// } | |||
} | |||
} | |||
@@ -96,9 +137,15 @@ object DataRepository { | |||
//计算词汇量和学习效率 | |||
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 ){ //当前为测试 | |||
val s = when (record.getExam(0).type.toInt()) { //测试类型 | |||
@@ -124,20 +171,29 @@ object DataRepository { | |||
//序列化输出 | |||
val string = JsonFormat.printToString(record.build()) | |||
val saveArray = record.build().toByteArray() | |||
val saveStringName = "${fileName}_jsonString.txt" | |||
FileUtil.writeBytesToFile(it,saveStringName,string.toByteArray()) | |||
val saveByteArrayName = "${fileName}_byteArray.txt" | |||
FileUtil.writeBytesToFile(it,saveByteArrayName,saveArray) | |||
} | |||
}*/ | |||
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() | |||
} | |||
} |
@@ -2,15 +2,16 @@ package com.xkl.cdl.module | |||
import android.app.Application | |||
import com.suliang.common.util.LogUtil | |||
import com.suliang.common.util.file.FileUtil | |||
import com.tencent.mmkv.MMKV | |||
import io.reactivex.rxjava3.exceptions.UndeliverableException | |||
import io.reactivex.rxjava3.functions.Consumer | |||
import io.reactivex.rxjava3.plugins.RxJavaPlugins | |||
import mobile_cache.MobileCache | |||
import mobile_cache.Mobile_cache | |||
import net.sqlcipher.database.SQLiteDatabase | |||
import java.io.File | |||
import java.io.IOException | |||
import java.lang.IllegalArgumentException | |||
import java.lang.IllegalStateException | |||
import java.lang.NullPointerException | |||
import java.util.* | |||
/** | |||
@@ -19,17 +20,26 @@ import java.util.* | |||
* Describe: | |||
*/ | |||
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() { | |||
super.onCreate() | |||
SQLiteDatabase.loadLibs(this) | |||
// ImageLoader.mStrategy = GlideLoaderStrategy() | |||
LogUtil.e(UUID.randomUUID().toString().replace("-","")) | |||
//初始MMKV存储 | |||
val rootDir = MMKV.initialize(this) | |||
LogUtil.e(rootDir) | |||
setRxJavaErrorHandler() | |||
// HookMobileCache().hook() | |||
} | |||
/*** | |||
* 避免 调用多次onError。正常来说第一次onError会走正常的Observer处理,其他会走ErrorHandler。通过此方法捕捉多次的error | |||
*/ |
@@ -95,7 +95,7 @@ class LearnCReadingViewModel : LearnBaseViewModel() { | |||
} | |||
/** | |||
* 添加到收藏 | |||
* 取消收藏 | |||
* @return MutableLiveData<Long> | |||
*/ | |||
fun removeCollect() : MutableLiveData<Long> { | |||
@@ -138,7 +138,6 @@ class LearnCReadingViewModel : LearnBaseViewModel() { | |||
} | |||
fun saveData() { | |||
// TODO: 2022/5/20 保存记录点 如果学习完成,则设置进度点和修改为学习完成 | |||
Observable.create<Boolean> { emitter -> | |||
val record = Struct.Record.newBuilder().apply { | |||
addEntity(Struct.LearnEntity.newBuilder().apply { |
@@ -14,8 +14,10 @@ import com.xkl.cdl.data.event.LearnEventData | |||
import com.xkl.cdl.data.manager.db.DBCourseManager | |||
import com.xkl.cdl.data.manager.db.DbControlBase | |||
import com.xkl.cdl.data.repository.DataRepository | |||
import com.xkl.cdl.module.XKLApplication | |||
import com.xkl.videoplayer.bean.PineMediaPlayerBean | |||
import io.reactivex.rxjava3.core.Observable | |||
import io.reactivex.rxjava3.functions.BiFunction | |||
import mqComsumerV1.Struct | |||
@@ -172,8 +174,20 @@ class LearnCVideoViewModel : LearnBaseViewModel() { | |||
uploadDate.value = true | |||
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 { | |||
//进度点 | |||
addEntity(Struct.LearnEntity.newBuilder().apply { | |||
@@ -206,7 +220,13 @@ class LearnCVideoViewModel : LearnBaseViewModel() { | |||
} | |||
emitter.onNext(DataRepository.saveRecord(record)) | |||
emitter.onComplete() | |||
}.compose(diskIo2Main()).subscribe { | |||
}, { t1, t2 -> | |||
t1 && t2 | |||
}).compose(diskIo2Main()).subscribe { | |||
if (!it){ | |||
LogUtil.e("视频数据保存有误") | |||
return@subscribe | |||
} | |||
lesson.videoPlayTime = currentPlayTime | |||
LogUtil.e("当前播放时间:$currentPlayTime") | |||
when{ |
@@ -591,7 +591,6 @@ class LearnExamActivity : BaseActivityVM<ActivityLearnExamBinding, LearnExamView | |||
/** 测试完成 : 弹窗显示 */ | |||
private fun testOver() { | |||
// TODO: 2022/5/17 作文测试弹窗 | |||
//对话框信息实体 | |||
val learnDialogBean = LearnDialogBean(AppConstants.DIALOG_TYPE_EXAM_OVER).apply { | |||
examType = vm.intentData.examType |
@@ -646,14 +646,9 @@ class LearnExamViewModel : LearnBaseViewModel() { | |||
private fun saveData() { | |||
showHideLoading(true) | |||
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({ | |||
showHideLoading(false) | |||
sendEventBus() //返回发送数据 |
@@ -97,7 +97,7 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView | |||
/** 横幅 自动播放 与 复习时使用 */ | |||
private fun initBanner() { | |||
// TODO: 2022/4/25 初始化横幅,默认隐藏 | |||
// 初始化横幅,默认隐藏 | |||
if (vm.learnData.isAutoPlay) { | |||
binding.tvBanner.apply { | |||
visibility = View.VISIBLE |
@@ -293,11 +293,6 @@ class LearnWordViewModel : LearnBaseViewModel() { | |||
fun saveData() { | |||
showHideLoading(true) | |||
Observable.create<Boolean> { | |||
viewModelScope.launch { | |||
delay(1000) | |||
it.onNext(true) | |||
} | |||
// TODO: 2022/4/14 传递保存record信息 | |||
// record 已经实例化并已经将数据保存 | |||
if (!saveInit) { | |||
//自动播放不修改课时信息 | |||
@@ -314,9 +309,8 @@ class LearnWordViewModel : LearnBaseViewModel() { | |||
record.addDuration(saveCurrentLearnDuration()) | |||
saveInit = true | |||
} | |||
DataRepository.saveRecord(record) | |||
// LogUtil.e(JsonFormat.printToString(record.build())) | |||
it.onNext(DataRepository.saveRecord(record)) | |||
it.onComplete() | |||
}.compose(diskIo2Main()).subscribe({ | |||
showHideLoading(false) | |||
sendEventBus() //返回发送数据 |
@@ -39,7 +39,7 @@ import com.xkl.cdl.module.m_center_learn.coursechildren.CourseMainFragment | |||
import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly | |||
/** | |||
* 课程中心 | |||
* 课程包主页中心 | |||
*/ | |||
class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CoursePackMainActivityViewModel>() { | |||
@@ -56,9 +56,6 @@ class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CourseP | |||
//子课程对应的Fragment | |||
private var childFragments = mutableListOf<Fragment>() | |||
// //更多按钮的弹窗显示 | |||
// private var moreDialog : BottomSheetDialog? = null | |||
//自动播放次数的弹窗选择 | |||
private var autoPlaySeletDialog : BottomSheetDialog? = null | |||
@@ -295,14 +292,6 @@ class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CourseP | |||
} | |||
} | |||
override fun onStop() { | |||
super.onStop() | |||
//如果是中文项目,更新课程包的进度 | |||
if (vm.coursePack.subjectId == AppConstants.SUBJECT_CHINESE) { | |||
vm.coursePack.learnProgress = vm.coursePack.childrenCourses[0].courseLearnProgress | |||
} | |||
} | |||
/** ViewModel Factory工厂 */ | |||
inner class ViewModelFactory(private val subjectId : Int, private val coursePackInPosition : Int) : |
@@ -1,7 +1,9 @@ | |||
package com.xkl.cdl.module.m_center_learn | |||
import androidx.lifecycle.LifecycleOwner | |||
import androidx.lifecycle.MutableLiveData | |||
import com.suliang.common.base.viewmodel.BaseViewModel | |||
import com.xkl.cdl.data.AppConstants | |||
import com.xkl.cdl.data.manager.CourseManager | |||
/** | |||
@@ -17,4 +19,11 @@ class CoursePackMainActivityViewModel(subjectId: Int , coursePackInPosition : In | |||
//设置显示当前课程的进度值和显示内容 | |||
val currentCourseProgress = MutableLiveData<Double>() | |||
override fun onStop(owner : LifecycleOwner) { | |||
super.onStop(owner) | |||
//如果是中文项目,更新课程包的进度 | |||
if (coursePack.subjectId == AppConstants.SUBJECT_CHINESE) { | |||
coursePack.learnProgress = coursePack.childrenCourses[0].courseLearnProgress | |||
} | |||
} | |||
} |
@@ -85,9 +85,8 @@ class LearnCenterFragment : BaseFragmentVM<FragmentLearnCenterBinding, LearnCent | |||
//TabLayout 与 ViewPager2 进行关联, 并进行tab设值 | |||
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() | |||
@@ -104,20 +103,6 @@ class LearnCenterFragment : BaseFragmentVM<FragmentLearnCenterBinding, LearnCent | |||
} | |||
} | |||
// /** 搜索框 输入监听 */ | |||
// 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() { |
@@ -81,7 +81,7 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
vm.allLesson[learnEventData.leesonPositionIndex].let { | |||
val key = "${it.chapterId}_${it.lessonId}" | |||
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 -> | |||
vm.courseDetail.exam_w_r_list.putAll(it1) | |||
} | |||
@@ -98,8 +98,8 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
//单词类 | |||
AppConstants.LESSON_TYPE_WORD ->{ | |||
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] //更新课时学习点 | |||
course_learn_point = "${key}_${it.wordIds[it.learnedIndex]}" //更新课程学习点 | |||
} | |||
@@ -130,8 +130,8 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
} | |||
//知识点 | |||
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] //更新课时学习点 | |||
course_learn_point = "${key}_${it.wordIds[it.learnedIndex]}" //更新课程学习点 | |||
} | |||
@@ -205,7 +205,6 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
//学后测试结束,弹窗动作 课时重新学习 | |||
AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN -> { | |||
//课时学后测试,点击重学 | |||
// TODO: 2022/4/22 清除当前课时数据,并更新当前课时,后进入学习界面 | |||
vm.allLesson[learnEventData.leesonPositionIndex].learnIsOver = false | |||
vm.relearnLesson(learnEventData.leesonPositionIndex).observe(this) { | |||
//item更新 | |||
@@ -390,7 +389,7 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
dialog.dismissAllowingStateLoss() | |||
when (action) { | |||
AppConstants.DIALOG_START_LEARN -> { //跳过测试 继续学习 | |||
// TODO: 2022/4/21 进入学习界面 | |||
// TODO: 2022/4/21 进入学习界面 ,不能跳过测试,必须学习 | |||
} | |||
// 开始测试 | |||
AppConstants.DIALOG_START_TEST -> startLessonTest(lesson, AppConstants.TEST_TYPE_BEFORE, it) |
@@ -115,9 +115,9 @@ class CourseMainFragment : BaseFragmentVM<FragmentCourseMainBinding, CourseMainF | |||
* 改变加载的子Fragment 单词类 | |||
*/ | |||
private fun changeChildrenFragmentForWord() { | |||
// TODO: 2022/5/5 复习页面加载 | |||
currentFragment = vm.courseDetail.let { | |||
when{ | |||
vm.reviewDataList.size > 0 -> CourseReviewFragment.newInstanceFromLearningCenter() //有复习 | |||
it.st_before == AppConstants.NOT_DOING -> CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_BEFORE_TOTAL) //学前总测未做 | |||
it.courseLearnProgress != AppConstants.DOING_OVER -> CourseLessonFragment.newInstance() //学习未完成 | |||
else -> CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL) //学习完成,学后总测试界面 | |||
@@ -130,9 +130,9 @@ class CourseMainFragment : BaseFragmentVM<FragmentCourseMainBinding, CourseMainF | |||
* 改变加载的子Fragment 作文 | |||
*/ | |||
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) | |||
} |
@@ -3,6 +3,7 @@ package com.xkl.cdl.module.m_center_learn.coursechildren | |||
import androidx.lifecycle.LifecycleOwner | |||
import androidx.lifecycle.MutableLiveData | |||
import com.google.common.base.Joiner | |||
import com.google.protobuf.ProtocolStringList | |||
import com.suliang.common.base.viewmodel.BaseViewModel | |||
import com.suliang.common.extension.diskIo2DiskIo | |||
import com.suliang.common.extension.diskIo2Main | |||
@@ -22,15 +23,15 @@ import com.xkl.cdl.data.manager.CourseManager | |||
import com.xkl.cdl.data.manager.db.DBCourseManager | |||
import com.xkl.cdl.data.manager.db.DbControlBase | |||
import com.xkl.cdl.data.repository.DataRepository | |||
import com.xkl.cdl.module.XKLApplication | |||
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.Scheduler | |||
import io.reactivex.rxjava3.functions.BiFunction | |||
import io.reactivex.rxjava3.schedulers.Schedulers | |||
import mqComsumerV1.Struct | |||
import java.io.File | |||
import java.util.* | |||
import kotlin.collections.ArrayList | |||
import kotlin.collections.HashMap | |||
/** | |||
@@ -54,7 +55,10 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() { | |||
lateinit var collectList : HashMap<String, Long> | |||
/** 复习的数据 */ | |||
lateinit var reviewDataList : MutableList<String> | |||
lateinit var reviewDataList : MutableList<LearnWord> | |||
/* 复习数据是否进行了内容赋值,如果进行了赋值,则直接复习,如果没有进行赋值,需要在复习前,进行查询赋值 */ | |||
var isAssignmentReviewData = false | |||
/** 课程所有课时 */ | |||
lateinit var allLesson : List<Lesson> | |||
@@ -68,7 +72,7 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() { | |||
//获取课程的章节数据 | |||
fun loadMain() : 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 | |||
course.courseLearnProgress = it.courseLearnProgress | |||
coursePackMainActivityVM.currentCourseProgress.postValue(course.courseLearnProgress) | |||
@@ -76,9 +80,12 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() { | |||
}.flatMap { | |||
allLesson = it | |||
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 | |||
}).compose(diskIo2Main()).subscribe { | |||
mutableLiveData.value = it | |||
@@ -86,6 +93,38 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() { | |||
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 { | |||
return when (course.courseType) { | |||
@@ -119,7 +158,7 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() { | |||
fun modifyLessonErrorNumber() { | |||
//重设detail中的错误数, exam_w_r_list为所有的测试前错误,因为时学前总测,所以,可以根据这个字段来计算每个课时的错误数 | |||
//记录错误数: key :chapterId_lessonId value :错误数 | |||
val errorNumber = hashMapOf<String, Int>() | |||
val errorNumber = hashMapOf<String, Long>() | |||
courseDetail.exam_w_r_list.keys.forEach { | |||
val split = it.split("_") | |||
if (split.size == 3) { | |||
@@ -131,7 +170,7 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() { | |||
//更新lesson中的错误数 | |||
allLesson.forEach { | |||
val key = "${it.chapterId}_${it.lessonId}" | |||
it.errorNumber = errorNumber.getOrElse(key, { 0 }) | |||
it.errorNumber = errorNumber.getOrElse(key, { 0 }).toInt() | |||
} | |||
} | |||
@@ -213,8 +252,9 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() { | |||
val subjectProgress = CourseManager.calculateSubjectProgressWithCourseLessonRelearn(course.subjectId, course.coursePackId, | |||
course.courseId, courseProgress) | |||
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 { | |||
//更新lesson | |||
val lesson = allLesson[lessonPositionIndex].apply { | |||
@@ -268,49 +308,30 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() { | |||
val subjectProgress = CourseManager.calculateSubjectProgressWithCourseRelearn(course.subjectId, course.coursePackId, | |||
course.courseId) | |||
Observable.fromCallable { | |||
// TODO: 2022/5/5 调用课程重学 重新请求详情 | |||
val file = File(FileUtil.getSaveDirPath("appcache"), "${course.subjectId}_${course.coursePackId}_${course.courseId}") | |||
if (file.exists()) { | |||
file.delete() | |||
} | |||
// 调用课程重学 后会再次重新调用loadMain | |||
XKLApplication.mobileCache.relearn(course.subjectId.toLong(),course.coursePackId,course.courseId,0, | |||
0, 0.0,0.0,subjectProgress) | |||
}.compose(diskIo2DiskIo()).subscribe { | |||
courseDetail.run { | |||
courseLearnProgress = 0.0 | |||
st_before = AppConstants.NOT_DOING | |||
st_after = AppConstants.NOT_DOING | |||
before.clear() | |||
after.clear() | |||
right.clear() | |||
wrong.clear() | |||
lesson_learn_point.clear() | |||
exam_w_r_list.clear() | |||
course_learn_point = "" | |||
rl += 1 | |||
temporary_words.clear() | |||
vp.clear() | |||
exercise_schedule.clear() | |||
} | |||
result.postValue(true) | |||
} | |||
return result | |||
} | |||
// TODO: 2022/5/6 这里在退出时进行了统计数据缓存保存,在loadMain的时候进行了read缓存 ,后期需要清除 | |||
override fun onDestroy(owner : LifecycleOwner) { | |||
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) | |||
}*/ | |||
/** | |||
* 单词类,自动播放,查询自动播放使用的单词 | |||
@@ -373,7 +394,7 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() { | |||
// TODO: 2022/5/19 查询课堂练习的收藏数据 需要给 CompositionReadingBean设置收藏id | |||
Observable.create<List<CompositionReadingBean>> { | |||
result.postValue(LearnData(entity).apply { | |||
readingList = DBCourseManager.queryCompositionReading(dbControlBase,entity) | |||
readingList = DBCourseManager.queryCompositionReading(dbControlBase, entity) | |||
}) | |||
it.onComplete() | |||
}.compose(diskIo2Main()).subscribe() |
@@ -1,43 +1,112 @@ | |||
package com.xkl.cdl.module.m_center_learn.coursechildren | |||
import android.os.Bundle | |||
import android.view.View | |||
import androidx.lifecycle.ViewModelProvider | |||
import com.suliang.common.AppConfig | |||
import com.suliang.common.base.fragment.BaseFragmentVM | |||
import com.suliang.common.extension.click | |||
import com.xkl.cdl.data.AppConstants | |||
import com.xkl.cdl.databinding.FragmentCourseReviewBinding | |||
/** | |||
* 课程章节目录 | |||
* @property param1 String? | |||
* @property param2 String? | |||
* 复习界面 | |||
* 加载此界面,需要传参 从备忘本进入的主页加载此界面,还是从学习中心进入的主页加载此界面,其显示将不同 | |||
*/ | |||
class CourseReviewFragment : BaseFragmentVM<FragmentCourseReviewBinding, CourseMainFragmentViewModel>() { | |||
companion object { | |||
/** | |||
* 新建实例 | |||
* @param pageSource Int 页面来源,默认为0,从学习中心加载, 1为从备忘本加载 | |||
* @return CourseReviewFragment | |||
*/ | |||
@JvmStatic | |||
fun newInstanceFromLearningCenter() : CourseReviewFragment { | |||
return CourseReviewFragment() | |||
} | |||
/** | |||
* 新建实例 | |||
* @param pageSource Int 页面来源,默认为0,从学习中心加载, 1为从备忘本加载 | |||
* @return CourseReviewFragment | |||
*/ | |||
@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() { | |||
//获取页面来源 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_VOICE -> "你将进行 [辨音] 课程智能复习" | |||
AppConstants.COURSE_TYPE_ENGLISH_SPELL -> "你将进行 [拼写] 课程智能复习" | |||
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 initViewModel(): CourseMainFragmentViewModel { | |||
override fun initViewModel() : CourseMainFragmentViewModel { | |||
return ViewModelProvider(requireParentFragment())[CourseMainFragmentViewModel::class.java] | |||
} | |||
} |
@@ -1,71 +1,51 @@ | |||
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.recyclerview.widget.StaggeredGridLayoutManager | |||
import com.suliang.common.base.fragment.BaseFragmentVM | |||
import com.xkl.cdl.R | |||
import com.xkl.cdl.databinding.FragmentMemoBinding | |||
import com.xkl.cdl.databinding.FragmentMyBinding | |||
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 { | |||
/** | |||
* 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 | |||
fun newInstance() = MemoFragment() | |||
} | |||
override fun initViewModel(): MemoFragmentViewModel { | |||
return ViewModelProvider(this)[MemoFragmentViewModel::class.java].apply { | |||
mainActivityViewModel = ViewModelProvider(requireActivity())[MainActivityViewModel::class.java] | |||
} | |||
} | |||
override fun initFragment() { | |||
binding.rv.run { | |||
layoutManager = StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL) | |||
adapter = vm.adapter | |||
} | |||
} | |||
override fun loadData() { | |||
/** 搜索内容变化监听 */ | |||
vm.etSearchLiveData.observe(this){ | |||
} | |||
} | |||
override fun initViewModel(): MainActivityViewModel { | |||
return ViewModelProvider(requireActivity())[MainActivityViewModel::class.java] | |||
override fun onResume() { | |||
super.onResume() | |||
//查询数据 | |||
vm.queryData() | |||
} | |||
} |
@@ -0,0 +1,56 @@ | |||
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() | |||
}) | |||
} | |||
} |
@@ -36,8 +36,6 @@ class MainActivity : BaseActivityVM<ActivityMainBinding, MainActivityViewModel>( | |||
override fun initActivity(savedInstanceState: Bundle?) { | |||
//加载Fragment并显示 | |||
loadFragment(R.id.layout_container, -1, mMemo, mStatistics, mLearnCenter, mServiceCenter, mMy) | |||
//点击事件 | |||
binding.layoutNavContainer.setOnCheckedChangeListener { group, checkedId -> | |||
when (checkedId) { | |||
@@ -51,10 +49,9 @@ class MainActivity : BaseActivityVM<ActivityMainBinding, MainActivityViewModel>( | |||
} | |||
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) | |||
} | |||
} |
@@ -1,14 +1,7 @@ | |||
package com.xkl.cdl.module.main | |||
import androidx.fragment.app.Fragment | |||
import androidx.lifecycle.MutableLiveData | |||
import androidx.lifecycle.viewModelScope | |||
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 | |||
@@ -17,14 +10,12 @@ import kotlinx.coroutines.launch | |||
* 进行用户信息的判断,加载用户课程 | |||
*/ | |||
class MainActivityViewModel: BaseViewModel() { | |||
/** 进入程序加载完毕监听 */ | |||
val mainInitLiveData = MutableLiveData<Boolean>() | |||
/*** | |||
* 进行初始化加载 | |||
*/ | |||
fun loadInit(): MutableLiveData<Boolean> { | |||
// fun loadInit(): MutableLiveData<Boolean> { | |||
//1、查询用户的课程数据 | |||
//设置进入课程管理类中 | |||
// viewModelScope.launch { | |||
@@ -35,8 +26,8 @@ class MainActivityViewModel: BaseViewModel() { | |||
// showHideLoading(false) | |||
// //通知Activity 加载完成 | |||
// } | |||
return MutableLiveData<Boolean>().apply { postValue(true) } | |||
} | |||
// return MutableLiveData<Boolean>().apply { postValue(true) } | |||
// } | |||
//2、计算用户的复习数据 | |||
//3、统计数据 |
@@ -2,57 +2,55 @@ package com.xkl.cdl.module.splash | |||
import android.annotation.SuppressLint | |||
import android.os.Bundle | |||
import appApi.AppApi | |||
import com.googlecode.protobuf.format.JsonFormat | |||
import com.suliang.common.base.activity.BaseActivity | |||
import com.suliang.common.util.LogUtil | |||
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.db.DbCoursePackManager | |||
import com.xkl.cdl.databinding.ActivitySplashBinding | |||
import com.xkl.cdl.module.XKLApplication | |||
import com.xkl.cdl.module.main.MainActivity | |||
import io.reactivex.rxjava3.core.Observable | |||
import java.util.* | |||
import java.util.concurrent.TimeUnit | |||
@SuppressLint("CustomSplashScreen") | |||
class SplashActivity : BaseActivity<ActivitySplashBinding>(){ | |||
override fun onCreate(savedInstanceState: Bundle?) { | |||
class SplashActivity : BaseActivity<ActivitySplashBinding>() { | |||
override fun onCreate(savedInstanceState : Bundle?) { | |||
if (!isTaskRoot) { | |||
finish() | |||
return | |||
} | |||
super.onCreate(savedInstanceState) | |||
} | |||
override fun initActivity(savedInstanceState: Bundle?) { | |||
override fun initActivity(savedInstanceState : Bundle?) { | |||
} | |||
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) | |||
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绑定的课程数据, | |||
DbCoursePackManager().queryBindingCoursePack("262,261,264,136,547,615,516,411") | |||
//复制课程的数据库到对应位置 | |||
@@ -67,22 +65,4 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>(){ | |||
}, 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() | |||
// } | |||
} |
@@ -44,22 +44,43 @@ | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginTop="4dp" | |||
android:textColor="@color/main_text_color" | |||
android:textColor="@color/theme_color" | |||
android:textSize="@dimen/smallSize" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/tv_tips" | |||
tools:text="本次智能复习数%d" /> | |||
<Button | |||
<com.google.android.material.button.MaterialButton | |||
android:id="@+id/tv_start_review" | |||
style="@style/common_button_style" | |||
android:text="@string/start_review" | |||
app:cornerRadius="8dp" | |||
app:layout_constraintBottom_toBottomOf="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_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> |
@@ -1,14 +1,72 @@ | |||
<?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" | |||
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_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> |
@@ -18,7 +18,7 @@ | |||
android:id="@+id/tab_discern" | |||
android:layout_width="wrap_content" | |||
android:layout_height="match_parent" | |||
android:drawableStart="@drawable/ic_discern" | |||
android:drawableStart="@drawable/ic_course_discern" | |||
android:text="认读" | |||
bind:svgColor="@{@color/theme_color}" | |||
android:textColor="@color/theme_color" | |||
@@ -48,7 +48,7 @@ | |||
android:id="@+id/tab_spell" | |||
android:layout_width="wrap_content" | |||
android:layout_height="match_parent" | |||
android:drawableStart="@drawable/ic_spell" | |||
android:drawableStart="@drawable/ic_course_spell" | |||
bind:svgColor="@{@color/theme_color}" | |||
android:textColor="@color/theme_color" | |||
android:text="拼写" | |||
@@ -77,7 +77,7 @@ | |||
android:id="@+id/tab_voice" | |||
android:layout_width="wrap_content" | |||
android:layout_height="match_parent" | |||
android:drawableStart="@drawable/ic_voice" | |||
android:drawableStart="@drawable/ic_course_voice" | |||
android:text="辨音" | |||
bind:svgColor="@{@color/theme_color}" | |||
android:textColor="@color/theme_color" |
@@ -0,0 +1,74 @@ | |||
<?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> |
@@ -62,7 +62,7 @@ | |||
android:layout_marginTop="8dp" | |||
android:layout_marginEnd="8dp" | |||
android:padding="3dp" | |||
android:src="@drawable/ic_discern" | |||
android:src="@drawable/ic_course_discern" | |||
android:visibility="gone" | |||
app:layout_constraintEnd_toStartOf="@+id/image_view_type_spell" | |||
app:layout_constraintHorizontal_bias="0" | |||
@@ -81,7 +81,7 @@ | |||
android:layout_marginTop="8dp" | |||
android:layout_marginEnd="8dp" | |||
android:padding="3dp" | |||
android:src="@drawable/ic_spell" | |||
android:src="@drawable/ic_course_spell" | |||
android:visibility="gone" | |||
app:layout_constraintEnd_toStartOf="@+id/image_view_type_voice" | |||
app:layout_constraintStart_toEndOf="@+id/image_view_type_discern" | |||
@@ -98,7 +98,7 @@ | |||
android:layout_marginTop="8dp" | |||
android:layout_marginEnd="8dp" | |||
android:padding="3dp" | |||
android:src="@drawable/ic_voice" | |||
android:src="@drawable/ic_course_voice" | |||
android:visibility="gone" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toEndOf="@+id/image_view_type_spell" |
@@ -85,5 +85,6 @@ | |||
<string name="quit_auto_play_title">你确定要退出自动播放吗?</string> | |||
<string name="quit_auto_play_title_over">本课程自动播放完毕</string> | |||
<string name="course_introduction">课程简介</string> | |||
<string name="start_see_memo">查看备忘本</string> | |||
</resources> |
@@ -0,0 +1,12 @@ | |||
<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> |
@@ -0,0 +1,12 @@ | |||
<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> |
@@ -0,0 +1,14 @@ | |||
<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> |
@@ -0,0 +1,18 @@ | |||
<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> |
@@ -1,10 +1,9 @@ | |||
<vector xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:width="18dp" | |||
android:height="18dp" | |||
android:tint="#40A540" | |||
android:viewportWidth="24" | |||
android:viewportHeight="24"> | |||
<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" /> | |||
</vector> |
@@ -10,6 +10,11 @@ import io.reactivex.rxjava3.schedulers.Schedulers | |||
* create 2022/4/1 18:02 | |||
* Describe: https://yuzhiqiang.blog.csdn.net/article/details/88233525 | |||
*/ | |||
/** | |||
* 单线程 : 只有diskIo单线程 | |||
* @return ObservableTransformer<T, T> | |||
*/ | |||
fun <T> diskIo2DiskIo(): ObservableTransformer<T, T> { | |||
return ObservableTransformer { upstream -> | |||
upstream!!.subscribeOn(Schedulers.from(AppExecutors.diskIO)) | |||
@@ -17,6 +22,27 @@ fun <T> diskIo2DiskIo(): ObservableTransformer<T, T> { | |||
} | |||
} | |||
/** | |||
* 多线程 | |||
* @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> { | |||
return ObservableTransformer { upstream -> | |||
upstream!!.subscribeOn(Schedulers.from(AppExecutors.diskIO)) |
@@ -43,6 +43,19 @@ class DateUtil { | |||
fun format(formatValue : Long, format : String) : String { | |||
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) | |||
} | |||
/** | |||
* 获取时间段 |