| <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 (!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) | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } |
| */ | */ | ||||
| @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, | |||||
| p, | |||||
| null, | |||||
| mDataBase = SQLiteDatabase.openDatabase(FilePathManager.getCourseDbPath(currentBase).path, 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, | |||||
| 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 | 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_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 { | 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 = | |||||
| "${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) | 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 = | |||||
| "${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) | 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 = | |||||
| "${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) | 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 = | |||||
| "${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) | 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 = | |||||
| "${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) | 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 = | |||||
| "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 { | 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, | |||||
| 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")) | 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 = | |||||
| "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 { | mDataBase?.rawQuery(sql, null)?.run { | ||||
| while (moveToNext()) { | 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 { | 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, | |||||
| 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 { | 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 { | |||||
| 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() | 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())) | |||||
| } | |||||
| } | } |