@@ -90,6 +90,7 @@ | |||
<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_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/main_item_course_progress.xml" value="0.25" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/main_item_coursepack.xml" value="0.43500866551126516" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/merge_recyclerview_smart_refresh_layout.xml" value="0.34427083333333336" /> |
@@ -107,5 +107,7 @@ dependencies { | |||
implementation customDependencies.grpc_android | |||
implementation customDependencies.grpc_protobuf | |||
implementation customDependencies.grpc_stub | |||
//XPopup | |||
implementation customDependencies.XPopup | |||
} |
@@ -0,0 +1,32 @@ | |||
package com.xkl.cdl.adapter | |||
import android.view.ViewGroup | |||
import android.widget.ImageView | |||
import com.suliang.common.base.adapter.BaseAdapterViewHolder | |||
import com.suliang.common.base.adapter.BaseRVAdapterVM | |||
import com.suliang.common.extension.click | |||
import com.suliang.common.util.image.ImageLoader | |||
import com.xkl.cdl.R | |||
import com.xkl.cdl.module.learn.LearnCTaskViewModel | |||
import java.io.File | |||
/** | |||
* author suliang | |||
* create 2022/5/23 14:44 | |||
* Describe: | |||
*/ | |||
class AdapterImageTask(viewModel : LearnCTaskViewModel) : BaseRVAdapterVM<File, LearnCTaskViewModel>(viewModel) { | |||
override fun coverViewHolder(parent : ViewGroup, viewType : Int) : BaseAdapterViewHolder { | |||
return BaseAdapterViewHolder(inflateBinding(parent, R.layout.item_task_image)) | |||
} | |||
override fun onBindVH(holder : BaseAdapterViewHolder, position : Int) { | |||
ImageLoader.loadImage(holder.binding.root as ImageView,getItem(position)) | |||
holder.binding.root.click { | |||
if (onItemClickIsInitialized()){ | |||
onItemClick.invoke(it,position,getItem(position)) | |||
} | |||
} | |||
} | |||
} |
@@ -1,65 +0,0 @@ | |||
package com.xkl.cdl.data.bean; | |||
import java.io.Serializable; | |||
/** | |||
* author suliang | |||
* create 2021/4/6 18:25 | |||
* Describe: 作文课外练习实体 | |||
*/ | |||
public class CompositionTaskBean { | |||
private long taskId; //数据id | |||
private int stage; //阶段 | |||
private int type; //类型 | |||
private String title; //标题 | |||
private String introduce; //介绍 | |||
private String photo; //图片 | |||
public long getTaskId() { | |||
return taskId; | |||
} | |||
public void setTaskId(long taskId) { | |||
this.taskId = taskId; | |||
} | |||
public int getStage() { | |||
return stage; | |||
} | |||
public void setStage(int stage) { | |||
this.stage = stage; | |||
} | |||
public int getType() { | |||
return type; | |||
} | |||
public void setType(int type) { | |||
this.type = type; | |||
} | |||
public String getTitle() { | |||
return title; | |||
} | |||
public void setTitle(String title) { | |||
this.title = title; | |||
} | |||
public String getIntroduce() { | |||
return introduce; | |||
} | |||
public void setIntroduce(String introduce) { | |||
this.introduce = introduce; | |||
} | |||
public String getPhoto() { | |||
return photo; | |||
} | |||
public void setPhoto(String photo) { | |||
this.photo = photo; | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
package com.xkl.cdl.data.bean | |||
/** | |||
* author suliang | |||
* create 2021/4/6 18:25 | |||
* Describe: 作文课外练习实体 | |||
*/ | |||
data class CompositionTaskBean(val taskId : Long, //数据id | |||
val stage : Long, //阶段 | |||
val type : Long, //类型 | |||
val title : String, //标题 | |||
var introduce : String?, //介绍 | |||
var photo : String?, | |||
val position : Int) |
@@ -1,15 +1,18 @@ | |||
package com.xkl.cdl.data.manager | |||
import appApi.AppApi | |||
import com.suliang.common.util.AppGlobals | |||
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.File | |||
import java.io.* | |||
import java.math.BigDecimal | |||
import java.math.RoundingMode | |||
import java.text.DecimalFormat | |||
import java.util.zip.ZipFile | |||
import java.util.zip.ZipInputStream | |||
/** | |||
* author suliang | |||
@@ -66,16 +69,52 @@ object CourseManager { | |||
/*** | |||
* 检查课程包下课程的db数据文件是否存在,不存在,则复制过去 | |||
* 作文是.zip结尾的文件,需要解压,将文件放入对应的目录 | |||
*/ | |||
fun checkCourseDb() { | |||
subjectWithCoursePackMap.forEach { entry -> | |||
entry.value.forEach { coursePack -> | |||
coursePack.childrenCourses.forEach { | |||
val file = File(FileUtil.getSaveDirPath("db"), | |||
"${entry.key}/${coursePack.coursePackId}/${it.courseId}/course.db") | |||
//不存在,复制,存在则不操作 | |||
if (!file.exists()) { | |||
FileUtil.copyAsset(it.dbPathName, file) | |||
//如果是作文,作文需要检查数据库,同时需要检查其下的图片、视频和关键帧 | |||
if (it.subjectId == AppConstants.SUBJECT_CHINESE && it.coursePackType == AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION){ | |||
val assetFileName = it.dbPathName.substringBeforeLast(".") + ".zip" | |||
val inputStream = AppGlobals.application.resources.assets.open(assetFileName) | |||
val zipInputStream = ZipInputStream(inputStream) | |||
var count = 0 | |||
val buffer = ByteArray(5120) | |||
var nextEntry = zipInputStream.nextEntry | |||
while (nextEntry != null){ | |||
when{ | |||
nextEntry.name.endsWith(".mp4.png") -> File(FilePathManager.getMp4PngRootPath(),"${entry.key}/${coursePack.coursePackId}/${it.courseId}/${nextEntry.name.substringAfterLast("/")}") | |||
nextEntry.name.endsWith(".mp4") -> File(FilePathManager.getMp4RootPath(),"${entry.key}/${coursePack.coursePackId}/${it.courseId}/${nextEntry.name.substringAfterLast("/")}") | |||
nextEntry.name.endsWith(".png") -> File(FilePathManager.getPngRootPath(),"${entry.key}/${coursePack.coursePackId}/${it.courseId}/${nextEntry.name.substringAfterLast("/")}") | |||
nextEntry.name.endsWith(".db") -> File(FilePathManager.getDbRootPath(),"${entry.key}/${coursePack.coursePackId}/${it.courseId}/course.db") | |||
else -> null | |||
}?.let { file -> | |||
//文件不存在 ,复制当前文件进入该文件 | |||
if (!file.exists()){ | |||
file.parentFile?.let { parentFile -> | |||
if (!parentFile.exists()) parentFile.mkdirs() | |||
} | |||
val outputStream = BufferedOutputStream(FileOutputStream(file)) | |||
while ((zipInputStream.read(buffer).also { count = it }) != -1 ){ | |||
outputStream.write(buffer,0,count) | |||
} | |||
outputStream.close() | |||
} | |||
} | |||
nextEntry = zipInputStream.nextEntry | |||
} | |||
}else{ //其他的项目只需要复制和检查数据库 | |||
val file = File(FilePathManager.getDbRootPath(), "${entry.key}/${coursePack.coursePackId}/${it.courseId}/course.db") | |||
//不存在,复制,存在则不操作 | |||
if (!file.exists()) { | |||
FileUtil.copyAsset(it.dbPathName, file) | |||
} | |||
} | |||
} | |||
} |
@@ -18,17 +18,39 @@ class FilePathManager { | |||
*/ | |||
@JvmStatic | |||
fun getCoursePackDbPath(): File{ | |||
return File(FileUtil.getSaveDirPath("db"), "course-pack.db") | |||
return File(getDbRootPath(), "course-pack.db") | |||
} | |||
/** 获取课程数据地址 */ | |||
@JvmStatic | |||
fun getCourseDbPath(base:DbControlBase): File{ | |||
return File(FileUtil.getSaveDirPath("db"), "${base.subjectId}/${base.coursePackId}/${base.courseId}/course.db") | |||
return File(getDbRootPath(), "${base.subjectId}/${base.coursePackId}/${base.courseId}/course.db") | |||
} | |||
/** 获取课程数据地址 */ | |||
@JvmStatic | |||
fun getCourseDbPath(subjectId:Int,coursePackId:Long,courseId:Long): File{ | |||
return File(FileUtil.getSaveDirPath("db"), "subjectId/coursePackId/courseId/course.db") | |||
return File(getDbRootPath(), "subjectId/coursePackId/courseId/course.db") | |||
} | |||
/** db文件保存的文件夹 */ | |||
fun getDbRootPath(): String{ | |||
return FileUtil.getSaveDirPath("db") | |||
} | |||
/**作文mp4.png保存的文件夹*/ | |||
fun getMp4PngRootPath():String{ | |||
return FileUtil.getSaveDirPath("mp4png") | |||
} | |||
/**作文课外练习png保存的文件夹*/ | |||
fun getPngRootPath():String{ | |||
return FileUtil.getSaveDirPath("png") | |||
} | |||
/**作文课外练习png保存的文件*/ | |||
fun getPngFile(subjectId : Int,coursePackId : Long,courseId : Long, pngName:String):File{ | |||
return File(getPngRootPath(),"$subjectId/$coursePackId/$courseId/$pngName") | |||
} | |||
/**作文视频MP4保存的文件夹*/ | |||
fun getMp4RootPath():String{ | |||
return FileUtil.getSaveDirPath("mp4") | |||
} | |||
} |
@@ -44,12 +44,10 @@ object DBCourseManager { | |||
val p = when (base.courseType) { | |||
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> COMPOSITION | |||
AppConstants.COURSE_TYPE_ENGLISH_SPOKEN -> SPOKEN | |||
AppConstants.COURSE_TYPE_CHINESE_LITERACY,AppConstants.COURSE_TYPE_CHINESE_PINYIN -> LITERACY_PINYING | |||
AppConstants.COURSE_TYPE_CHINESE_LITERACY, AppConstants.COURSE_TYPE_CHINESE_PINYIN -> LITERACY_PINYING | |||
else -> NORMAL | |||
} | |||
mDataBase = SQLiteDatabase.openDatabase(FilePathManager.getCourseDbPath(currentBase).path, | |||
p, | |||
null, | |||
mDataBase = SQLiteDatabase.openDatabase(FilePathManager.getCourseDbPath(currentBase).path, p, null, | |||
SQLiteDatabase.OPEN_READONLY) | |||
} | |||
if (mDataBase == null) { | |||
@@ -89,7 +87,7 @@ object DBCourseManager { | |||
val lessonId = it.getLong(it.getColumnIndex("lesson_id")) | |||
val lessonName : String = when (base.courseType) { | |||
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION, AppConstants.COURSE_TYPE_CHINESE_LITERACY, AppConstants.COURSE_TYPE_CHINESE_PINYIN -> it.getString( | |||
it.getColumnIndex("lesson_title")) | |||
it.getColumnIndex("lesson_title")) | |||
else -> it.getString(it.getColumnIndex("lesson")) | |||
} | |||
val wordIds : MutableList<Long> = when (base.courseType) { | |||
@@ -109,15 +107,8 @@ object DBCourseManager { | |||
// 注:针对口语对话课时,如果没有学习完,是不会设置进度点的。 todo 分课程类型设置进度点 | |||
val learnIsOver = wordIds.size - 1 == learnIndex | |||
val lesson = Lesson(base.subjectId, | |||
base.coursePackId, | |||
base.coursePackType, | |||
base.courseId, | |||
base.courseType, | |||
chapterId, | |||
chapterName, | |||
lessonId, | |||
lessonName).apply { | |||
val lesson = Lesson(base.subjectId, base.coursePackId, base.coursePackType, base.courseId, base.courseType, | |||
chapterId, chapterName, lessonId, lessonName).apply { | |||
lessonPositionInList = positionIndex | |||
this.wordIds = wordIds //内容 | |||
totalNumber = this.wordIds.size //总数 | |||
@@ -132,12 +123,12 @@ object DBCourseManager { | |||
mutableList.add(lesson) | |||
positionIndex += 1 | |||
// TODO: 2022/5/9 对课时数量进行限制,课时太多,开发人员不好进行测试 | |||
val needBreak = when(base.coursePackType){ | |||
val needBreak = when (base.coursePackType) { | |||
AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION -> positionIndex == 11 //保留十个课时 | |||
AppConstants.COURSEPACK_TYPE_ENGLISH_SPOKEN -> positionIndex == 7 //保留六个课时 | |||
else -> positionIndex == 3 //保留三个课时 | |||
} | |||
if (needBreak){ | |||
if (needBreak) { | |||
break | |||
} | |||
} | |||
@@ -164,7 +155,9 @@ object DBCourseManager { | |||
} | |||
val sql = when (base.courseType) { | |||
//作文知识点测试 | |||
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> "SELECT * FROM exam WHERE chapter_id = ${lesson!!.chapterId} AND exam_id in (${Joiner.on(",").join(lesson.wordIds)}) ORDER BY random()" | |||
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> "SELECT * FROM exam WHERE chapter_id = ${lesson!!.chapterId} AND exam_id in (${ | |||
Joiner.on(",").join(lesson.wordIds) | |||
}) ORDER BY random()" | |||
AppConstants.COURSE_TYPE_ENGLISH_DISCERN, AppConstants.COURSE_TYPE_ENGLISH_VOICE, | |||
AppConstants.COURSE_TYPE_ENGLISH_SOUNDMARK, AppConstants.COURSE_TYPE_CHINESE_LITERACY, | |||
@@ -203,7 +196,7 @@ object DBCourseManager { | |||
mDataBase?.rawQuery(sql, null)?.run { | |||
when (base.courseType) { | |||
//作文知识点测试 | |||
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> while (moveToNext()) { | |||
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> while (moveToNext()) { | |||
result.add(ExamBean().apply { | |||
id = getLong(3) | |||
word_id = getLong(2) | |||
@@ -218,23 +211,21 @@ object DBCourseManager { | |||
}) | |||
} | |||
AppConstants.COURSE_TYPE_ENGLISH_DISCERN, AppConstants.COURSE_TYPE_ENGLISH_VOICE, | |||
AppConstants.COURSE_TYPE_CHINESE_LITERACY, | |||
AppConstants.COURSE_TYPE_CHINESE_PINYIN -> while (moveToNext()) { | |||
result.add(ExamBean().apply { | |||
id = getLong(0) | |||
word_id = getLong(1) | |||
word = getString(2) | |||
correct = getString(3) | |||
error1 = getString(4) | |||
error2 = getString(5) | |||
error3 = getString(6) | |||
type = AppConstants.TEST_QUEST_TYPE_CHOICE // 为1 | |||
chapterId = getLong(8) | |||
lessonId = getLong(9) | |||
}) | |||
} | |||
AppConstants.COURSE_TYPE_ENGLISH_SOUNDMARK -> while (moveToNext()) { | |||
AppConstants.COURSE_TYPE_ENGLISH_DISCERN, AppConstants.COURSE_TYPE_ENGLISH_VOICE, AppConstants.COURSE_TYPE_CHINESE_LITERACY, AppConstants.COURSE_TYPE_CHINESE_PINYIN -> while (moveToNext()) { | |||
result.add(ExamBean().apply { | |||
id = getLong(0) | |||
word_id = getLong(1) | |||
word = getString(2) | |||
correct = getString(3) | |||
error1 = getString(4) | |||
error2 = getString(5) | |||
error3 = getString(6) | |||
type = AppConstants.TEST_QUEST_TYPE_CHOICE // 为1 | |||
chapterId = getLong(8) | |||
lessonId = getLong(9) | |||
}) | |||
} | |||
AppConstants.COURSE_TYPE_ENGLISH_SOUNDMARK -> while (moveToNext()) { | |||
result.add(ExamBean().apply { | |||
id = getLong(0) | |||
word_id = getLong(1) | |||
@@ -355,28 +346,24 @@ object DBCourseManager { | |||
//不为空,写入本身,如果为空,用另外的发音方式写入 | |||
val parentPath = FileUtil.getSaveDirPath(AppConfig.VOICE) | |||
val audio_us_file_path = audio_us?.let { | |||
val audioFileNameUS = | |||
"${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_US}" | |||
val audioFileNameUS = "${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_US}" | |||
val file = File(parentPath, audioFileNameUS) | |||
FileUtil.writeBytesToFile(file, it) | |||
file.path | |||
} ?: audio_uk?.let { | |||
val audioFileNameUS = | |||
"${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_US}" | |||
val audioFileNameUS = "${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_US}" | |||
val file = File(parentPath, audioFileNameUS) | |||
FileUtil.writeBytesToFile(file, it) | |||
file.path | |||
} | |||
//不为空,写入本身,如果为空,用另外的发音方式写入 | |||
val audio_uk_file_path = audio_uk?.let { | |||
val audioFileNameUk = | |||
"${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_UK}" | |||
val audioFileNameUk = "${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_UK}" | |||
val file = File(parentPath, audioFileNameUk) | |||
FileUtil.writeBytesToFile(file, it) | |||
file.path | |||
} ?: audio_us?.let { | |||
val audioFileNameUk = | |||
"${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_UK}" | |||
val audioFileNameUk = "${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_UK}" | |||
val file = File(parentPath, audioFileNameUk) | |||
FileUtil.writeBytesToFile(file, it) | |||
file.path | |||
@@ -392,8 +379,7 @@ object DBCourseManager { | |||
while (it.moveToNext()) { | |||
//写入文件 | |||
audio_cn_file_path = it.getBlobOrNull(0)?.let { | |||
val audioFileNameCN = | |||
"${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_CN}" | |||
val audioFileNameCN = "${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_CN}" | |||
val file = File(FileUtil.getSaveDirPath(AppConfig.VOICE), audioFileNameCN) | |||
FileUtil.writeBytesToFile(file, it) | |||
file.path | |||
@@ -434,25 +420,16 @@ object DBCourseManager { | |||
open(dbcb) | |||
//从lesson已学位置开始获取数据 | |||
val needLearnIds = lesson.wordIds.subList(lesson.learnedIndex + 1, lesson.wordIds.size) | |||
val sql = | |||
"SELECT * FROM chapter WHERE chapter_id = ${lesson.chapterId} and lesson_id = ${lesson.lessonId} AND word_id in (${ | |||
Joiner.on(",").join(needLearnIds) | |||
}) " + "ORDER by word_sort ASC" | |||
val sql = "SELECT * FROM chapter WHERE chapter_id = ${lesson.chapterId} and lesson_id = ${lesson.lessonId} AND word_id in (${ | |||
Joiner.on(",").join(needLearnIds) | |||
}) " + "ORDER by word_sort ASC" | |||
mDataBase?.rawQuery(sql, null)?.run { | |||
while (moveToNext()) { | |||
//单词id | |||
val word_id = getLong(getColumnIndex("word_id")) | |||
//学习单词实体 | |||
result.add(LearnWord(dbcb.subjectId, | |||
dbcb.coursePackId, | |||
dbcb.courseId, | |||
dbcb.coursePackType, | |||
dbcb.courseType, | |||
lesson.chapterId, | |||
lesson.lessonId, | |||
word_id, | |||
true, | |||
lesson.lessonType).apply { | |||
result.add(LearnWord(dbcb.subjectId, dbcb.coursePackId, dbcb.courseId, dbcb.coursePackType, dbcb.courseType, | |||
lesson.chapterId, lesson.lessonId, word_id, true, lesson.lessonType).apply { | |||
word = getString(getColumnIndex("word")) | |||
basic_explanation = getString(getColumnIndex("basic_explaination")) | |||
extend_explanation = getString(getColumnIndex("all_explaination")) | |||
@@ -506,21 +483,13 @@ object DBCourseManager { | |||
else -> wordIds.append(value).append(",") | |||
} | |||
} | |||
val sql = | |||
"SELECT * FROM knowledge WHERE chapter_id = ${lesson.chapterId} AND knowledge_id in ($wordIds) ORDER by CASE $sortSqlBuilder END" | |||
val sql = "SELECT * FROM knowledge WHERE chapter_id = ${lesson.chapterId} AND knowledge_id in ($wordIds) ORDER by CASE $sortSqlBuilder END" | |||
mDataBase?.rawQuery(sql, null)?.run { | |||
while (moveToNext()) { | |||
//学习单词实体 | |||
result.add(LearnWord(dbcb.subjectId, | |||
dbcb.coursePackId, | |||
dbcb.courseId, | |||
dbcb.coursePackType, | |||
dbcb.courseType, | |||
lesson.chapterId, | |||
lesson.lessonId, | |||
getLong(getColumnIndex("knowledge_id")), | |||
true, | |||
result.add(LearnWord(dbcb.subjectId, dbcb.coursePackId, dbcb.courseId, dbcb.coursePackType, dbcb.courseType, | |||
lesson.chapterId, lesson.lessonId, getLong(getColumnIndex("knowledge_id")), true, | |||
lesson.lessonType).apply { | |||
word = getString(getColumnIndex("title")) | |||
basic_explanation = getString(getColumnIndex("explaination")) | |||
@@ -558,10 +527,14 @@ object DBCourseManager { | |||
* @return List<LearnWord> | |||
*/ | |||
@SuppressLint("Range") | |||
fun queryLearnWord(dbcb : DbControlBase, chapterId:Long, lessonId:Long, wordIds:String, isNeedOriginSort:Boolean = false):List<LearnWord>{ | |||
fun queryLearnWord(dbcb : DbControlBase, | |||
chapterId : Long, | |||
lessonId : Long, | |||
wordIds : String, | |||
isNeedOriginSort : Boolean = false) : List<LearnWord> { | |||
val result = mutableListOf<LearnWord>() | |||
open(dbcb) | |||
val sql = when{ | |||
val sql = when { | |||
!isNeedOriginSort -> "SELECT * FROM chapter WHERE chapter_id = $chapterId and lesson_id = $lessonId AND word_id in ($wordIds) ORDER by word_sort ASC" | |||
else -> { | |||
val sortSqlBuilder = StringBuilder() //排序value | |||
@@ -574,15 +547,8 @@ object DBCourseManager { | |||
mDataBase?.rawQuery(sql, null)?.run { | |||
while (moveToNext()) { | |||
//学习单词实体 | |||
result.add(LearnWord(dbcb.subjectId, | |||
dbcb.coursePackId, | |||
dbcb.courseId, | |||
dbcb.coursePackType, | |||
dbcb.courseType, | |||
chapterId, | |||
lessonId, | |||
getLong(getColumnIndex("word_id")), | |||
true, | |||
result.add(LearnWord(dbcb.subjectId, dbcb.coursePackId, dbcb.courseId, dbcb.coursePackType, dbcb.courseType, | |||
chapterId, lessonId, getLong(getColumnIndex("word_id")), true, | |||
AppConstants.LESSON_TYPE_WORD).apply { | |||
word = getString(getColumnIndex("word")) | |||
basic_explanation = getString(getColumnIndex("basic_explaination")) | |||
@@ -590,7 +556,7 @@ object DBCourseManager { | |||
phrase = getString(getColumnIndex("phrase")) | |||
example = getString(getColumnIndex("example")) | |||
reference = getString(getColumnIndex("reference")) | |||
when (dbcb.courseType) { | |||
AppConstants.COURSE_TYPE_CHINESE_LITERACY -> { | |||
literacyIspolyphone = getInt(getColumnIndex("polyphone")) > 0 | |||
@@ -619,23 +585,21 @@ object DBCourseManager { | |||
fun queryCompositionReading(dbcb : DbControlBase, entity : Lesson) : List<CompositionReadingBean> { | |||
val result = mutableListOf<CompositionReadingBean>() | |||
val sql = "SELECT * FROM reading WHERE reading_id in (${Joiner.on(",").join(entity.wordIds)}) AND chapter_id = 5272 ORDER by CASE " + entity.wordIds.let { | |||
val sql = "SELECT * FROM reading WHERE reading_id in (${ | |||
Joiner.on(",") | |||
.join(entity.wordIds) | |||
}) AND chapter_id = ${entity.chapterId} ORDER by CASE " + entity.wordIds.let { | |||
val sortedCase = StringBuilder() | |||
it.forEachIndexed{ index,id -> | |||
it.forEachIndexed { index, id -> | |||
sortedCase.append(" WHEN reading_id = $id THEN $index") | |||
} | |||
sortedCase | |||
} + " END" | |||
open(dbcb) | |||
mDataBase?.rawQuery(sql,null)?.run { | |||
while (moveToNext()){ | |||
result.add(CompositionReadingBean( | |||
getLong(2), | |||
getString(3), | |||
getString(4), | |||
getString(7), | |||
getString(6)).apply { | |||
collectId = 0 | |||
mDataBase?.rawQuery(sql, null)?.run { | |||
while (moveToNext()) { | |||
result.add(CompositionReadingBean(getLong(2), getString(3), getString(4), getString(7), getString(6)).apply { | |||
collectId = 0 | |||
}) | |||
} | |||
close() | |||
@@ -650,10 +614,41 @@ object DBCourseManager { | |||
* @param entity Lesson | |||
* @return List<CompositionTaskBean> | |||
*/ | |||
fun queryCompositonTask(dbcb : DbControlBase,entity : Lesson) : List<CompositionTaskBean>{ | |||
fun queryCompositionTask(dbcb : DbControlBase, entity : Lesson) : List<CompositionTaskBean> { | |||
val result = mutableListOf<CompositionTaskBean>() | |||
val sql = "SELECT * FROM task WHERE task_id in (${ | |||
Joiner.on(",") | |||
.join(entity.wordIds) | |||
}) AND chapter_id = ${entity.chapterId} ORDER by CASE " + entity.wordIds.let { | |||
val sortedCase = StringBuilder() | |||
it.forEachIndexed { index, id -> | |||
sortedCase.append(" WHEN task_id = $id THEN $index") | |||
} | |||
sortedCase | |||
} + " END" | |||
open(dbcb) | |||
mDataBase?.rawQuery(sql, null)?.run { | |||
var position = 0 | |||
while (moveToNext()) { | |||
result.add(CompositionTaskBean(getLong(2), | |||
getLong(4), | |||
getLong(3), | |||
getString(6), | |||
getString(7), | |||
getString(8), | |||
position)) | |||
position++ | |||
result.add(CompositionTaskBean(getLong(2), | |||
getLong(4), | |||
getLong(3), | |||
getString(6), | |||
getString(7), | |||
getString(8), | |||
position)) | |||
position++ | |||
} | |||
close() | |||
} | |||
return result | |||
} | |||
@@ -1,6 +1,5 @@ | |||
package com.xkl.cdl.data.repository | |||
import androidx.lifecycle.MutableLiveData | |||
import com.suliang.common.util.file.FileUtil | |||
import com.xkl.cdl.data.bean.course.CourseDetail | |||
import com.xkl.cdl.data.bean.course.Lesson |
@@ -1,9 +1,28 @@ | |||
package com.xkl.cdl.module.learn | |||
import android.media.Image | |||
import android.os.Bundle | |||
import android.text.TextUtils | |||
import android.widget.ImageView | |||
import androidx.databinding.DataBindingUtil | |||
import androidx.lifecycle.ViewModelProvider | |||
import androidx.recyclerview.widget.LinearLayoutManager | |||
import androidx.recyclerview.widget.StaggeredGridLayoutManager | |||
import com.google.android.material.bottomsheet.BottomSheetDialog | |||
import com.lxj.xpopup.XPopup | |||
import com.lxj.xpopup.core.ImageViewerPopupView | |||
import com.lxj.xpopup.interfaces.OnSrcViewUpdateListener | |||
import com.lxj.xpopup.util.SmartGlideImageLoader | |||
import com.suliang.common.base.activity.BaseActivityVM | |||
import com.suliang.common.extension.click | |||
import com.xkl.cdl.R | |||
import com.xkl.cdl.adapter.AdapterBottomDialogSwitch | |||
import com.xkl.cdl.adapter.AdapterImageTask | |||
import com.xkl.cdl.data.bean.CompositionReadingBean | |||
import com.xkl.cdl.data.bean.CompositionTaskBean | |||
import com.xkl.cdl.data.manager.FilePathManager | |||
import com.xkl.cdl.databinding.ActivityLearnCtaskBinding | |||
import com.xkl.cdl.databinding.DialogBottomAutoPlaySelectBinding | |||
/** | |||
* author suliang | |||
@@ -17,11 +36,85 @@ class LearnCTaskActivity : BaseActivityVM<ActivityLearnCtaskBinding,LearnCTaskVi | |||
} | |||
override fun initActivity(savedInstanceState : Bundle?) { | |||
binding.titleBar.textViewTitle.run { | |||
ellipsize = TextUtils.TruncateAt.MIDDLE | |||
text = vm.lesson.lessonName | |||
} | |||
binding.titleBar.onBackClick = { | |||
finish() | |||
} | |||
binding.titleBar.onRightClick = { | |||
showChangeTaskDialog() | |||
} | |||
//初始化图片显示recyclerView | |||
binding.recyclerView.apply { | |||
layoutManager = StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL) | |||
adapter = AdapterImageTask(vm).apply { | |||
onItemClick = { v, position, item -> | |||
XPopup.Builder(this@LearnCTaskActivity) | |||
.asImageViewer(v as ImageView, position, | |||
(binding.recyclerView.adapter as AdapterImageTask).getData() as List<*>, object : OnSrcViewUpdateListener{ | |||
override fun onSrcViewUpdate(popupView : ImageViewerPopupView, position : Int) { | |||
popupView.updateSrcView(binding.recyclerView.getChildAt(position) as ImageView) | |||
} | |||
}, SmartGlideImageLoader()) | |||
.show() | |||
} | |||
} | |||
} | |||
} | |||
override fun loadData() { | |||
vm.queryData() | |||
//当前练习 | |||
vm.currentTask.observe(this){ | |||
binding.run { | |||
topicValue.text = it.title | |||
requireValue.text = it.introduce | |||
sourceValue.text = "学考乐" | |||
} | |||
it.photo?.run { | |||
val fileList = split(",").map { | |||
FilePathManager.getPngFile(vm.lesson.subjectId,vm.lesson.coursePackId,vm.lesson.courseId,it.substringAfterLast ("/")) | |||
}.toMutableList() | |||
(binding.recyclerView.adapter as AdapterImageTask).setData(fileList) | |||
} | |||
} | |||
} | |||
private var changeTaskDialog: BottomSheetDialog? = null | |||
/** 切换item的dialog */ | |||
private fun showChangeTaskDialog() { | |||
if (!vm.isCanSwitch) return | |||
if (changeTaskDialog == null) { | |||
changeTaskDialog = BottomSheetDialog(this, R.style.dialog_style).apply { | |||
val autoPlayBinding = DataBindingUtil.inflate<DialogBottomAutoPlaySelectBinding>(layoutInflater, | |||
R.layout.dialog_bottom_auto_play_select, | |||
null, false) | |||
setContentView(autoPlayBinding.root) | |||
autoPlayBinding.tvTitle.text = "请选择课外练习查看详情" | |||
autoPlayBinding.ivCancel.click { v -> dismiss() } | |||
autoPlayBinding.rvRepeat.run { | |||
layoutManager = LinearLayoutManager(this@LearnCTaskActivity, LinearLayoutManager.VERTICAL, false) | |||
adapter = AdapterBottomDialogSwitch<CompositionTaskBean>().apply { | |||
onItemClick = { _, position, _ -> | |||
dismiss() | |||
vm.currentTask.value = vm.mTaskList[position] | |||
} | |||
setData(vm.mTaskList.toMutableList()) | |||
} | |||
} | |||
//显示时,定位到当前item的位置 | |||
setOnShowListener{ | |||
(autoPlayBinding.rvRepeat.adapter as AdapterBottomDialogSwitch<*>).selectItemIndex = vm.currentTask.value!!.position | |||
autoPlayBinding.rvRepeat.scrollToPosition( vm.currentTask.value!!.position) | |||
} | |||
} | |||
} | |||
changeTaskDialog?.show() | |||
} | |||
@@ -1,15 +1,46 @@ | |||
package com.xkl.cdl.module.learn | |||
import androidx.lifecycle.MutableLiveData | |||
import com.suliang.common.base.activity.ToastEvent | |||
import com.suliang.common.base.viewmodel.BaseViewModel | |||
import com.suliang.common.extension.diskIo2Main | |||
import com.xkl.cdl.data.DataTransferHolder | |||
import com.xkl.cdl.data.bean.CompositionTaskBean | |||
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 io.reactivex.rxjava3.core.Observable | |||
class LearnCTaskViewModel : BaseViewModel() { | |||
val lesson = DataTransferHolder.instance.getData<Lesson>() | |||
val dbControlBase = DbControlBase(lesson.subjectId, lesson.coursePackId, lesson.coursePackType, lesson.courseId, | |||
lesson.courseType) | |||
fun queryData(){ | |||
//是否有切换的练习 | |||
var isCanSwitch = false | |||
//数据列表 | |||
lateinit var mTaskList : List<CompositionTaskBean> | |||
//当前显示task | |||
val currentTask = MutableLiveData<CompositionTaskBean>() | |||
/** | |||
* 查询数据 | |||
*/ | |||
fun queryData() { | |||
Observable.create<List<CompositionTaskBean>> { | |||
it.onNext(DBCourseManager.queryCompositionTask(dbControlBase, lesson)) | |||
it.onComplete() | |||
}.compose(diskIo2Main()).subscribe({ | |||
isCanSwitch = it.size > 1 | |||
mTaskList = it | |||
currentTask.value = mTaskList[0] | |||
}, { | |||
it.printStackTrace() | |||
showToast(ToastEvent(content = "课外练习数据获取异常,请联系管理人员进行处理")) | |||
}) | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<layout xmlns:android="http://schemas.android.com/apk/res/android" | |||
xmlns:app="http://schemas.android.com/apk/res-auto"> | |||
<data> | |||
</data> | |||
<com.google.android.material.imageview.ShapeableImageView | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:scaleType="fitXY" | |||
app:shapeAppearance="@style/roundedCornerStyle" /> | |||
</layout> |
@@ -100,7 +100,11 @@ ext { | |||
grpc_protobuf: "io.grpc:grpc-protobuf:1.27.0", | |||
grpc_stub: "io.grpc:grpc-stub:1.27.0", | |||
//liveEventBus https://github.com/JeremyLiao/LiveEventBus | |||
liveEventBus : "io.github.jeremyliao:live-event-bus-x:1.8.0" | |||
liveEventBus : "io.github.jeremyliao:live-event-bus-x:1.8.0", | |||
//BigImageViewPager | |||
// BigImageViewPager : "com.github.SherlockGougou:BigImageViewPager:androidx-7.0.0", | |||
//XPopup https://github.com/li-xiaojun/XPopup | |||
XPopup : "com.github.li-xiaojun:XPopup:2.8.0" | |||
] | |||
@@ -16,6 +16,9 @@ import com.bumptech.glide.module.AppGlideModule | |||
import com.bumptech.glide.module.LibraryGlideModule | |||
import com.bumptech.glide.request.RequestOptions | |||
import com.suliang.common.util.file.FileUtil | |||
import com.bumptech.glide.load.model.GlideUrl | |||
import java.io.InputStream | |||
/** | |||
* author suliang | |||
@@ -66,5 +69,12 @@ class MyGlideApp : AppGlideModule() { | |||
// setDefaultRequestOptions(apply) | |||
} | |||
} | |||
override fun registerComponents(context : Context, glide : Glide, registry : Registry) { | |||
super.registerComponents(context, glide, registry) | |||
// 替换底层网络框架为okhttp3,这步很重要!如果不添加会无法正常显示原图的加载百分比,或者卡在1% | |||
// 如果你的app中已经存在了自定义的GlideModule,你只需要把这一行代码,添加到对应的重载方法中即可。 | |||
// registry.replace(GlideUrl::class.java, InputStream::class.java, Factory(ProgressManager.getOkHttpClient())) | |||
} | |||
} |