<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_empty.xml" value="0.4979166666666667" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_empty.xml" value="0.4979166666666667" /> | ||||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_historical_route.xml" value="0.4859375" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_historical_route.xml" value="0.4859375" /> | ||||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_spell_single_word.xml" value="0.23632218844984804" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_spell_single_word.xml" value="0.23632218844984804" /> | ||||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_task_image.xml" value="0.23353596757852077" /> | |||||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/main_item_course_progress.xml" value="0.25" /> | <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/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" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/merge_recyclerview_smart_refresh_layout.xml" value="0.34427083333333336" /> |
implementation customDependencies.grpc_android | implementation customDependencies.grpc_android | ||||
implementation customDependencies.grpc_protobuf | implementation customDependencies.grpc_protobuf | ||||
implementation customDependencies.grpc_stub | implementation customDependencies.grpc_stub | ||||
//XPopup | |||||
implementation customDependencies.XPopup | |||||
} | } |
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)) | |||||
} | |||||
} | |||||
} | |||||
} |
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; | |||||
} | |||||
} |
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) |
package com.xkl.cdl.data.manager | package com.xkl.cdl.data.manager | ||||
import appApi.AppApi | import appApi.AppApi | ||||
import com.suliang.common.util.AppGlobals | |||||
import com.suliang.common.util.file.FileUtil | import com.suliang.common.util.file.FileUtil | ||||
import com.xkl.cdl.data.AppConstants | import com.xkl.cdl.data.AppConstants | ||||
import com.xkl.cdl.data.bean.course.CoursePack | import com.xkl.cdl.data.bean.course.CoursePack | ||||
import com.xkl.cdl.data.bean.course.ExamBean | import com.xkl.cdl.data.bean.course.ExamBean | ||||
import com.xkl.cdl.data.bean.course.Lesson | import com.xkl.cdl.data.bean.course.Lesson | ||||
import java.io.File | import java.io.* | ||||
import java.math.BigDecimal | import java.math.BigDecimal | ||||
import java.math.RoundingMode | import java.math.RoundingMode | ||||
import java.text.DecimalFormat | import java.text.DecimalFormat | ||||
import java.util.zip.ZipFile | |||||
import java.util.zip.ZipInputStream | |||||
/** | /** | ||||
* author suliang | * author suliang | ||||
/*** | /*** | ||||
* 检查课程包下课程的db数据文件是否存在,不存在,则复制过去 | * 检查课程包下课程的db数据文件是否存在,不存在,则复制过去 | ||||
* 作文是.zip结尾的文件,需要解压,将文件放入对应的目录 | |||||
*/ | */ | ||||
fun checkCourseDb() { | fun checkCourseDb() { | ||||
subjectWithCoursePackMap.forEach { entry -> | subjectWithCoursePackMap.forEach { entry -> | ||||
entry.value.forEach { coursePack -> | entry.value.forEach { coursePack -> | ||||
coursePack.childrenCourses.forEach { | coursePack.childrenCourses.forEach { | ||||
val file = File(FileUtil.getSaveDirPath("db"), | //如果是作文,作文需要检查数据库,同时需要检查其下的图片、视频和关键帧 | ||||
"${entry.key}/${coursePack.coursePackId}/${it.courseId}/course.db") | if (it.subjectId == AppConstants.SUBJECT_CHINESE && it.coursePackType == AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION){ | ||||
//不存在,复制,存在则不操作 | val assetFileName = it.dbPathName.substringBeforeLast(".") + ".zip" | ||||
if (!file.exists()) { | val inputStream = AppGlobals.application.resources.assets.open(assetFileName) | ||||
FileUtil.copyAsset(it.dbPathName, file) | 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) | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
*/ | */ | ||||
@JvmStatic | @JvmStatic | ||||
fun getCoursePackDbPath(): File{ | fun getCoursePackDbPath(): File{ | ||||
return File(FileUtil.getSaveDirPath("db"), "course-pack.db") | return File(getDbRootPath(), "course-pack.db") | ||||
} | } | ||||
/** 获取课程数据地址 */ | /** 获取课程数据地址 */ | ||||
@JvmStatic | @JvmStatic | ||||
fun getCourseDbPath(base:DbControlBase): File{ | 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 | @JvmStatic | ||||
fun getCourseDbPath(subjectId:Int,coursePackId:Long,courseId:Long): File{ | 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") | |||||
} | } | ||||
} | } |
val p = when (base.courseType) { | val p = when (base.courseType) { | ||||
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> COMPOSITION | AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> COMPOSITION | ||||
AppConstants.COURSE_TYPE_ENGLISH_SPOKEN -> SPOKEN | 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 | else -> NORMAL | ||||
} | } | ||||
mDataBase = SQLiteDatabase.openDatabase(FilePathManager.getCourseDbPath(currentBase).path, | mDataBase = SQLiteDatabase.openDatabase(FilePathManager.getCourseDbPath(currentBase).path, p, null, | ||||
p, | |||||
null, | |||||
SQLiteDatabase.OPEN_READONLY) | SQLiteDatabase.OPEN_READONLY) | ||||
} | } | ||||
if (mDataBase == null) { | if (mDataBase == null) { | ||||
val lessonId = it.getLong(it.getColumnIndex("lesson_id")) | val lessonId = it.getLong(it.getColumnIndex("lesson_id")) | ||||
val lessonName : String = when (base.courseType) { | val lessonName : String = when (base.courseType) { | ||||
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION, AppConstants.COURSE_TYPE_CHINESE_LITERACY, AppConstants.COURSE_TYPE_CHINESE_PINYIN -> it.getString( | 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")) | else -> it.getString(it.getColumnIndex("lesson")) | ||||
} | } | ||||
val wordIds : MutableList<Long> = when (base.courseType) { | val wordIds : MutableList<Long> = when (base.courseType) { | ||||
// 注:针对口语对话课时,如果没有学习完,是不会设置进度点的。 todo 分课程类型设置进度点 | // 注:针对口语对话课时,如果没有学习完,是不会设置进度点的。 todo 分课程类型设置进度点 | ||||
val learnIsOver = wordIds.size - 1 == learnIndex | val learnIsOver = wordIds.size - 1 == learnIndex | ||||
val lesson = Lesson(base.subjectId, | val lesson = Lesson(base.subjectId, base.coursePackId, base.coursePackType, base.courseId, base.courseType, | ||||
base.coursePackId, | chapterId, chapterName, lessonId, lessonName).apply { | ||||
base.coursePackType, | |||||
base.courseId, | |||||
base.courseType, | |||||
chapterId, | |||||
chapterName, | |||||
lessonId, | |||||
lessonName).apply { | |||||
lessonPositionInList = positionIndex | lessonPositionInList = positionIndex | ||||
this.wordIds = wordIds //内容 | this.wordIds = wordIds //内容 | ||||
totalNumber = this.wordIds.size //总数 | totalNumber = this.wordIds.size //总数 | ||||
mutableList.add(lesson) | mutableList.add(lesson) | ||||
positionIndex += 1 | positionIndex += 1 | ||||
// TODO: 2022/5/9 对课时数量进行限制,课时太多,开发人员不好进行测试 | // TODO: 2022/5/9 对课时数量进行限制,课时太多,开发人员不好进行测试 | ||||
val needBreak = when(base.coursePackType){ | val needBreak = when (base.coursePackType) { | ||||
AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION -> positionIndex == 11 //保留十个课时 | AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION -> positionIndex == 11 //保留十个课时 | ||||
AppConstants.COURSEPACK_TYPE_ENGLISH_SPOKEN -> positionIndex == 7 //保留六个课时 | AppConstants.COURSEPACK_TYPE_ENGLISH_SPOKEN -> positionIndex == 7 //保留六个课时 | ||||
else -> positionIndex == 3 //保留三个课时 | else -> positionIndex == 3 //保留三个课时 | ||||
} | } | ||||
if (needBreak){ | if (needBreak) { | ||||
break | break | ||||
} | } | ||||
} | } | ||||
} | } | ||||
val sql = when (base.courseType) { | 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_DISCERN, AppConstants.COURSE_TYPE_ENGLISH_VOICE, | ||||
AppConstants.COURSE_TYPE_ENGLISH_SOUNDMARK, AppConstants.COURSE_TYPE_CHINESE_LITERACY, | AppConstants.COURSE_TYPE_ENGLISH_SOUNDMARK, AppConstants.COURSE_TYPE_CHINESE_LITERACY, | ||||
mDataBase?.rawQuery(sql, null)?.run { | mDataBase?.rawQuery(sql, null)?.run { | ||||
when (base.courseType) { | when (base.courseType) { | ||||
//作文知识点测试 | //作文知识点测试 | ||||
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> while (moveToNext()) { | AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> while (moveToNext()) { | ||||
result.add(ExamBean().apply { | result.add(ExamBean().apply { | ||||
id = getLong(3) | id = getLong(3) | ||||
word_id = getLong(2) | word_id = getLong(2) | ||||
}) | }) | ||||
} | } | ||||
AppConstants.COURSE_TYPE_ENGLISH_DISCERN, AppConstants.COURSE_TYPE_ENGLISH_VOICE, | AppConstants.COURSE_TYPE_ENGLISH_DISCERN, AppConstants.COURSE_TYPE_ENGLISH_VOICE, AppConstants.COURSE_TYPE_CHINESE_LITERACY, AppConstants.COURSE_TYPE_CHINESE_PINYIN -> while (moveToNext()) { | ||||
AppConstants.COURSE_TYPE_CHINESE_LITERACY, | result.add(ExamBean().apply { | ||||
AppConstants.COURSE_TYPE_CHINESE_PINYIN -> while (moveToNext()) { | id = getLong(0) | ||||
result.add(ExamBean().apply { | word_id = getLong(1) | ||||
id = getLong(0) | word = getString(2) | ||||
word_id = getLong(1) | correct = getString(3) | ||||
word = getString(2) | error1 = getString(4) | ||||
correct = getString(3) | error2 = getString(5) | ||||
error1 = getString(4) | error3 = getString(6) | ||||
error2 = getString(5) | type = AppConstants.TEST_QUEST_TYPE_CHOICE // 为1 | ||||
error3 = getString(6) | chapterId = getLong(8) | ||||
type = AppConstants.TEST_QUEST_TYPE_CHOICE // 为1 | lessonId = getLong(9) | ||||
chapterId = getLong(8) | }) | ||||
lessonId = getLong(9) | } | ||||
}) | AppConstants.COURSE_TYPE_ENGLISH_SOUNDMARK -> while (moveToNext()) { | ||||
} | |||||
AppConstants.COURSE_TYPE_ENGLISH_SOUNDMARK -> while (moveToNext()) { | |||||
result.add(ExamBean().apply { | result.add(ExamBean().apply { | ||||
id = getLong(0) | id = getLong(0) | ||||
word_id = getLong(1) | word_id = getLong(1) | ||||
//不为空,写入本身,如果为空,用另外的发音方式写入 | //不为空,写入本身,如果为空,用另外的发音方式写入 | ||||
val parentPath = FileUtil.getSaveDirPath(AppConfig.VOICE) | val parentPath = FileUtil.getSaveDirPath(AppConfig.VOICE) | ||||
val audio_us_file_path = audio_us?.let { | val audio_us_file_path = audio_us?.let { | ||||
val audioFileNameUS = | val audioFileNameUS = "${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_US}" | ||||
"${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_US}" | |||||
val file = File(parentPath, audioFileNameUS) | val file = File(parentPath, audioFileNameUS) | ||||
FileUtil.writeBytesToFile(file, it) | FileUtil.writeBytesToFile(file, it) | ||||
file.path | file.path | ||||
} ?: audio_uk?.let { | } ?: audio_uk?.let { | ||||
val audioFileNameUS = | val audioFileNameUS = "${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_US}" | ||||
"${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_US}" | |||||
val file = File(parentPath, audioFileNameUS) | val file = File(parentPath, audioFileNameUS) | ||||
FileUtil.writeBytesToFile(file, it) | FileUtil.writeBytesToFile(file, it) | ||||
file.path | file.path | ||||
} | } | ||||
//不为空,写入本身,如果为空,用另外的发音方式写入 | //不为空,写入本身,如果为空,用另外的发音方式写入 | ||||
val audio_uk_file_path = audio_uk?.let { | val audio_uk_file_path = audio_uk?.let { | ||||
val audioFileNameUk = | val audioFileNameUk = "${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_UK}" | ||||
"${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_UK}" | |||||
val file = File(parentPath, audioFileNameUk) | val file = File(parentPath, audioFileNameUk) | ||||
FileUtil.writeBytesToFile(file, it) | FileUtil.writeBytesToFile(file, it) | ||||
file.path | file.path | ||||
} ?: audio_us?.let { | } ?: audio_us?.let { | ||||
val audioFileNameUk = | val audioFileNameUk = "${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_UK}" | ||||
"${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_UK}" | |||||
val file = File(parentPath, audioFileNameUk) | val file = File(parentPath, audioFileNameUk) | ||||
FileUtil.writeBytesToFile(file, it) | FileUtil.writeBytesToFile(file, it) | ||||
file.path | file.path | ||||
while (it.moveToNext()) { | while (it.moveToNext()) { | ||||
//写入文件 | //写入文件 | ||||
audio_cn_file_path = it.getBlobOrNull(0)?.let { | audio_cn_file_path = it.getBlobOrNull(0)?.let { | ||||
val audioFileNameCN = | val audioFileNameCN = "${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_CN}" | ||||
"${base.subjectId}_${base.coursePackId}_${base.courseId}_${wordId}_${AppConstants.SOUND_TYPE_CN}" | |||||
val file = File(FileUtil.getSaveDirPath(AppConfig.VOICE), audioFileNameCN) | val file = File(FileUtil.getSaveDirPath(AppConfig.VOICE), audioFileNameCN) | ||||
FileUtil.writeBytesToFile(file, it) | FileUtil.writeBytesToFile(file, it) | ||||
file.path | file.path | ||||
open(dbcb) | open(dbcb) | ||||
//从lesson已学位置开始获取数据 | //从lesson已学位置开始获取数据 | ||||
val needLearnIds = lesson.wordIds.subList(lesson.learnedIndex + 1, lesson.wordIds.size) | val needLearnIds = lesson.wordIds.subList(lesson.learnedIndex + 1, lesson.wordIds.size) | ||||
val sql = | val sql = "SELECT * FROM chapter WHERE chapter_id = ${lesson.chapterId} and lesson_id = ${lesson.lessonId} AND word_id in (${ | ||||
"SELECT * FROM chapter WHERE chapter_id = ${lesson.chapterId} and lesson_id = ${lesson.lessonId} AND word_id in (${ | Joiner.on(",").join(needLearnIds) | ||||
Joiner.on(",").join(needLearnIds) | }) " + "ORDER by word_sort ASC" | ||||
}) " + "ORDER by word_sort ASC" | |||||
mDataBase?.rawQuery(sql, null)?.run { | mDataBase?.rawQuery(sql, null)?.run { | ||||
while (moveToNext()) { | while (moveToNext()) { | ||||
//单词id | //单词id | ||||
val word_id = getLong(getColumnIndex("word_id")) | val word_id = getLong(getColumnIndex("word_id")) | ||||
//学习单词实体 | //学习单词实体 | ||||
result.add(LearnWord(dbcb.subjectId, | result.add(LearnWord(dbcb.subjectId, dbcb.coursePackId, dbcb.courseId, dbcb.coursePackType, dbcb.courseType, | ||||
dbcb.coursePackId, | lesson.chapterId, lesson.lessonId, word_id, true, lesson.lessonType).apply { | ||||
dbcb.courseId, | |||||
dbcb.coursePackType, | |||||
dbcb.courseType, | |||||
lesson.chapterId, | |||||
lesson.lessonId, | |||||
word_id, | |||||
true, | |||||
lesson.lessonType).apply { | |||||
word = getString(getColumnIndex("word")) | word = getString(getColumnIndex("word")) | ||||
basic_explanation = getString(getColumnIndex("basic_explaination")) | basic_explanation = getString(getColumnIndex("basic_explaination")) | ||||
extend_explanation = getString(getColumnIndex("all_explaination")) | extend_explanation = getString(getColumnIndex("all_explaination")) | ||||
else -> wordIds.append(value).append(",") | else -> wordIds.append(value).append(",") | ||||
} | } | ||||
} | } | ||||
val sql = | val sql = "SELECT * FROM knowledge WHERE chapter_id = ${lesson.chapterId} AND knowledge_id in ($wordIds) ORDER by CASE $sortSqlBuilder END" | ||||
"SELECT * FROM knowledge WHERE chapter_id = ${lesson.chapterId} AND knowledge_id in ($wordIds) ORDER by CASE $sortSqlBuilder END" | |||||
mDataBase?.rawQuery(sql, null)?.run { | mDataBase?.rawQuery(sql, null)?.run { | ||||
while (moveToNext()) { | while (moveToNext()) { | ||||
//学习单词实体 | //学习单词实体 | ||||
result.add(LearnWord(dbcb.subjectId, | result.add(LearnWord(dbcb.subjectId, dbcb.coursePackId, dbcb.courseId, dbcb.coursePackType, dbcb.courseType, | ||||
dbcb.coursePackId, | lesson.chapterId, lesson.lessonId, getLong(getColumnIndex("knowledge_id")), true, | ||||
dbcb.courseId, | |||||
dbcb.coursePackType, | |||||
dbcb.courseType, | |||||
lesson.chapterId, | |||||
lesson.lessonId, | |||||
getLong(getColumnIndex("knowledge_id")), | |||||
true, | |||||
lesson.lessonType).apply { | lesson.lessonType).apply { | ||||
word = getString(getColumnIndex("title")) | word = getString(getColumnIndex("title")) | ||||
basic_explanation = getString(getColumnIndex("explaination")) | basic_explanation = getString(getColumnIndex("explaination")) | ||||
* @return List<LearnWord> | * @return List<LearnWord> | ||||
*/ | */ | ||||
@SuppressLint("Range") | @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>() | val result = mutableListOf<LearnWord>() | ||||
open(dbcb) | 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" | !isNeedOriginSort -> "SELECT * FROM chapter WHERE chapter_id = $chapterId and lesson_id = $lessonId AND word_id in ($wordIds) ORDER by word_sort ASC" | ||||
else -> { | else -> { | ||||
val sortSqlBuilder = StringBuilder() //排序value | val sortSqlBuilder = StringBuilder() //排序value | ||||
mDataBase?.rawQuery(sql, null)?.run { | mDataBase?.rawQuery(sql, null)?.run { | ||||
while (moveToNext()) { | while (moveToNext()) { | ||||
//学习单词实体 | //学习单词实体 | ||||
result.add(LearnWord(dbcb.subjectId, | result.add(LearnWord(dbcb.subjectId, dbcb.coursePackId, dbcb.courseId, dbcb.coursePackType, dbcb.courseType, | ||||
dbcb.coursePackId, | chapterId, lessonId, getLong(getColumnIndex("word_id")), true, | ||||
dbcb.courseId, | |||||
dbcb.coursePackType, | |||||
dbcb.courseType, | |||||
chapterId, | |||||
lessonId, | |||||
getLong(getColumnIndex("word_id")), | |||||
true, | |||||
AppConstants.LESSON_TYPE_WORD).apply { | AppConstants.LESSON_TYPE_WORD).apply { | ||||
word = getString(getColumnIndex("word")) | word = getString(getColumnIndex("word")) | ||||
basic_explanation = getString(getColumnIndex("basic_explaination")) | basic_explanation = getString(getColumnIndex("basic_explaination")) | ||||
phrase = getString(getColumnIndex("phrase")) | phrase = getString(getColumnIndex("phrase")) | ||||
example = getString(getColumnIndex("example")) | example = getString(getColumnIndex("example")) | ||||
reference = getString(getColumnIndex("reference")) | reference = getString(getColumnIndex("reference")) | ||||
when (dbcb.courseType) { | when (dbcb.courseType) { | ||||
AppConstants.COURSE_TYPE_CHINESE_LITERACY -> { | AppConstants.COURSE_TYPE_CHINESE_LITERACY -> { | ||||
literacyIspolyphone = getInt(getColumnIndex("polyphone")) > 0 | literacyIspolyphone = getInt(getColumnIndex("polyphone")) > 0 | ||||
fun queryCompositionReading(dbcb : DbControlBase, entity : Lesson) : List<CompositionReadingBean> { | fun queryCompositionReading(dbcb : DbControlBase, entity : Lesson) : List<CompositionReadingBean> { | ||||
val result = mutableListOf<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() | val sortedCase = StringBuilder() | ||||
it.forEachIndexed{ index,id -> | it.forEachIndexed { index, id -> | ||||
sortedCase.append(" WHEN reading_id = $id THEN $index") | sortedCase.append(" WHEN reading_id = $id THEN $index") | ||||
} | } | ||||
sortedCase | sortedCase | ||||
} + " END" | } + " END" | ||||
open(dbcb) | open(dbcb) | ||||
mDataBase?.rawQuery(sql,null)?.run { | mDataBase?.rawQuery(sql, null)?.run { | ||||
while (moveToNext()){ | while (moveToNext()) { | ||||
result.add(CompositionReadingBean( | result.add(CompositionReadingBean(getLong(2), getString(3), getString(4), getString(7), getString(6)).apply { | ||||
getLong(2), | collectId = 0 | ||||
getString(3), | |||||
getString(4), | |||||
getString(7), | |||||
getString(6)).apply { | |||||
collectId = 0 | |||||
}) | }) | ||||
} | } | ||||
close() | close() | ||||
* @param entity Lesson | * @param entity Lesson | ||||
* @return List<CompositionTaskBean> | * @return List<CompositionTaskBean> | ||||
*/ | */ | ||||
fun queryCompositonTask(dbcb : DbControlBase,entity : Lesson) : List<CompositionTaskBean>{ | fun queryCompositionTask(dbcb : DbControlBase, entity : Lesson) : List<CompositionTaskBean> { | ||||
val result = mutableListOf<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 | return result | ||||
} | } | ||||
package com.xkl.cdl.data.repository | package com.xkl.cdl.data.repository | ||||
import androidx.lifecycle.MutableLiveData | |||||
import com.suliang.common.util.file.FileUtil | import com.suliang.common.util.file.FileUtil | ||||
import com.xkl.cdl.data.bean.course.CourseDetail | import com.xkl.cdl.data.bean.course.CourseDetail | ||||
import com.xkl.cdl.data.bean.course.Lesson | import com.xkl.cdl.data.bean.course.Lesson |
package com.xkl.cdl.module.learn | package com.xkl.cdl.module.learn | ||||
import android.media.Image | |||||
import android.os.Bundle | import android.os.Bundle | ||||
import android.text.TextUtils | |||||
import android.widget.ImageView | |||||
import androidx.databinding.DataBindingUtil | |||||
import androidx.lifecycle.ViewModelProvider | 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.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.ActivityLearnCtaskBinding | ||||
import com.xkl.cdl.databinding.DialogBottomAutoPlaySelectBinding | |||||
/** | /** | ||||
* author suliang | * author suliang | ||||
} | } | ||||
override fun initActivity(savedInstanceState : Bundle?) { | 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() { | 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() | |||||
} | } | ||||
package com.xkl.cdl.module.learn | 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.base.viewmodel.BaseViewModel | ||||
import com.suliang.common.extension.diskIo2Main | |||||
import com.xkl.cdl.data.DataTransferHolder | 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.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() { | class LearnCTaskViewModel : BaseViewModel() { | ||||
val lesson = DataTransferHolder.instance.getData<Lesson>() | 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 = "课外练习数据获取异常,请联系管理人员进行处理")) | |||||
}) | |||||
} | } | ||||
} | } |
<?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> |
grpc_protobuf: "io.grpc:grpc-protobuf:1.27.0", | grpc_protobuf: "io.grpc:grpc-protobuf:1.27.0", | ||||
grpc_stub: "io.grpc:grpc-stub:1.27.0", | grpc_stub: "io.grpc:grpc-stub:1.27.0", | ||||
//liveEventBus https://github.com/JeremyLiao/LiveEventBus | //liveEventBus 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" | |||||
] | ] | ||||
import com.bumptech.glide.module.LibraryGlideModule | import com.bumptech.glide.module.LibraryGlideModule | ||||
import com.bumptech.glide.request.RequestOptions | import com.bumptech.glide.request.RequestOptions | ||||
import com.suliang.common.util.file.FileUtil | import com.suliang.common.util.file.FileUtil | ||||
import com.bumptech.glide.load.model.GlideUrl | |||||
import java.io.InputStream | |||||
/** | /** | ||||
* author suliang | * author suliang | ||||
// setDefaultRequestOptions(apply) | // 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())) | |||||
} | |||||
} | } |