| <entry key="..\:/xuekaole/XKLLocal/app/src/main/res/layout/fragment_statistics_time_top.xml" value="0.23385416666666667" /> | <entry key="..\:/xuekaole/XKLLocal/app/src/main/res/layout/fragment_statistics_time_top.xml" value="0.23385416666666667" /> | ||||
| <entry key="..\:/xuekaole/XKLLocal/app/src/main/res/layout/item_statics_for_time.xml" value="0.24791666666666667" /> | <entry key="..\:/xuekaole/XKLLocal/app/src/main/res/layout/item_statics_for_time.xml" value="0.24791666666666667" /> | ||||
| <entry key="..\:/xuekaole/XKLLocal/lib/common/src/main/res/drawable/ic_search.xml" value="0.2212962962962963" /> | <entry key="..\:/xuekaole/XKLLocal/lib/common/src/main/res/drawable/ic_search.xml" value="0.2212962962962963" /> | ||||
| <entry key="..\:/xuekaole/XKLLocal/videoplayer/src/main/res/layout/pine_player_media_controller.xml" value="0.125" /> | |||||
| <entry key="..\:/xuekaole/XKLLocal/videoplayer/src/main/res/layout/pine_player_media_controller_full.xml" value="0.125" /> | |||||
| <entry key="..\:/xuekaole/XKLLocal/videoplayer/src/main/res/layout/video_popup.xml" value="0.1953125" /> | |||||
| </map> | </map> | ||||
| </option> | </option> | ||||
| </component> | </component> |
| implementation fileTree(include: ['*.jar', "*.aar"], dir: 'libs') | implementation fileTree(include: ['*.jar', "*.aar"], dir: 'libs') | ||||
| // implementation 'androidx.legacy:legacy-support-v4:1.0.0' | // implementation 'androidx.legacy:legacy-support-v4:1.0.0' | ||||
| implementation project(path: ':lib:common') | implementation project(path: ':lib:common') | ||||
| implementation project(path: ':videoplayer') | |||||
| implementation 'androidx.appcompat:appcompat:1.2.0' | implementation 'androidx.appcompat:appcompat:1.2.0' | ||||
| implementation 'com.google.android.material:material:1.3.0' | implementation 'com.google.android.material:material:1.3.0' | ||||
| implementation 'androidx.constraintlayout:constraintlayout:2.0.4' | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' | ||||
| implementation project(path: ':videoplayer') | |||||
| // implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1' | // implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1' | ||||
| // implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' | // implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' | ||||
| // implementation 'androidx.appcompat:appcompat:1.2.0' | // implementation 'androidx.appcompat:appcompat:1.2.0' |
| while (moveToNext()) { | while (moveToNext()) { | ||||
| val videoAnchor = VideoAnchor().apply { | val videoAnchor = VideoAnchor().apply { | ||||
| anchor_id = getLong(3) | anchor_id = getLong(3) | ||||
| title = getString(5) | |||||
| // TODO: 2022/8/24 需要确定数据库打包是否错误,没有错误用4,有错误用5 | |||||
| title = getString(4) | |||||
| time = DateUtil.getTimeSecond(getString(2)) | time = DateUtil.getTimeSecond(getString(2)) | ||||
| } | } | ||||
| videoAnchorList.add(videoAnchor) | videoAnchorList.add(videoAnchor) |
| if (vm.course.courseType == AppConstants.COURSE_TYPE_ENGLISH_SPOKEN) { | if (vm.course.courseType == AppConstants.COURSE_TYPE_ENGLISH_SPOKEN) { | ||||
| binding.spokenTopLayout.visibility = View.VISIBLE | binding.spokenTopLayout.visibility = View.VISIBLE | ||||
| } | } | ||||
| val selectPos = vm.initSelectPosition() | |||||
| binding.recyclerView.apply { | binding.recyclerView.apply { | ||||
| layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) | layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) | ||||
| adapterLesson = AdapterLesson(vm).apply { | adapterLesson = AdapterLesson(vm).apply { | ||||
| onItemClick = onLessonClick | onItemClick = onLessonClick | ||||
| //默认选中位置,默认为0 ,如果学习完成则设置为-1 | //默认选中位置,默认为0 ,如果学习完成则设置为-1 | ||||
| selectPos = vm.initSelectPosition() | |||||
| this.selectPos = selectPos | |||||
| } | } | ||||
| adapter = adapterLesson | adapter = adapterLesson | ||||
| } | } | ||||
| //设置数据 | //设置数据 | ||||
| (binding.recyclerView.adapter as AdapterLesson).setData(vm.allLesson.toMutableList()) | (binding.recyclerView.adapter as AdapterLesson).setData(vm.allLesson.toMutableList()) | ||||
| //滑动到学习的位置 | |||||
| if (selectPos != -1){ | |||||
| binding.recyclerView.post { | |||||
| binding.recyclerView.smoothScrollToPosition(selectPos) | |||||
| } | |||||
| } | |||||
| } | } | ||||
| override fun loadData() { | override fun loadData() { |
| } | } | ||||
| } | } | ||||
| /** 查询课程的所有课时 */ | |||||
| /** 查询课程的所有课时 ,作文显示章节,故只使用了知识点学习所在的课时*/ | |||||
| fun queryAllLesson() { | fun queryAllLesson() { | ||||
| if (isAllLessonListInit()) { | if (isAllLessonListInit()) { | ||||
| showLessonListLivaData.value = 2 | showLessonListLivaData.value = 2 | ||||
| } else { | } else { | ||||
| DataRepository.getCourseAllLesson(dbControlBase, CourseDetail()).compose(diskIo2Main()).subscribe({ | DataRepository.getCourseAllLesson(dbControlBase, CourseDetail()).compose(diskIo2Main()).subscribe({ | ||||
| allLessonList = it.toMutableList() | |||||
| //如果是作文,取知识点的所在的章节即可 | |||||
| if (memoData.course.coursePackType == AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION){ | |||||
| allLessonList = it.filter { | |||||
| it.lessonType == AppConstants.LESSON_TYPE_COMPOSITION_KNOWLEDGE | |||||
| }.toMutableList() | |||||
| } else { | |||||
| allLessonList = it.toMutableList() | |||||
| } | |||||
| showLessonListLivaData.value = 2 | showLessonListLivaData.value = 2 | ||||
| }, { | }, { | ||||
| it.printStackTrace() | it.printStackTrace() | ||||
| }) | }) | ||||
| //查询测试数据 | //查询测试数据 | ||||
| val testDataList = DBCourseManager.queryMemoTest(dbControlBase, queryLesson, count) | val testDataList = DBCourseManager.queryMemoTest(dbControlBase, queryLesson, count) | ||||
| //测试类型 | //测试类型 | ||||
| val examType = if (recordTestType == 1) AppConstants.TEST_TYPE_MEMO else AppConstants.TEST_TYPE_SERVICE_CENTER | |||||
| return@fromCallable ExamData(memoData.course.subjectId, examType, memoData.course.courseTitle, | |||||
| saveTitle).apply { | |||||
| val examType = if (recordTestType == 1) AppConstants.TEST_TYPE_MEMO else AppConstants.TEST_TYPE_SERVICE_CENTER | |||||
| return@fromCallable ExamData(memoData.course.subjectId, examType, memoData.course.courseTitle, saveTitle).apply { | |||||
| coursePackId = memoData.course.coursePackId //课程包id, 测试错误上次数据需要 | coursePackId = memoData.course.coursePackId //课程包id, 测试错误上次数据需要 | ||||
| coursePackType = memoData.course.coursePackType //课程包类型 | coursePackType = memoData.course.coursePackType //课程包类型 | ||||
| courseId = memoData.course.courseId //课程id | courseId = memoData.course.courseId //课程id |
| } | } | ||||
| } | } | ||||
| /**实例化增量图标和颜色与正负*/ | |||||
| /**实例化增量图标和颜色*/ | |||||
| private fun initIncr(timeStaticItem_1 : TimeStatisticItem, sdIncr : Double) { | private fun initIncr(timeStaticItem_1 : TimeStatisticItem, sdIncr : Double) { | ||||
| timeStaticItem_1.run { | timeStaticItem_1.run { | ||||
| when{ | when{ |
| * @return true在 false不存在 | * @return true在 false不存在 | ||||
| */ | */ | ||||
| fun isInExamErrorMap(key : String) : Boolean { | fun isInExamErrorMap(key : String) : Boolean { | ||||
| return examErrorsMap?.getOrDefault(key, false) ?: false | |||||
| return examErrorsMap?.containsKey(key) ?: false | |||||
| } | } | ||||
| /** 是否是在学习的错误列表中 */ | /** 是否是在学习的错误列表中 */ | ||||
| fun isInCurrentErrorMap(key : String) : Boolean { | fun isInCurrentErrorMap(key : String) : Boolean { | ||||
| return currentErrorMap.getOrDefault(key, false) | |||||
| return currentErrorMap.containsKey(key) | |||||
| } | } | ||||
| /** 判断源数据中的数据是否已经学习完成:由外部调用,originLearnPosition下标的数据为已经取出来学习的数据,只有为第一个的时候,才判断此方法 */ | /** 判断源数据中的数据是否已经学习完成:由外部调用,originLearnPosition下标的数据为已经取出来学习的数据,只有为第一个的时候,才判断此方法 */ |
| # http://www.gradle.org/docs/current/userguide/build_environment.html | # http://www.gradle.org/docs/current/userguide/build_environment.html | ||||
| # Specifies the JVM arguments used for the daemon process. | # Specifies the JVM arguments used for the daemon process. | ||||
| # The setting is particularly useful for tweaking memory settings. | # The setting is particularly useful for tweaking memory settings. | ||||
| org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 | |||||
| #org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 | |||||
| org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 | |||||
| # When configured, Gradle will run in incubating parallel mode. | # When configured, Gradle will run in incubating parallel mode. | ||||
| # This option should only be used with decoupled projects. More details, visit | # This option should only be used with decoupled projects. More details, visit | ||||
| # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects |
| rootProject.name = "XklLocal" | rootProject.name = "XklLocal" | ||||
| include ':app' | include ':app' | ||||
| include ':lib:common' | include ':lib:common' | ||||
| include ':videoplayer' | |||||
| include 'videoplayer' |
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| package="com.xkl.videoplayer" > | package="com.xkl.videoplayer" > | ||||
| <!-- <uses-permission android:name="android.permission.WRITE_SETTINGS" />--> | |||||
| <!-- <uses-permission android:name="android.permission.WRITE_SETTINGS" />--> | |||||
| <application | <application | ||||
| android:allowBackup="true" | android:allowBackup="true" | ||||
| android:label="@string/app_name" | android:label="@string/app_name" |
| cmake_minimum_required(VERSION 3.4.1) | cmake_minimum_required(VERSION 3.4.1) | ||||
| #if (${ANDROID_ABI} STREQUAL "armeabi-v7a") | |||||
| # include_directories(${ANDROID_SYSROOT}/usr/include/arm-linux-androideabi) | |||||
| #elseif (${ANDROID_ABI} STREQUAL "arm64-v8a") | |||||
| # include_directories(${ANDROID_SYSROOT}/usr/include/aarch64-linux-android) | |||||
| #else () | |||||
| # include_directories(${ANDROID_SYSROOT}/usr/include/arm-linux-androideabi) | |||||
| #endif () | |||||
| find_library( | find_library( | ||||
| log-lib | log-lib | ||||
| ) | ) | ||||
| #add_library( | |||||
| # postproc | |||||
| # SHARED | |||||
| # IMPORTED | |||||
| #) | |||||
| #set_target_properties(postproc | |||||
| #PROPERTIES IMPORTED_LOCATION ../../../../libs/${ANDROID_ABI}/lipostproc.so | |||||
| #) | |||||
| target_link_libraries(MoviePlayer | target_link_libraries(MoviePlayer | ||||
| avutil | avutil |
| private long previousPlayTime; //播放上一次记录的时间 | private long previousPlayTime; //播放上一次记录的时间 | ||||
| public void PlayPCM(byte[] pcmBuffer, int length, int milliseconds) { | public void PlayPCM(byte[] pcmBuffer, int length, int milliseconds) { | ||||
| // LogUtil.e("MoviePlayer", "PlayPCM milliseconds = " + milliseconds + " Length=" + length ); | |||||
| // LogUtil.INSTANCE.e("MoviePlayer", "PlayPCM milliseconds = " + milliseconds + " Length=" + length ); | |||||
| if (null != audioTrack && AudioTrack.PLAYSTATE_PLAYING == audioTrack.getPlayState()) { | if (null != audioTrack && AudioTrack.PLAYSTATE_PLAYING == audioTrack.getPlayState()) { | ||||
| audioTrack.write(pcmBuffer, 0, length); | audioTrack.write(pcmBuffer, 0, length); | ||||
| currentPosition = milliseconds; | currentPosition = milliseconds; | ||||
| if (l - l1 > 3000) { //误差超过三秒降帧 | if (l - l1 > 3000) { //误差超过三秒降帧 | ||||
| previousPlayTime = 0; | previousPlayTime = 0; | ||||
| LogUtil.INSTANCE.e("MoviePlayer localTimeInterval = " + l + " playIntervelTime = " + l1); | |||||
| LogUtil.INSTANCE.e("MoviePlayer", "localTimeInterval = " + l + " playIntervelTime = " + l1); | |||||
| if (onPlayStatusChanged != null) { | if (onPlayStatusChanged != null) { | ||||
| onPlayStatusChanged.onStatus(10); | onPlayStatusChanged.onStatus(10); | ||||
| } | } |
| /** | /** | ||||
| * Created by tanghongfeng on 2017/9/18. | * Created by tanghongfeng on 2017/9/18. | ||||
| * 要播放的多媒体的 | |||||
| */ | */ | ||||
| /** | /** |
| /** | /** | ||||
| * Created by tanghongfeng on 2018/3/7. | * Created by tanghongfeng on 2018/3/7. | ||||
| * 要播放的多媒体的数据源的实体类 | |||||
| */ | */ | ||||
| public class PineMediaUriSource implements Serializable { | public class PineMediaUriSource implements Serializable { |
| /** | /** | ||||
| * 播放器组件,封装播放器细节 | |||||
| * 封装具体的播放器,通过代理PineMediaPlayerProxy与界面进行交互 | * 封装具体的播放器,通过代理PineMediaPlayerProxy与界面进行交互 | ||||
| * Created by tanghongfeng on 2017/8/14. | * Created by tanghongfeng on 2017/8/14. | ||||
| */ | */ | ||||
| LogUtil.INSTANCE.e("=================>", "状态:" + "JPlayer"); | LogUtil.INSTANCE.e("=================>", "状态:" + "JPlayer"); | ||||
| isSwitchDefinition = false; | isSwitchDefinition = false; | ||||
| moviePlayer.JPlay(); | moviePlayer.JPlay(); | ||||
| if (moviePlayer.getCachPosition() != 0) { | |||||
| LogUtil.INSTANCE.e("=================>", "状态:" + "SetPlayTime:" + moviePlayer.getCachPosition()); | |||||
| moviePlayer.SetPlayTime(moviePlayer.getCachPosition()); | |||||
| moviePlayer.setCachPosition(0); | |||||
| // if (moviePlayer.getCachPosition() != 0) { | |||||
| // LogUtil.INSTANCE.e("=================>", "状态:" + "SetPlayTime:" + moviePlayer.getCachPosition()); | |||||
| // moviePlayer.SetPlayTime(moviePlayer.getCachPosition()); | |||||
| // moviePlayer.setCachPosition(0); | |||||
| // } | |||||
| if (getCachPositon() != 0) { | |||||
| LogUtil.INSTANCE.e("=================>", "状态:" + "SetPlayTime:" + getCachPositon()); | |||||
| moviePlayer.SetPlayTime(getCachPositon()); | |||||
| setCachPosition(0); | |||||
| } | } | ||||
| } else { | } else { | ||||
| LogUtil.INSTANCE.e("=================>", "状态:" + "JResume:"); | LogUtil.INSTANCE.e("=================>", "状态:" + "JResume:"); | ||||
| moviePlayer.setCurrentPosition(position); | moviePlayer.setCurrentPosition(position); | ||||
| } | } | ||||
| } | } | ||||
| private int chachePosition = 0 ; | |||||
| public void setCachPosition(int position) { | public void setCachPosition(int position) { | ||||
| if (moviePlayer == null){ | |||||
| chachePosition = position ; | |||||
| return; | |||||
| } | |||||
| moviePlayer.setCachPosition(position); | moviePlayer.setCachPosition(position); | ||||
| } | } | ||||
| @Override | @Override | ||||
| public int getCachPositon() { | public int getCachPositon() { | ||||
| if (moviePlayer == null){ | |||||
| return chachePosition; | |||||
| } | |||||
| return moviePlayer.getCachPosition(); | return moviePlayer.getCachPosition(); | ||||
| } | } | ||||
| /** | /** | ||||
| * Created by tanghongfeng on 2018/4/2. | * Created by tanghongfeng on 2018/4/2. | ||||
| * 播放器组件代理类 | |||||
| */ | */ | ||||
| public class PineMediaPlayerProxy implements PineMediaWidget.IPineMediaPlayer, PineMediaWidget.IPineMediaPlayerComponent { | public class PineMediaPlayerProxy implements PineMediaWidget.IPineMediaPlayer, PineMediaWidget.IPineMediaPlayerComponent { |
| /** | /** | ||||
| * Created by tanghongfeng on 2017/8/28. | * Created by tanghongfeng on 2017/8/28. | ||||
| * 播放器组件接口集合 | |||||
| */ | */ | ||||
| public class PineMediaWidget { | public class PineMediaWidget { |
| mWaitingProgressViewHolder = new PineWaitingProgressViewHolder(); | mWaitingProgressViewHolder = new PineWaitingProgressViewHolder(); | ||||
| } | } | ||||
| // 内置右侧Views | // 内置右侧Views | ||||
| // LogUtil.INSTANCE.e("attachToParentView",mRightViewHolderList.toString()); | |||||
| LogUtil.INSTANCE.e("attachToParentView",mRightViewHolderList.toString()); | |||||
| if (mRightViewHolderList != null && mRightViewHolderList.size() > 0) { | if (mRightViewHolderList != null && mRightViewHolderList.size() > 0) { | ||||
| mRightViewContainer = new RelativeLayout(getContext()); | mRightViewContainer = new RelativeLayout(getContext()); | ||||
| for (int i = 0; i < mRightViewHolderList.size(); i++) { | for (int i = 0; i < mRightViewHolderList.size(); i++) { |
| mediaListBtn.setVisibility(View.GONE); | mediaListBtn.setVisibility(View.GONE); | ||||
| } | } | ||||
| mDDefinitionBtn = (TextView) mDFullControllerView.findViewById(R.id.media_definition_text); | mDDefinitionBtn = (TextView) mDFullControllerView.findViewById(R.id.media_definition_text); | ||||
| PineMediaPlayerBean pineMediaPlayerBean = player.getMediaPlayerBean(); | |||||
| if (hasDefinitionList(pineMediaPlayerBean) && mDEnableDefinition) { | |||||
| rightViewControlBtnList.add(mDDefinitionBtn); | |||||
| mDDefinitionBtn.setVisibility(View.VISIBLE); | |||||
| mDDefinitionBtn.setText(getDefinitionName(pineMediaPlayerBean.getCurrentDefinition())); | |||||
| } else { | |||||
| // PineMediaPlayerBean pineMediaPlayerBean = player.getMediaPlayerBean(); | |||||
| // if (hasDefinitionList(pineMediaPlayerBean) && mDEnableDefinition) { | |||||
| // rightViewControlBtnList.add(mDDefinitionBtn); | |||||
| // mDDefinitionBtn.setVisibility(View.VISIBLE); | |||||
| // mDDefinitionBtn.setText(getDefinitionName(pineMediaPlayerBean.getCurrentDefinition())); | |||||
| // } else { | |||||
| mDDefinitionBtn.setVisibility(View.GONE); | mDDefinitionBtn.setVisibility(View.GONE); | ||||
| } | |||||
| // } | |||||
| if (rightViewControlBtnList.size() > 0) { | if (rightViewControlBtnList.size() > 0) { | ||||
| mDFullControllerViewHolder.setRightViewControlBtnList(rightViewControlBtnList); | mDFullControllerViewHolder.setRightViewControlBtnList(rightViewControlBtnList); | ||||
| } | } | ||||
| } | } | ||||
| private boolean hasDefinitionList(PineMediaPlayerBean pineMediaPlayerBean) { | private boolean hasDefinitionList(PineMediaPlayerBean pineMediaPlayerBean) { | ||||
| return pineMediaPlayerBean != null && pineMediaPlayerBean.getMediaUriSourceList().size() > 1; | |||||
| return pineMediaPlayerBean != null; | |||||
| // && pineMediaPlayerBean.getMediaUriSourceList().size() > 1; | |||||
| } | } | ||||
| public String getCurMediaCode() { | public String getCurMediaCode() { |
| android:layout_centerVertical="true" | android:layout_centerVertical="true" | ||||
| android:textSize="14sp" | android:textSize="14sp" | ||||
| android:text="@string/pine_media_definition_sd" | android:text="@string/pine_media_definition_sd" | ||||
| android:textColor="@color/pine_controller_text"/> | |||||
| android:textColor="@color/pine_controller_text" | |||||
| android:visibility="gone"/> | |||||
| <ImageView | <ImageView | ||||
| android:id="@+id/full_screen_btn" | android:id="@+id/full_screen_btn" |