<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" |