Prechádzať zdrojové kódy

1.增加全屏播放器
2.增加滑动播放器
3.增加滑动播放器逻辑和控制
4.增加录音功能/重播录音功能

FailedToRead 3 rokov pred
rodič
commit
cf0472e4ea
81 zmenil súbory, kde vykonal 6434 pridanie a 174 odobranie
  1. 1 2
      .idea/gradle.xml
  2. 14 0
      .idea/misc.xml
  3. 28 2
      app/build.gradle
  4. BIN
      app/libs/armeabi-v7a/libijkffmpeg.so
  5. BIN
      app/libs/armeabi-v7a/libijkplayer.so
  6. BIN
      app/libs/armeabi-v7a/libijksdl.so
  7. 1 1
      app/src/androidTest/java/com/efunbox/reader/ExampleInstrumentedTest.java
  8. 18 4
      app/src/main/AndroidManifest.xml
  9. 1 0
      app/src/main/assets/aiengine.provision
  10. 114 0
      app/src/main/java/com/edufound/reader/activity/MainActivity.java
  11. 76 0
      app/src/main/java/com/edufound/reader/activity/VideoActivity.java
  12. 75 0
      app/src/main/java/com/edufound/reader/adapter/SlideVideoAdapter.java
  13. 1 1
      app/src/main/java/com/efunbox/reader/annotation/BindView.java
  14. 19 11
      app/src/main/java/com/efunbox/reader/application/EApplication.java
  15. 20 5
      app/src/main/java/com/efunbox/reader/base/BaseActivity.java
  16. 1 1
      app/src/main/java/com/efunbox/reader/base/BaseFragment.java
  17. 1 1
      app/src/main/java/com/efunbox/reader/base/BaseMvpActivity.java
  18. 1 1
      app/src/main/java/com/efunbox/reader/base/BaseMvpFragment.java
  19. 1 1
      app/src/main/java/com/efunbox/reader/base/BasePresenter.java
  20. 4 1
      app/src/main/java/com/efunbox/reader/base/BaseView.java
  21. 6 0
      app/src/main/java/com/edufound/reader/bean/ChivoxBean.java
  22. 6 0
      app/src/main/java/com/edufound/reader/bean/SlideVideoBean.java
  23. 30 0
      app/src/main/java/com/edufound/reader/contract/MainContract.java
  24. 67 0
      app/src/main/java/com/edufound/reader/contract/VideoContract.java
  25. 41 0
      app/src/main/java/com/edufound/reader/cusview/FullScreenVideoController.java
  26. 1007 0
      app/src/main/java/com/edufound/reader/cusview/RvListJzvdStd.java
  27. 104 0
      app/src/main/java/com/edufound/reader/ijkplayer/application/Settings.java
  28. 57 0
      app/src/main/java/com/edufound/reader/ijkplayer/media/FileMediaDataSource.java
  29. 42 0
      app/src/main/java/com/edufound/reader/ijkplayer/media/IMediaController.java
  30. 87 0
      app/src/main/java/com/edufound/reader/ijkplayer/media/IRenderView.java
  31. 1150 0
      app/src/main/java/com/edufound/reader/ijkplayer/media/IjkVideoView.java
  32. 215 0
      app/src/main/java/com/edufound/reader/ijkplayer/media/MeasureHelper.java
  33. 288 0
      app/src/main/java/com/edufound/reader/ijkplayer/media/SurfaceRenderView.java
  34. 370 0
      app/src/main/java/com/edufound/reader/ijkplayer/media/TextureRenderView.java
  35. 63 0
      app/src/main/java/com/edufound/reader/ijkplayer/services/MediaPlayerService.java
  36. 6 0
      app/src/main/java/com/edufound/reader/model/MainModel.java
  37. 12 0
      app/src/main/java/com/edufound/reader/model/VideoModel.java
  38. 49 0
      app/src/main/java/com/edufound/reader/net/RxScheduler.java
  39. 232 0
      app/src/main/java/com/edufound/reader/presenter/MainPresenter.java
  40. 160 0
      app/src/main/java/com/edufound/reader/presenter/VideoPresenter.java
  41. 268 0
      app/src/main/java/com/edufound/reader/services/RecordServices.java
  42. 297 0
      app/src/main/java/com/edufound/reader/util/AIEngineHelper.java
  43. 237 0
      app/src/main/java/com/edufound/reader/util/CToast.java
  44. 27 0
      app/src/main/java/com/edufound/reader/util/Consts.java
  45. 1 1
      app/src/main/java/com/efunbox/reader/util/CrashHandler.java
  46. 1 2
      app/src/main/java/com/efunbox/reader/util/DeviceUtil.java
  47. 1 1
      app/src/main/java/com/efunbox/reader/util/DeviceUuidFactory.java
  48. 24 0
      app/src/main/java/com/edufound/reader/util/EfunboxUtil.java
  49. 2 2
      app/src/main/java/com/efunbox/reader/util/GlideUtils.java
  50. 1 1
      app/src/main/java/com/efunbox/reader/util/HttpInterceptor.java
  51. 1 3
      app/src/main/java/com/efunbox/reader/util/LiuHaiScreenUtil.java
  52. 188 0
      app/src/main/java/com/edufound/reader/util/MMKVUtil.java
  53. 22 0
      app/src/main/java/com/edufound/reader/util/PermissionsUtil.java
  54. 1 1
      app/src/main/java/com/efunbox/reader/util/SizeUtils.java
  55. 230 0
      app/src/main/java/com/edufound/reader/videoutil/JZMediaIjk.java
  56. 16 0
      app/src/main/java/com/edufound/reader/videoutil/OnViewPagerListener.java
  57. 133 0
      app/src/main/java/com/edufound/reader/videoutil/ViewPagerLayoutManager.java
  58. 0 50
      app/src/main/java/com/efunbox/reader/activity/MainActivity.java
  59. 0 20
      app/src/main/java/com/efunbox/reader/contract/MainContract.java
  60. 0 6
      app/src/main/java/com/efunbox/reader/model/MainModel.java
  61. 0 6
      app/src/main/java/com/efunbox/reader/model/MainModelImpl.java
  62. 0 11
      app/src/main/java/com/efunbox/reader/presenter/MainPresenter.java
  63. 0 10
      app/src/main/java/com/efunbox/reader/presenter/MainPresenterImpl.java
  64. 0 15
      app/src/main/java/com/efunbox/reader/util/Consts.java
  65. 28 0
      app/src/main/res/drawable/cusjzplayer_controller_seekbar.xml
  66. BIN
      app/src/main/res/drawable/edufound_player_pause.png
  67. BIN
      app/src/main/res/drawable/edufound_player_play.png
  68. BIN
      app/src/main/res/drawable/loading.png
  69. BIN
      app/src/main/res/drawable/player_back.png
  70. BIN
      app/src/main/res/drawable/player_little_icon_pause.png
  71. BIN
      app/src/main/res/drawable/player_little_icon_play.png
  72. 34 0
      app/src/main/res/drawable/player_seekbar_list.xml
  73. BIN
      app/src/main/res/drawable/seekbar_thumb.png
  74. 32 13
      app/src/main/res/layout/activity_main.xml
  75. 7 0
      app/src/main/res/layout/activity_video.xml
  76. 10 0
      app/src/main/res/layout/adapter_item_slidevideo.xml
  77. 255 0
      app/src/main/res/layout/cusjzplayer_controller.xml
  78. 124 0
      app/src/main/res/layout/player_controller.xml
  79. 2 0
      app/src/main/res/values/strings.xml
  80. 123 0
      app/src/main/res/values/strings_pref.xml
  81. 1 1
      app/src/test/java/com/efunbox/reader/ExampleUnitTest.java

+ 1 - 2
.idea/gradle.xml

@@ -4,7 +4,7 @@
   <component name="GradleSettings">
     <option name="linkedExternalProjectsSettings">
       <GradleProjectSettings>
-        <option name="testRunner" value="PLATFORM" />
+        <option name="testRunner" value="GRADLE" />
         <option name="disableWrapperSourceDistributionNotification" value="true" />
         <option name="distributionType" value="DEFAULT_WRAPPED" />
         <option name="externalProjectPath" value="$PROJECT_DIR$" />
@@ -17,7 +17,6 @@
           </set>
         </option>
         <option name="resolveModulePerSourceSet" value="false" />
-        <option name="useQualifiedModuleNames" value="true" />
       </GradleProjectSettings>
     </option>
   </component>

+ 14 - 0
.idea/misc.xml

@@ -1,5 +1,19 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
+  <component name="DesignSurface">
+    <option name="filePathToZoomLevelMap">
+      <map>
+        <entry key="..\:/WorkSpace/Git_WorkSpace/efunboxReader-android/efunboxReader-android-master/app/src/main/res/drawable/cusjzplayer_controller_seekbar.xml" value="0.2713541666666667" />
+        <entry key="..\:/WorkSpace/Git_WorkSpace/efunboxReader-android/efunboxReader-android-master/app/src/main/res/drawable/player_seekbar_list.xml" value="0.2713541666666667" />
+        <entry key="..\:/WorkSpace/Git_WorkSpace/efunboxReader-android/efunboxReader-android-master/app/src/main/res/layout/activity_main.xml" value="0.25" />
+        <entry key="..\:/WorkSpace/Git_WorkSpace/efunboxReader-android/efunboxReader-android-master/app/src/main/res/layout/activity_video.xml" value="0.1331521739130435" />
+        <entry key="..\:/WorkSpace/Git_WorkSpace/efunboxReader-android/efunboxReader-android-master/app/src/main/res/layout/adapter_item_slidevideo.xml" value="0.18177083333333333" />
+        <entry key="..\:/WorkSpace/Git_WorkSpace/efunboxReader-android/efunboxReader-android-master/app/src/main/res/layout/curjzplayer_controller.xml" value="0.16" />
+        <entry key="..\:/WorkSpace/Git_WorkSpace/efunboxReader-android/efunboxReader-android-master/app/src/main/res/layout/cusjzplayer_controller.xml" value="0.18" />
+        <entry key="..\:/WorkSpace/Git_WorkSpace/efunboxReader-android/efunboxReader-android-master/app/src/main/res/layout/player_controller.xml" value="0.13" />
+      </map>
+    </option>
+  </component>
   <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
     <output url="file://$PROJECT_DIR$/build/classes" />
   </component>

+ 28 - 2
app/build.gradle

@@ -3,7 +3,7 @@ plugins {
 }
 
 def releaseTime() {
-    return new Date().format("yyyyMMddhhmmss", TimeZone.getTimeZone("UTC"))
+    return new Date().format("yyyyMMddHHmmss", TimeZone.getTimeZone("UTC"))
 }
 
 android {
@@ -11,7 +11,7 @@ android {
     buildToolsVersion "30.0.2"
 
     defaultConfig {
-        applicationId "com.efunbox.reader"
+        applicationId "com.edufound.reader"
         minSdkVersion 21
         targetSdkVersion 30
         versionCode 1
@@ -19,6 +19,9 @@ android {
         flavorDimensions "versionCode"
         manifestPlaceholders = [UMENG_CHANNEL_VALUE: "2006"]//添加一个默认渠道号
         testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
+        ndk {
+            abiFilters 'armeabi-v7a'
+        }
     }
     signingConfigs {
         efunbox {
@@ -52,6 +55,13 @@ android {
         }
     }
 
+
+    sourceSets {
+        main {
+            jniLibs.srcDirs = ['libs']
+        }
+    }
+
     productFlavors {
         //义方
         channel_efunbox {
@@ -85,8 +95,11 @@ dependencies {
         exclude(module: 'support-annotations')
         exclude(module: 'gson')
     }
+    //gson
     implementation 'com.google.code.gson:gson:2.8.9'
+    //logger
     implementation 'com.orhanobut:logger:2.2.0'
+    //mmkv
     implementation 'com.tencent:mmkv:1.2.11'
     implementation 'com.uber.autodispose2:autodispose:2.0.0'
     implementation 'com.uber.autodispose2:autodispose-android:2.0.0'
@@ -95,4 +108,17 @@ dependencies {
     // 友盟基础组件库(所有友盟业务SDK都依赖基础组件库)
     implementation 'com.umeng.umsdk:common:9.4.4'// (必选)
     implementation 'com.umeng.umsdk:asms:1.4.1'// asms包依赖必选
+
+    //ijkplayer
+    implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
+    implementation "tv.danmaku.ijk.media:ijkplayer-exo:0.8.8"
+
+    //JZ播放器
+    implementation 'cn.jzvd:jiaozivideoplayer:7.7.0'
+
+    //recyclerview
+    implementation 'androidx.recyclerview:recyclerview:1.1.0'
+    //驰声
+    implementation 'io.github.ChivoxSupport:vox-sdk:2.2.23+2.0.8.1'
+
 }

BIN
app/libs/armeabi-v7a/libijkffmpeg.so


BIN
app/libs/armeabi-v7a/libijkplayer.so


BIN
app/libs/armeabi-v7a/libijksdl.so


+ 1 - 1
app/src/androidTest/java/com/efunbox/reader/ExampleInstrumentedTest.java

@@ -1,4 +1,4 @@
-package com.efunbox.reader;
+package com.edufound.reader;
 
 import android.content.Context;
 import androidx.test.platform.app.InstrumentationRegistry;

+ 18 - 4
app/src/main/AndroidManifest.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.efunbox.reader">
+        package="com.edufound.reader">
 
 
     <uses-permission android:name="android.permission.INTERNET" />
@@ -11,10 +11,10 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!-- 本地信息缓存 -->
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!--开关wifi状态,解决国内机型移动⽹络权限问题需要 -->
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
 
     <application
-            android:name=".application.EApplication"
+            android:name="com.edufound.reader.application.EApplication"
             android:allowBackup="true"
             android:icon="${icon}"
             android:label="@string/app_name"
@@ -33,7 +33,7 @@
                 android:value="portrait|landscape" />
 
         <activity
-                android:name=".activity.MainActivity"
+                android:name="com.edufound.reader.activity.MainActivity"
                 android:configChanges="screenLayout|screenSize|keyboardHidden|keyboard|orientation"
                 android:maxAspectRatio="2.1"
                 android:resizeableActivity="true"
@@ -47,6 +47,14 @@
             </intent-filter>
         </activity>
 
+        <activity
+                android:name="com.edufound.reader.activity.VideoActivity"
+                android:configChanges="screenLayout|screenSize|keyboardHidden|keyboard|orientation"
+                android:maxAspectRatio="2.1"
+                android:resizeableActivity="true"
+                android:screenOrientation="landscape"
+                android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen"
+                android:windowSoftInputMode="adjustNothing|stateHidden"></activity>
         <!--友盟start-->
         <meta-data
                 android:name="UMENG_APPKEY"
@@ -56,6 +64,12 @@
                 android:value="${UMENG_CHANNEL_VALUE}" />
         <!--友盟end-->
 
+        <!-- 录音services -->
+        <!--services -->
+        <service android:name="com.edufound.reader.services.RecordServices"></service>
+        <!--services -->
+        <!-- 录音services -->
+
     </application>
 
 

+ 1 - 0
app/src/main/assets/aiengine.provision

@@ -0,0 +1 @@
+侍饰颂掏滔滔滔涛伤墒茩松推怂蕷浵櫶锨似铺殯虤洐仁溝櫸葡蜗锨惹滔上澥浵褪炌茷櫹魄蠙洑贤伤櫄灋螢蔀仙撬翁莆迫澢蠙溙洖

+ 114 - 0
app/src/main/java/com/edufound/reader/activity/MainActivity.java

@@ -0,0 +1,114 @@
+package com.edufound.reader.activity;
+
+import android.app.Activity;
+import android.view.View;
+import android.widget.Button;
+
+import com.edufound.reader.R;
+import com.edufound.reader.annotation.BindView;
+import com.edufound.reader.base.BaseMvpActivity;
+import com.edufound.reader.contract.MainContract;
+import com.edufound.reader.presenter.MainPresenter;
+
+import androidx.recyclerview.widget.RecyclerView;
+import cn.jzvd.Jzvd;
+import cn.jzvd.JzvdStd;
+
+public class MainActivity extends BaseMvpActivity<MainPresenter> implements MainContract.View {
+
+
+    Activity mActivity;
+
+    @BindView(id = R.id.main_initrecord)
+    Button btn_main_initrecord;
+
+    @BindView(id = R.id.main_stoprecord)
+    Button main_stoprecord;
+
+    @BindView(id = R.id.main_playrecord)
+    Button main_playrecord;
+
+
+    @BindView(id = R.id.main_recyclerview)
+    RecyclerView mRecyclerView;
+
+
+    @Override
+    public int getLayoutId() {
+        return R.layout.activity_main;
+    }
+
+    @Override
+    public void initView() {
+        mPresenter = new MainPresenter();
+        mPresenter.attachView(this);
+        mActivity = this;
+//        GlideUtils.loadImageSizeKipMemoryCache(this, "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.zhimg.com%2F50%2Fv2-a96199f59f76eef6698e572c19944e40_hd.jpg&refer=http%3A%2F%2Fpic1.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1640338680&t=511cd171a44a99e646ce6063b751c78f", mImage);
+
+        mPresenter.bindRecordServices();
+        btn_main_initrecord.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                mPresenter.startRecord("123", "1234567");
+            }
+        });
+
+        main_stoprecord.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                mPresenter.stopRecord();
+            }
+        });
+        main_playrecord.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                mPresenter.rePlayRecord();
+            }
+        });
+
+        mPresenter.initVideoList(mRecyclerView);
+
+    }
+
+    @Override
+    public void showLoading() {
+
+    }
+
+    @Override
+    public void hideLoading() {
+
+    }
+
+    @Override
+    public void onError(String errMessage) {
+
+    }
+
+    @Override
+    public Activity getActivity() {
+        return mActivity;
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (Jzvd.backPress()) {
+            return;
+        }
+        super.onBackPressed();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+//        Jzvd.releaseAllVideos();
+        JzvdStd.goOnPlayOnPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        JzvdStd.goOnPlayOnResume();
+    }
+
+}

+ 76 - 0
app/src/main/java/com/edufound/reader/activity/VideoActivity.java

@@ -0,0 +1,76 @@
+package com.edufound.reader.activity;
+
+import android.app.Activity;
+import android.widget.FrameLayout;
+
+import com.edufound.reader.R;
+import com.edufound.reader.annotation.BindView;
+import com.edufound.reader.base.BaseMvpActivity;
+import com.edufound.reader.contract.VideoContract;
+import com.edufound.reader.presenter.VideoPresenter;
+
+import tv.danmaku.ijk.media.player.IjkMediaPlayer;
+
+public class VideoActivity extends BaseMvpActivity<VideoPresenter> implements VideoContract.View {
+
+    Activity mActivity;
+
+    @BindView(id = R.id.player_frame)
+    FrameLayout mPlayerFrame;
+
+
+    @Override
+    public int getLayoutId() {
+        return R.layout.activity_video;
+    }
+
+    @Override
+    public void initView() {
+        mPresenter = new VideoPresenter();
+        mPresenter.attachView(this);
+        mActivity = this;
+        IjkMediaPlayer.loadLibrariesOnce(null);
+        IjkMediaPlayer.native_profileBegin("libijkplayer.so");
+        mPresenter.createVideoPlayer(mPlayerFrame);
+        mPresenter.addVideoController(mPlayerFrame);
+        mPresenter.initVideoPlayer(getIntent());
+    }
+
+    @Override
+    public void showLoading() {
+
+    }
+
+    @Override
+    public void hideLoading() {
+
+    }
+
+    @Override
+    public void onError(String errMessage) {
+
+    }
+
+    @Override
+    public Activity getActivity() {
+        return mActivity;
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mPresenter.activityDestroy();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mPresenter.activityPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mPresenter.activityResume();
+    }
+}

+ 75 - 0
app/src/main/java/com/edufound/reader/adapter/SlideVideoAdapter.java

@@ -0,0 +1,75 @@
+package com.edufound.reader.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.edufound.reader.R;
+import com.edufound.reader.bean.SlideVideoBean;
+import com.edufound.reader.cusview.RvListJzvdStd;
+import com.edufound.reader.videoutil.JZMediaIjk;
+import com.edufound.reader.util.GlideUtils;
+import com.orhanobut.logger.Logger;
+
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import cn.jzvd.JZDataSource;
+import cn.jzvd.JzvdStd;
+
+public class SlideVideoAdapter extends RecyclerView.Adapter<VideoHolder> {
+    private LayoutInflater mLayoutInflater;
+    private Context mContext;
+    private List<SlideVideoBean> mListData;
+
+    public SlideVideoAdapter(Context context, @NonNull List<SlideVideoBean> data) {
+        mContext = context;
+        mLayoutInflater = LayoutInflater.from(context);
+        this.mListData = data;
+    }
+
+
+    public void addMored(List<SlideVideoBean> listData) {
+        Logger.e("loadMore---" + listData.size());
+        mListData.addAll(listData);
+    }
+
+
+    @NonNull
+    @Override
+    public VideoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        return new VideoHolder(mLayoutInflater.inflate(R.layout.adapter_item_slidevideo, parent, false));
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull VideoHolder holder, int position) {
+        holder.mItemView.setTag(holder);
+        holder.mVideo.setUp(new JZDataSource(mListData.get(position).videoUrl), JzvdStd.SCREEN_FULLSCREEN, JZMediaIjk.class);
+        GlideUtils.loadImageSizeKipMemoryCache(mContext, "http://p.qpic.cn/videoyun/0/2449_43b6f696980311e59ed467f22794e792_1/640", holder.mVideo.posterImageView);
+        holder.mVideo.startPreloading();
+//        holder.mVideo.posterImageView.setImageURI(Uri.parse(mListData.get(position).videoUrl));
+
+    }
+
+    @Override
+    public int getItemCount() {
+        return mListData.size();
+    }
+
+
+}
+
+class VideoHolder extends RecyclerView.ViewHolder {
+    public View mItemView;
+    public RvListJzvdStd mVideo;
+
+    public VideoHolder(@NonNull View itemView) {
+        super(itemView);
+        mItemView = itemView;
+        mVideo = itemView.findViewById((R.id.slidevideo_video));
+    }
+}
+
+

+ 1 - 1
app/src/main/java/com/efunbox/reader/annotation/BindView.java

@@ -1,4 +1,4 @@
-package com.efunbox.reader.annotation;
+package com.edufound.reader.annotation;
 
 
 import java.lang.annotation.ElementType;

+ 19 - 11
app/src/main/java/com/efunbox/reader/application/EApplication.java

@@ -1,22 +1,21 @@
-package com.efunbox.reader.application;
+package com.edufound.reader.application;
 
 import android.app.Application;
-import android.content.Context;
 import android.os.Environment;
 
-
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.Priority;
-import com.bumptech.glide.load.engine.DiskCacheStrategy;
-import com.bumptech.glide.request.RequestOptions;
-import com.efunbox.reader.util.Consts;
-import com.efunbox.reader.util.HttpInterceptor;
+import com.edufound.reader.util.Consts;
+import com.edufound.reader.util.HttpInterceptor;
 import com.okhttplib.OkHttpUtil;
 import com.okhttplib.annotation.CacheType;
 import com.okhttplib.annotation.Encoding;
 import com.okhttplib.cookie.PersistentCookieJar;
 import com.okhttplib.cookie.cache.SetCookieCache;
 import com.okhttplib.cookie.persistence.SharedPrefsCookiePersistor;
+import com.orhanobut.logger.AndroidLogAdapter;
+import com.orhanobut.logger.FormatStrategy;
+import com.orhanobut.logger.Logger;
+import com.orhanobut.logger.PrettyFormatStrategy;
+import com.tencent.mmkv.MMKV;
 import com.umeng.commonsdk.UMConfigure;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -54,10 +53,19 @@ public class EApplication extends Application {
                     .addExceptionInterceptor(HttpInterceptor.ExceptionInterceptor)//请求链路异常拦截器
                     .setCookieJar(new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(getApplicationContext())))//持久化cookie
                     .build();
-
-
+            //初始化logger
+            FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
+                    .showThreadInfo(false)  // 是否显示线程信息,默认为ture
+                    .methodCount(3)         // 显示的方法行数,默认为2
+//                    .methodOffset(1)        // 隐藏内部方法调用到偏移量,默认为5
+//                    .logStrategy() // 更改要打印的日志策略。
+                    .tag(getPackageName())   // 每个日志的全局标记。默认PRETTY_LOGGER
+                    .build();
+            Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy));
             //初始化友盟
             UMConfigure.init(this, UMConfigure.DEVICE_TYPE_PHONE, "");
+            //初始化MMKV
+            MMKV.initialize(this);
         } catch (XmlPullParserException e) {
             e.printStackTrace();
         }

+ 20 - 5
app/src/main/java/com/efunbox/reader/base/BaseActivity.java

@@ -1,19 +1,21 @@
-package com.efunbox.reader.base;
+package com.edufound.reader.base;
 
 import android.app.Activity;
 import android.os.Build;
 import android.os.Bundle;
-import android.view.View;
+import android.util.DisplayMetrics;
 import android.view.WindowManager;
 
-import com.efunbox.reader.annotation.BindView;
-import com.efunbox.reader.util.LiuHaiScreenUtil;
+import com.edufound.reader.annotation.BindView;
+import com.edufound.reader.util.Consts;
+import com.edufound.reader.util.EfunboxUtil;
+import com.edufound.reader.util.LiuHaiScreenUtil;
+import com.edufound.reader.util.PermissionsUtil;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
-import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.app.AppCompatActivity;
 import io.reactivex.rxjava3.annotations.Nullable;
 
@@ -23,11 +25,24 @@ public abstract class BaseActivity extends AppCompatActivity {
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        EfunboxUtil.setCustomDensity(this, Consts.getmApplicAtion());
         setContentView(this.getLayoutId());
         getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
             LiuHaiScreenUtil.openFullScreenModel(this);
         }
+
+        //获取录音权限
+        PermissionsUtil.verifyAudioPermissions(this);
+        if (Consts.getScreenSize() == null) {
+            DisplayMetrics outMetrics = new DisplayMetrics();
+            getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
+            int widthPixels = outMetrics.widthPixels;
+            int heightPixels = outMetrics.heightPixels;
+            int arr[] = {widthPixels, heightPixels};
+            Consts.setScreenSize(arr);
+        }
+
         bindViews(this);
         initView();
     }

+ 1 - 1
app/src/main/java/com/efunbox/reader/base/BaseFragment.java

@@ -1,4 +1,4 @@
-package com.efunbox.reader.base;
+package com.edufound.reader.base;
 
 import android.os.Bundle;
 import android.view.LayoutInflater;

+ 1 - 1
app/src/main/java/com/efunbox/reader/base/BaseMvpActivity.java

@@ -1,4 +1,4 @@
-package com.efunbox.reader.base;
+package com.edufound.reader.base;
 
 import android.os.Bundle;
 

+ 1 - 1
app/src/main/java/com/efunbox/reader/base/BaseMvpFragment.java

@@ -1,4 +1,4 @@
-package com.efunbox.reader.base;
+package com.edufound.reader.base;
 
 import androidx.lifecycle.Lifecycle;
 import autodispose2.AutoDispose;

+ 1 - 1
app/src/main/java/com/efunbox/reader/base/BasePresenter.java

@@ -1,4 +1,4 @@
-package com.efunbox.reader.base;
+package com.edufound.reader.base;
 
 public class BasePresenter<V extends BaseView> {
     protected V mView;

+ 4 - 1
app/src/main/java/com/efunbox/reader/base/BaseView.java

@@ -1,4 +1,6 @@
-package com.efunbox.reader.base;
+package com.edufound.reader.base;
+
+import android.app.Activity;
 
 import autodispose2.AutoDisposeConverter;
 
@@ -29,4 +31,5 @@ public interface BaseView {
      */
     <T> AutoDisposeConverter<T> bindAutoDispose();
 
+    Activity getActivity();
 }

+ 6 - 0
app/src/main/java/com/edufound/reader/bean/ChivoxBean.java

@@ -0,0 +1,6 @@
+package com.edufound.reader.bean;
+
+public class ChivoxBean {
+    public static String appKey = "16075689600000da";
+    public static String secretKey = "caa8e60da6042731c230fe431ac9c7fd";
+}

+ 6 - 0
app/src/main/java/com/edufound/reader/bean/SlideVideoBean.java

@@ -0,0 +1,6 @@
+package com.edufound.reader.bean;
+
+public class SlideVideoBean {
+    public String videoUrl;
+    public String videoTitle;
+}

+ 30 - 0
app/src/main/java/com/edufound/reader/contract/MainContract.java

@@ -0,0 +1,30 @@
+package com.edufound.reader.contract;
+
+import com.edufound.reader.base.BaseView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+public interface MainContract {
+    interface Model {
+    }
+
+    interface View extends BaseView {
+    }
+
+    interface Presenter {
+        void initVideoList(RecyclerView rv);
+
+        void bindRecordServices();
+
+        void startRecord(String name, String refText);
+
+        void stopRecord();
+
+
+        void activityDestroy();
+
+        void activityPause();
+
+        void activityResume();
+    }
+}

+ 67 - 0
app/src/main/java/com/edufound/reader/contract/VideoContract.java

@@ -0,0 +1,67 @@
+package com.edufound.reader.contract;
+
+import android.content.Intent;
+import android.widget.FrameLayout;
+
+import com.edufound.reader.base.BaseView;
+
+import tv.danmaku.ijk.media.player.IMediaPlayer;
+
+public interface VideoContract {
+    interface Model {
+//        Observable<Object> login(String username, String password);
+    }
+
+    interface View extends BaseView {
+
+    }
+
+    interface Presenter {
+
+        /*
+         * 创建播放器及其方法
+         * */
+        void createVideoPlayer(FrameLayout frame);
+
+
+        /*
+         * 设置播放器播放路径等
+         * */
+        void initVideoPlayer(Intent intent);
+
+
+        /*
+         * 播放器播放完成
+         * */
+        void onVideoCompletion(IMediaPlayer mediaPlayer);
+
+        /*
+         * 播放器异常
+         * */
+        void onVideoError(IMediaPlayer mediaPlayer, int what, int extra);
+
+
+        /*
+         * 播放器缓冲完成
+         * */
+        void onVideoPrepared(IMediaPlayer mediaPlayer);
+
+
+        /*
+         * 播放器状态info
+         * */
+        void onVideoInfoListener(IMediaPlayer mediaPlayer, int what, int extra);
+
+
+        /*
+         * 设置播放器控制栏
+         * */
+        void addVideoController(FrameLayout frame);
+
+        void activityDestroy();
+
+        void activityPause();
+
+        void activityResume();
+    }
+}

+ 41 - 0
app/src/main/java/com/edufound/reader/cusview/FullScreenVideoController.java

@@ -0,0 +1,41 @@
+package com.edufound.reader.cusview;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import com.edufound.reader.R;
+
+public class FullScreenVideoController {
+    FrameLayout mController;
+    ImageView mControllerBack;
+    TextView mControllerVideoName;
+    ImageView mControllerLittleIcon;
+    SeekBar mControllerSeekBar;
+    TextView mControllerCurTime;
+    ImageView mControllerCenterStatus;
+    ImageView mControllerLoading;
+
+    public FullScreenVideoController(Context context) {
+        this.mController = (FrameLayout) LayoutInflater.from(context).inflate(R.layout.player_controller, null);
+        initControllView();
+    }
+
+    void initControllView() {
+        mControllerBack = mController.findViewById(R.id.player_controller_back);
+        mControllerVideoName = mController.findViewById(R.id.player_controller_videoname);
+        mControllerLittleIcon = mController.findViewById(R.id.player_controller_little_icon);
+        mControllerSeekBar = mController.findViewById(R.id.player_controller_seekbar);
+        mControllerSeekBar = mController.findViewById(R.id.player_controller_seekbar);
+        mControllerCurTime = mController.findViewById(R.id.player_controller_current);
+        mControllerCenterStatus = mController.findViewById(R.id.player_controller_status);
+        mControllerLoading = mController.findViewById(R.id.player_controller_loading);
+    }
+
+    public FrameLayout getController() {
+        return this.mController;
+    }
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1007 - 0
app/src/main/java/com/edufound/reader/cusview/RvListJzvdStd.java


+ 104 - 0
app/src/main/java/com/edufound/reader/ijkplayer/application/Settings.java

@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.edufound.reader.ijkplayer.application;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+
+import com.edufound.reader.R;
+
+
+public class Settings {
+    private Context mAppContext;
+    private SharedPreferences mSharedPreferences;
+
+    public static final int PV_PLAYER__AndroidMediaPlayer = 1;
+    public static final int PV_PLAYER__IjkMediaPlayer = 2;
+    public static final int PV_PLAYER__IjkExoMediaPlayer = 3;
+
+    public Settings(Context context) {
+        mAppContext = context.getApplicationContext();
+        mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mAppContext);
+    }
+
+    public boolean getEnableBackgroundPlay() {
+        String key = mAppContext.getString(R.string.pref_key_enable_background_play);
+        return mSharedPreferences.getBoolean(key, false);
+    }
+
+    public int getPlayer() {
+        String key = mAppContext.getString(R.string.pref_key_player);
+        String value = mSharedPreferences.getString(key, "");
+        try {
+            return Integer.valueOf(value).intValue();
+        } catch (NumberFormatException e) {
+            return 0;
+        }
+    }
+
+    public boolean getUsingMediaCodec() {
+        String key = mAppContext.getString(R.string.pref_key_using_media_codec);
+        return mSharedPreferences.getBoolean(key, false);
+    }
+
+    public boolean getUsingMediaCodecAutoRotate() {
+        String key = mAppContext.getString(R.string.pref_key_using_media_codec_auto_rotate);
+        return mSharedPreferences.getBoolean(key, false);
+    }
+
+    public boolean getMediaCodecHandleResolutionChange() {
+        String key = mAppContext.getString(R.string.pref_key_media_codec_handle_resolution_change);
+        return mSharedPreferences.getBoolean(key, false);
+    }
+
+    public boolean getUsingOpenSLES() {
+        String key = mAppContext.getString(R.string.pref_key_using_opensl_es);
+        return mSharedPreferences.getBoolean(key, false);
+    }
+
+    public String getPixelFormat() {
+        String key = mAppContext.getString(R.string.pref_key_pixel_format);
+        return mSharedPreferences.getString(key, "");
+    }
+
+    public boolean getEnableNoView() {
+        String key = mAppContext.getString(R.string.pref_key_enable_no_view);
+        return mSharedPreferences.getBoolean(key, false);
+    }
+
+    public boolean getEnableSurfaceView() {
+        String key = mAppContext.getString(R.string.pref_key_enable_surface_view);
+        return mSharedPreferences.getBoolean(key, false);
+    }
+
+    public boolean getEnableTextureView() {
+        String key = mAppContext.getString(R.string.pref_key_enable_texture_view);
+        return mSharedPreferences.getBoolean(key, false);
+    }
+
+    public boolean getEnableDetachedSurfaceTextureView() {
+        String key = mAppContext.getString(R.string.pref_key_enable_detached_surface_texture);
+        return mSharedPreferences.getBoolean(key, false);
+    }
+
+    public boolean getUsingMediaDataSource() {
+        String key = mAppContext.getString(R.string.pref_key_using_mediadatasource);
+        return mSharedPreferences.getBoolean(key, false);
+    }
+}

+ 57 - 0
app/src/main/java/com/edufound/reader/ijkplayer/media/FileMediaDataSource.java

@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.edufound.reader.ijkplayer.media;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import tv.danmaku.ijk.media.player.misc.IMediaDataSource;
+
+public class FileMediaDataSource implements IMediaDataSource {
+    private RandomAccessFile mFile;
+    private long mFileSize;
+
+    public FileMediaDataSource(File file) throws IOException {
+        mFile = new RandomAccessFile(file, "r");
+        mFileSize = mFile.length();
+    }
+
+    @Override
+    public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
+        if (mFile.getFilePointer() != position)
+            mFile.seek(position);
+
+        if (size == 0)
+            return 0;
+
+        return mFile.read(buffer, 0, size);
+    }
+
+    @Override
+    public long getSize() throws IOException {
+        return mFileSize;
+    }
+
+    @Override
+    public void close() throws IOException {
+        mFileSize = 0;
+        mFile.close();
+        mFile = null;
+    }
+}

+ 42 - 0
app/src/main/java/com/edufound/reader/ijkplayer/media/IMediaController.java

@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.edufound.reader.ijkplayer.media;
+
+import android.view.View;
+import android.widget.MediaController;
+
+public interface IMediaController {
+    void hide();
+
+    boolean isShowing();
+
+    void setAnchorView(View view);
+
+    void setEnabled(boolean enabled);
+
+    void setMediaPlayer(MediaController.MediaPlayerControl player);
+
+    void show(int timeout);
+
+    void show();
+
+    //----------
+    // Extends
+    //----------
+    void showOnce(View view);
+}

+ 87 - 0
app/src/main/java/com/edufound/reader/ijkplayer/media/IRenderView.java

@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.edufound.reader.ijkplayer.media;
+
+import android.graphics.SurfaceTexture;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.View;
+
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.annotations.Nullable;
+import tv.danmaku.ijk.media.player.IMediaPlayer;
+
+public interface IRenderView {
+    int AR_ASPECT_FIT_PARENT = 0; // without clip
+    int AR_ASPECT_FILL_PARENT = 1; // may clip
+    int AR_ASPECT_WRAP_CONTENT = 2;
+    int AR_MATCH_PARENT = 3;
+    int AR_16_9_FIT_PARENT = 4;
+    int AR_4_3_FIT_PARENT = 5;
+
+    View getView();
+
+    boolean shouldWaitForResize();
+
+    void setVideoSize(int videoWidth, int videoHeight);
+
+    void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen);
+
+    void setVideoRotation(int degree);
+
+    void setAspectRatio(int aspectRatio);
+
+    void addRenderCallback(@NonNull IRenderCallback callback);
+
+    void removeRenderCallback(@NonNull IRenderCallback callback);
+
+    interface ISurfaceHolder {
+        void bindToMediaPlayer(IMediaPlayer mp);
+
+        @NonNull
+        IRenderView getRenderView();
+
+        @Nullable
+        SurfaceHolder getSurfaceHolder();
+
+        @Nullable
+        Surface openSurface();
+
+        @Nullable
+        SurfaceTexture getSurfaceTexture();
+    }
+
+    interface IRenderCallback {
+        /**
+         * @param holder
+         * @param width  could be 0
+         * @param height could be 0
+         */
+        void onSurfaceCreated(@NonNull ISurfaceHolder holder, int width, int height);
+
+        /**
+         * @param holder
+         * @param format could be 0
+         * @param width
+         * @param height
+         */
+        void onSurfaceChanged(@NonNull ISurfaceHolder holder, int format, int width, int height);
+
+        void onSurfaceDestroyed(@NonNull ISurfaceHolder holder);
+    }
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1150 - 0
app/src/main/java/com/edufound/reader/ijkplayer/media/IjkVideoView.java


+ 215 - 0
app/src/main/java/com/edufound/reader/ijkplayer/media/MeasureHelper.java

@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.edufound.reader.ijkplayer.media;
+
+import android.view.View;
+
+import java.lang.ref.WeakReference;
+
+public final class MeasureHelper {
+    private WeakReference<View> mWeakView;
+
+    private int mVideoWidth;
+    private int mVideoHeight;
+    private int mVideoSarNum;
+    private int mVideoSarDen;
+
+    private int mVideoRotationDegree;
+
+    private int mMeasuredWidth;
+    private int mMeasuredHeight;
+
+    private int mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT;
+
+    public MeasureHelper(View view) {
+        mWeakView = new WeakReference<View>(view);
+    }
+
+    public View getView() {
+        if (mWeakView == null)
+            return null;
+        return mWeakView.get();
+    }
+
+    public void setVideoSize(int videoWidth, int videoHeight) {
+        mVideoWidth = videoWidth;
+        mVideoHeight = videoHeight;
+    }
+
+    public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
+        mVideoSarNum = videoSarNum;
+        mVideoSarDen = videoSarDen;
+    }
+
+    public void setVideoRotation(int videoRotationDegree) {
+        mVideoRotationDegree = videoRotationDegree;
+    }
+
+    /**
+     * Must be called by View.onMeasure(int, int)
+     *
+     * @param widthMeasureSpec
+     * @param heightMeasureSpec
+     */
+    public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
+        //        + MeasureSpec.toString(heightMeasureSpec) + ")");
+        if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
+            int tempSpec = widthMeasureSpec;
+            widthMeasureSpec  = heightMeasureSpec;
+            heightMeasureSpec = tempSpec;
+        }
+
+        int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec);
+        int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec);
+        if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) {
+            width = widthMeasureSpec;
+            height = heightMeasureSpec;
+        } else if (mVideoWidth > 0 && mVideoHeight > 0) {
+            int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec);
+            int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec);
+            int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec);
+            int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec);
+
+            if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) {
+                float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize;
+                float displayAspectRatio;
+                switch (mCurrentAspectRatio) {
+                    case IRenderView.AR_16_9_FIT_PARENT:
+                        displayAspectRatio = 16.0f / 9.0f;
+                        if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
+                            displayAspectRatio = 1.0f / displayAspectRatio;
+                        break;
+                    case IRenderView.AR_4_3_FIT_PARENT:
+                        displayAspectRatio = 4.0f / 3.0f;
+                        if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
+                            displayAspectRatio = 1.0f / displayAspectRatio;
+                        break;
+                    case IRenderView.AR_ASPECT_FIT_PARENT:
+                    case IRenderView.AR_ASPECT_FILL_PARENT:
+                    case IRenderView.AR_ASPECT_WRAP_CONTENT:
+                    default:
+                        displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight;
+                        if (mVideoSarNum > 0 && mVideoSarDen > 0)
+                            displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen;
+                        break;
+                }
+                boolean shouldBeWider = displayAspectRatio > specAspectRatio;
+
+                switch (mCurrentAspectRatio) {
+                    case IRenderView.AR_ASPECT_FIT_PARENT:
+                    case IRenderView.AR_16_9_FIT_PARENT:
+                    case IRenderView.AR_4_3_FIT_PARENT:
+                        if (shouldBeWider) {
+                            // too wide, fix width
+                            width = widthSpecSize;
+                            height = (int) (width / displayAspectRatio);
+                        } else {
+                            // too high, fix height
+                            height = heightSpecSize;
+                            width = (int) (height * displayAspectRatio);
+                        }
+                        break;
+                    case IRenderView.AR_ASPECT_FILL_PARENT:
+                        if (shouldBeWider) {
+                            // not high enough, fix height
+                            height = heightSpecSize;
+                            width = (int) (height * displayAspectRatio);
+                        } else {
+                            // not wide enough, fix width
+                            width = widthSpecSize;
+                            height = (int) (width / displayAspectRatio);
+                        }
+                        break;
+                    case IRenderView.AR_ASPECT_WRAP_CONTENT:
+                    default:
+                        if (shouldBeWider) {
+                            // too wide, fix width
+                            width = Math.min(mVideoWidth, widthSpecSize);
+                            height = (int) (width / displayAspectRatio);
+                        } else {
+                            // too high, fix height
+                            height = Math.min(mVideoHeight, heightSpecSize);
+                            width = (int) (height * displayAspectRatio);
+                        }
+                        break;
+                }
+            } else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) {
+                // the size is fixed
+                width = widthSpecSize;
+                height = heightSpecSize;
+
+                // for compatibility, we adjust size based on aspect ratio
+                if (mVideoWidth * height < width * mVideoHeight) {
+                    //Log.i("@@@", "image too wide, correcting");
+                    width = height * mVideoWidth / mVideoHeight;
+                } else if (mVideoWidth * height > width * mVideoHeight) {
+                    //Log.i("@@@", "image too tall, correcting");
+                    height = width * mVideoHeight / mVideoWidth;
+                }
+            } else if (widthSpecMode == View.MeasureSpec.EXACTLY) {
+                // only the width is fixed, adjust the height to match aspect ratio if possible
+                width = widthSpecSize;
+                height = width * mVideoHeight / mVideoWidth;
+                if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
+                    // couldn't match aspect ratio within the constraints
+                    height = heightSpecSize;
+                }
+            } else if (heightSpecMode == View.MeasureSpec.EXACTLY) {
+                // only the height is fixed, adjust the width to match aspect ratio if possible
+                height = heightSpecSize;
+                width = height * mVideoWidth / mVideoHeight;
+                if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
+                    // couldn't match aspect ratio within the constraints
+                    width = widthSpecSize;
+                }
+            } else {
+                // neither the width nor the height are fixed, try to use actual video size
+                width = mVideoWidth;
+                height = mVideoHeight;
+                if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
+                    // too tall, decrease both width and height
+                    height = heightSpecSize;
+                    width = height * mVideoWidth / mVideoHeight;
+                }
+                if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
+                    // too wide, decrease both width and height
+                    width = widthSpecSize;
+                    height = width * mVideoHeight / mVideoWidth;
+                }
+            }
+        } else {
+            // no size yet, just adopt the given spec sizes
+        }
+
+        mMeasuredWidth = width;
+        mMeasuredHeight = height;
+    }
+
+    public int getMeasuredWidth() {
+        return mMeasuredWidth;
+    }
+
+    public int getMeasuredHeight() {
+        return mMeasuredHeight;
+    }
+
+    public void setAspectRatio(int aspectRatio) {
+        mCurrentAspectRatio = aspectRatio;
+    }
+}

+ 288 - 0
app/src/main/java/com/edufound/reader/ijkplayer/media/SurfaceRenderView.java

@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.edufound.reader.ijkplayer.media;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import java.lang.ref.WeakReference;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.annotations.Nullable;
+import tv.danmaku.ijk.media.player.IMediaPlayer;
+import tv.danmaku.ijk.media.player.ISurfaceTextureHolder;
+
+public class SurfaceRenderView extends SurfaceView implements IRenderView {
+    private MeasureHelper mMeasureHelper;
+
+    public SurfaceRenderView(Context context) {
+        super(context);
+        initView(context);
+    }
+
+    public SurfaceRenderView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initView(context);
+    }
+
+    public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initView(context);
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        initView(context);
+    }
+
+    private void initView(Context context) {
+        mMeasureHelper = new MeasureHelper(this);
+        mSurfaceCallback = new SurfaceCallback(this);
+        getHolder().addCallback(mSurfaceCallback);
+        //noinspection deprecation
+        getHolder().setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
+    }
+
+    @Override
+    public View getView() {
+        return this;
+    }
+
+    @Override
+    public boolean shouldWaitForResize() {
+        return true;
+    }
+
+    //--------------------
+    // Layout & Measure
+    //--------------------
+    @Override
+    public void setVideoSize(int videoWidth, int videoHeight) {
+        if (videoWidth > 0 && videoHeight > 0) {
+            mMeasureHelper.setVideoSize(videoWidth, videoHeight);
+            getHolder().setFixedSize(videoWidth, videoHeight);
+            requestLayout();
+        }
+    }
+
+    @Override
+    public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
+        if (videoSarNum > 0 && videoSarDen > 0) {
+            mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen);
+            requestLayout();
+        }
+    }
+
+    @Override
+    public void setVideoRotation(int degree) {
+        Log.e("", "SurfaceView doesn't support rotation (" + degree + ")!\n");
+    }
+
+    @Override
+    public void setAspectRatio(int aspectRatio) {
+        mMeasureHelper.setAspectRatio(aspectRatio);
+        requestLayout();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec);
+        setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight());
+    }
+
+    //--------------------
+    // SurfaceViewHolder
+    //--------------------
+
+    private static final class InternalSurfaceHolder implements ISurfaceHolder {
+        private SurfaceRenderView mSurfaceView;
+        private SurfaceHolder mSurfaceHolder;
+
+        public InternalSurfaceHolder(@NonNull SurfaceRenderView surfaceView,
+                                     @Nullable SurfaceHolder surfaceHolder) {
+            mSurfaceView = surfaceView;
+            mSurfaceHolder = surfaceHolder;
+        }
+
+        public void bindToMediaPlayer(IMediaPlayer mp) {
+            if (mp != null) {
+                if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) &&
+                        (mp instanceof ISurfaceTextureHolder)) {
+                    ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;
+                    textureHolder.setSurfaceTexture(null);
+                }
+                mp.setDisplay(mSurfaceHolder);
+            }
+        }
+
+        @NonNull
+        @Override
+        public IRenderView getRenderView() {
+            return mSurfaceView;
+        }
+
+        @Nullable
+        @Override
+        public SurfaceHolder getSurfaceHolder() {
+            return mSurfaceHolder;
+        }
+
+        @Nullable
+        @Override
+        public SurfaceTexture getSurfaceTexture() {
+            return null;
+        }
+
+        @Nullable
+        @Override
+        public Surface openSurface() {
+            if (mSurfaceHolder == null)
+                return null;
+            return mSurfaceHolder.getSurface();
+        }
+    }
+
+    //-------------------------
+    // SurfaceHolder.Callback
+    //-------------------------
+
+    @Override
+    public void addRenderCallback(IRenderCallback callback) {
+        mSurfaceCallback.addRenderCallback(callback);
+    }
+
+    @Override
+    public void removeRenderCallback(IRenderCallback callback) {
+        mSurfaceCallback.removeRenderCallback(callback);
+    }
+
+    private SurfaceCallback mSurfaceCallback;
+
+    private static final class SurfaceCallback implements SurfaceHolder.Callback {
+        private SurfaceHolder mSurfaceHolder;
+        private boolean mIsFormatChanged;
+        private int mFormat;
+        private int mWidth;
+        private int mHeight;
+
+        private WeakReference<SurfaceRenderView> mWeakSurfaceView;
+        private Map<IRenderCallback, Object> mRenderCallbackMap = new ConcurrentHashMap<IRenderCallback, Object>();
+
+        public SurfaceCallback(@NonNull SurfaceRenderView surfaceView) {
+            mWeakSurfaceView = new WeakReference<SurfaceRenderView>(surfaceView);
+        }
+
+        public void addRenderCallback(@NonNull IRenderCallback callback) {
+            mRenderCallbackMap.put(callback, callback);
+
+            ISurfaceHolder surfaceHolder = null;
+            if (mSurfaceHolder != null) {
+                if (surfaceHolder == null)
+                    surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
+                callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);
+            }
+
+            if (mIsFormatChanged) {
+                if (surfaceHolder == null)
+                    surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
+                callback.onSurfaceChanged(surfaceHolder, mFormat, mWidth, mHeight);
+            }
+        }
+
+        public void removeRenderCallback(@NonNull IRenderCallback callback) {
+            mRenderCallbackMap.remove(callback);
+        }
+
+        @Override
+        public void surfaceCreated(SurfaceHolder holder) {
+            mSurfaceHolder = holder;
+            mIsFormatChanged = false;
+            mFormat = 0;
+            mWidth = 0;
+            mHeight = 0;
+
+            ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
+            for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
+                renderCallback.onSurfaceCreated(surfaceHolder, 0, 0);
+            }
+        }
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder holder) {
+            mSurfaceHolder = null;
+            mIsFormatChanged = false;
+            mFormat = 0;
+            mWidth = 0;
+            mHeight = 0;
+
+            ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
+            for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
+                renderCallback.onSurfaceDestroyed(surfaceHolder);
+            }
+        }
+
+        @Override
+        public void surfaceChanged(SurfaceHolder holder, int format,
+                                   int width, int height) {
+            mSurfaceHolder = holder;
+            mIsFormatChanged = true;
+            mFormat = format;
+            mWidth = width;
+            mHeight = height;
+
+            // mMeasureHelper.setVideoSize(width, height);
+
+            ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
+            for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
+                renderCallback.onSurfaceChanged(surfaceHolder, format, width, height);
+            }
+        }
+    }
+
+    //--------------------
+    // Accessibility
+    //--------------------
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setClassName(SurfaceRenderView.class.getName());
+    }
+
+    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            info.setClassName(SurfaceRenderView.class.getName());
+        }
+    }
+}

+ 370 - 0
app/src/main/java/com/edufound/reader/ijkplayer/media/TextureRenderView.java

@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.edufound.reader.ijkplayer.media;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.TextureView;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import java.lang.ref.WeakReference;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.annotations.Nullable;
+import tv.danmaku.ijk.media.player.IMediaPlayer;
+import tv.danmaku.ijk.media.player.ISurfaceTextureHolder;
+import tv.danmaku.ijk.media.player.ISurfaceTextureHost;
+
+@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+public class TextureRenderView extends TextureView implements IRenderView {
+    private static final String TAG = "TextureRenderView";
+    private MeasureHelper mMeasureHelper;
+
+    public TextureRenderView(Context context) {
+        super(context);
+        initView(context);
+    }
+
+    public TextureRenderView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initView(context);
+    }
+
+    public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initView(context);
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        initView(context);
+    }
+
+    private void initView(Context context) {
+        mMeasureHelper = new MeasureHelper(this);
+        mSurfaceCallback = new SurfaceCallback(this);
+        setSurfaceTextureListener(mSurfaceCallback);
+    }
+
+    @Override
+    public View getView() {
+        return this;
+    }
+
+    @Override
+    public boolean shouldWaitForResize() {
+        return false;
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        mSurfaceCallback.willDetachFromWindow();
+        super.onDetachedFromWindow();
+        mSurfaceCallback.didDetachFromWindow();
+    }
+
+    //--------------------
+    // Layout & Measure
+    //--------------------
+    @Override
+    public void setVideoSize(int videoWidth, int videoHeight) {
+        if (videoWidth > 0 && videoHeight > 0) {
+            mMeasureHelper.setVideoSize(videoWidth, videoHeight);
+            requestLayout();
+        }
+    }
+
+    @Override
+    public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
+        if (videoSarNum > 0 && videoSarDen > 0) {
+            mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen);
+            requestLayout();
+        }
+    }
+
+    @Override
+    public void setVideoRotation(int degree) {
+        mMeasureHelper.setVideoRotation(degree);
+        setRotation(degree);
+    }
+
+    @Override
+    public void setAspectRatio(int aspectRatio) {
+        mMeasureHelper.setAspectRatio(aspectRatio);
+        requestLayout();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec);
+        setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight());
+    }
+
+    //--------------------
+    // TextureViewHolder
+    //--------------------
+
+    public ISurfaceHolder getSurfaceHolder() {
+        return new InternalSurfaceHolder(this, mSurfaceCallback.mSurfaceTexture, mSurfaceCallback);
+    }
+
+    private static final class InternalSurfaceHolder implements ISurfaceHolder {
+        private TextureRenderView mTextureView;
+        private SurfaceTexture mSurfaceTexture;
+        private ISurfaceTextureHost mSurfaceTextureHost;
+
+        public InternalSurfaceHolder(@NonNull TextureRenderView textureView,
+                                     @Nullable SurfaceTexture surfaceTexture,
+                                     @NonNull ISurfaceTextureHost surfaceTextureHost) {
+            mTextureView = textureView;
+            mSurfaceTexture = surfaceTexture;
+            mSurfaceTextureHost = surfaceTextureHost;
+        }
+
+        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+        public void bindToMediaPlayer(IMediaPlayer mp) {
+            if (mp == null)
+                return;
+
+            if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) &&
+                    (mp instanceof ISurfaceTextureHolder)) {
+                ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;
+                mTextureView.mSurfaceCallback.setOwnSurfaceTexture(false);
+
+                SurfaceTexture surfaceTexture = textureHolder.getSurfaceTexture();
+                if (surfaceTexture != null) {
+                    mTextureView.setSurfaceTexture(surfaceTexture);
+                } else {
+                    textureHolder.setSurfaceTexture(mSurfaceTexture);
+                    textureHolder.setSurfaceTextureHost(mTextureView.mSurfaceCallback);
+                }
+            } else {
+                mp.setSurface(openSurface());
+            }
+        }
+
+        @NonNull
+        @Override
+        public IRenderView getRenderView() {
+            return mTextureView;
+        }
+
+        @Nullable
+        @Override
+        public SurfaceHolder getSurfaceHolder() {
+            return null;
+        }
+
+        @Nullable
+        @Override
+        public SurfaceTexture getSurfaceTexture() {
+            return mSurfaceTexture;
+        }
+
+        @Nullable
+        @Override
+        public Surface openSurface() {
+            if (mSurfaceTexture == null)
+                return null;
+            return new Surface(mSurfaceTexture);
+        }
+    }
+
+    //-------------------------
+    // SurfaceHolder.Callback
+    //-------------------------
+
+    @Override
+    public void addRenderCallback(IRenderCallback callback) {
+        mSurfaceCallback.addRenderCallback(callback);
+    }
+
+    @Override
+    public void removeRenderCallback(IRenderCallback callback) {
+        mSurfaceCallback.removeRenderCallback(callback);
+    }
+
+    private SurfaceCallback mSurfaceCallback;
+
+    private static final class SurfaceCallback implements SurfaceTextureListener, ISurfaceTextureHost {
+        private SurfaceTexture mSurfaceTexture;
+        private boolean mIsFormatChanged;
+        private int mWidth;
+        private int mHeight;
+
+        private boolean mOwnSurfaceTexture = true;
+        private boolean mWillDetachFromWindow = false;
+        private boolean mDidDetachFromWindow = false;
+
+        private WeakReference<TextureRenderView> mWeakRenderView;
+        private Map<IRenderCallback, Object> mRenderCallbackMap = new ConcurrentHashMap<IRenderCallback, Object>();
+
+        public SurfaceCallback(@NonNull TextureRenderView renderView) {
+            mWeakRenderView = new WeakReference<TextureRenderView>(renderView);
+        }
+
+        public void setOwnSurfaceTexture(boolean ownSurfaceTexture) {
+            mOwnSurfaceTexture = ownSurfaceTexture;
+        }
+
+        public void addRenderCallback(@NonNull IRenderCallback callback) {
+            mRenderCallbackMap.put(callback, callback);
+
+            ISurfaceHolder surfaceHolder = null;
+            if (mSurfaceTexture != null) {
+                if (surfaceHolder == null)
+                    surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this);
+                callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);
+            }
+
+            if (mIsFormatChanged) {
+                if (surfaceHolder == null)
+                    surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this);
+                callback.onSurfaceChanged(surfaceHolder, 0, mWidth, mHeight);
+            }
+        }
+
+        public void removeRenderCallback(@NonNull IRenderCallback callback) {
+            mRenderCallbackMap.remove(callback);
+        }
+
+        @Override
+        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+            mSurfaceTexture = surface;
+            mIsFormatChanged = false;
+            mWidth = 0;
+            mHeight = 0;
+
+            ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
+            for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
+                renderCallback.onSurfaceCreated(surfaceHolder, 0, 0);
+            }
+        }
+
+        @Override
+        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+            mSurfaceTexture = surface;
+            mIsFormatChanged = true;
+            mWidth = width;
+            mHeight = height;
+
+            ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
+            for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
+                renderCallback.onSurfaceChanged(surfaceHolder, 0, width, height);
+            }
+        }
+
+        @Override
+        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+            mSurfaceTexture = surface;
+            mIsFormatChanged = false;
+            mWidth = 0;
+            mHeight = 0;
+
+            ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
+            for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
+                renderCallback.onSurfaceDestroyed(surfaceHolder);
+            }
+
+            Log.d(TAG, "onSurfaceTextureDestroyed: destroy: " + mOwnSurfaceTexture);
+            return mOwnSurfaceTexture;
+        }
+
+        @Override
+        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+        }
+
+        //-------------------------
+        // ISurfaceTextureHost
+        //-------------------------
+
+        @Override
+        public void releaseSurfaceTexture(SurfaceTexture surfaceTexture) {
+            if (surfaceTexture == null) {
+                Log.d(TAG, "releaseSurfaceTexture: null");
+            } else if (mDidDetachFromWindow) {
+                if (surfaceTexture != mSurfaceTexture) {
+                    Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release different SurfaceTexture");
+                    surfaceTexture.release();
+                } else if (!mOwnSurfaceTexture) {
+                    Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release detached SurfaceTexture");
+                    surfaceTexture.release();
+                } else {
+                    Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): already released by TextureView");
+                }
+            } else if (mWillDetachFromWindow) {
+                if (surfaceTexture != mSurfaceTexture) {
+                    Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): release different SurfaceTexture");
+                    surfaceTexture.release();
+                } else if (!mOwnSurfaceTexture) {
+                    Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): re-attach SurfaceTexture to TextureView");
+                    setOwnSurfaceTexture(true);
+                } else {
+                    Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): will released by TextureView");
+                }
+            } else {
+                if (surfaceTexture != mSurfaceTexture) {
+                    Log.d(TAG, "releaseSurfaceTexture: alive: release different SurfaceTexture");
+                    surfaceTexture.release();
+                } else if (!mOwnSurfaceTexture) {
+                    Log.d(TAG, "releaseSurfaceTexture: alive: re-attach SurfaceTexture to TextureView");
+                    setOwnSurfaceTexture(true);
+                } else {
+                    Log.d(TAG, "releaseSurfaceTexture: alive: will released by TextureView");
+                }
+            }
+        }
+
+        public void willDetachFromWindow() {
+            Log.d(TAG, "willDetachFromWindow()");
+            mWillDetachFromWindow = true;
+        }
+
+        public void didDetachFromWindow() {
+            Log.d(TAG, "didDetachFromWindow()");
+            mDidDetachFromWindow = true;
+        }
+    }
+
+    //--------------------
+    // Accessibility
+    //--------------------
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setClassName(TextureRenderView.class.getName());
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setClassName(TextureRenderView.class.getName());
+    }
+}

+ 63 - 0
app/src/main/java/com/edufound/reader/ijkplayer/services/MediaPlayerService.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.edufound.reader.ijkplayer.services;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+
+import io.reactivex.rxjava3.annotations.Nullable;
+import tv.danmaku.ijk.media.player.IMediaPlayer;
+
+public class MediaPlayerService extends Service {
+    private static IMediaPlayer sMediaPlayer;
+
+    public static Intent newIntent(Context context) {
+        Intent intent = new Intent(context, MediaPlayerService.class);
+        return intent;
+    }
+
+    public static void intentToStart(Context context) {
+        context.startService(newIntent(context));
+    }
+
+    public static void intentToStop(Context context) {
+        context.stopService(newIntent(context));
+    }
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    public static void setMediaPlayer(IMediaPlayer mp) {
+        if (sMediaPlayer != null && sMediaPlayer != mp) {
+            if (sMediaPlayer.isPlaying())
+                sMediaPlayer.stop();
+            sMediaPlayer.release();
+            sMediaPlayer = null;
+        }
+        sMediaPlayer = mp;
+    }
+
+    public static IMediaPlayer getMediaPlayer() {
+        return sMediaPlayer;
+    }
+}

+ 6 - 0
app/src/main/java/com/edufound/reader/model/MainModel.java

@@ -0,0 +1,6 @@
+package com.edufound.reader.model;
+
+import com.edufound.reader.contract.MainContract;
+
+public class MainModel implements MainContract.Model {
+}

+ 12 - 0
app/src/main/java/com/edufound/reader/model/VideoModel.java

@@ -0,0 +1,12 @@
+package com.edufound.reader.model;
+
+import com.edufound.reader.contract.VideoContract;
+
+public class VideoModel implements VideoContract.Model {
+
+
+//    @Override
+//    public Observable<Object> login(String username, String password) {
+//        return null;
+//    }
+}

+ 49 - 0
app/src/main/java/com/edufound/reader/net/RxScheduler.java

@@ -0,0 +1,49 @@
+package com.edufound.reader.net;
+
+
+import org.reactivestreams.Publisher;
+
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.core.Flowable;
+import io.reactivex.rxjava3.core.FlowableTransformer;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.ObservableSource;
+import io.reactivex.rxjava3.core.ObservableTransformer;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+
+public class RxScheduler {
+
+    /**
+     * 统一线程处理
+     *
+     * @param <T> 指定的泛型类型
+     * @return FlowableTransformer
+     */
+    public static <T> FlowableTransformer<T, T> Flo_io_main() {
+        return new FlowableTransformer<T, T>() {
+            @Override
+            public Publisher<T> apply(Flowable<T> upstream) {
+                return upstream.subscribeOn(Schedulers.io())
+                        .observeOn(AndroidSchedulers.mainThread());
+            }
+        };
+    }
+
+    /**
+     * 统一线程处理
+     *
+     * @param <T> 指定的泛型类型
+     * @return ObservableTransformer
+     */
+    public static <T> ObservableTransformer<T, T> Obs_io_main() {
+        return new ObservableTransformer<T, T>() {
+            @Override
+            public ObservableSource<T> apply(Observable<T> upstream) {
+                return upstream.subscribeOn(Schedulers.io())
+                        .observeOn(AndroidSchedulers.mainThread());
+            }
+        };
+    }
+
+}

+ 232 - 0
app/src/main/java/com/edufound/reader/presenter/MainPresenter.java

@@ -0,0 +1,232 @@
+package com.edufound.reader.presenter;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.chivox.aiengine.EvalResult;
+import com.edufound.reader.R;
+import com.edufound.reader.adapter.SlideVideoAdapter;
+import com.edufound.reader.base.BasePresenter;
+import com.edufound.reader.bean.SlideVideoBean;
+import com.edufound.reader.contract.MainContract;
+import com.edufound.reader.cusview.RvListJzvdStd;
+import com.edufound.reader.model.MainModel;
+import com.edufound.reader.services.RecordServices;
+import com.edufound.reader.util.CToast;
+import com.edufound.reader.videoutil.OnViewPagerListener;
+import com.edufound.reader.videoutil.ViewPagerLayoutManager;
+import com.orhanobut.logger.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.OrientationHelper;
+import androidx.recyclerview.widget.RecyclerView;
+import cn.jzvd.Jzvd;
+
+public class MainPresenter extends BasePresenter<MainContract.View> implements MainContract.Presenter {
+    MainModel mModel;
+    private ViewPagerLayoutManager mViewPagerLayoutManager;
+    private int mCurrentPosition = -1;
+
+
+    /*
+     * 测试数据
+     * */
+    List<SlideVideoBean> mDataList;
+    SlideVideoAdapter mAdapter;
+
+    public MainPresenter() {
+        mModel = new MainModel();
+    }
+
+    @Override
+    public void initVideoList(RecyclerView rv) {
+
+        /*
+         * 测试数据
+         * */
+        mDataList = new ArrayList<>();
+        for (int i = 0; i < 5; i++) {
+            SlideVideoBean bean = new SlideVideoBean();
+            if (i % 2 == 0) {
+                bean.videoUrl = "http://reader-wx.ai160.com/vs2m-m3u8/reader/053/05303004/05303004038/05303004038.m3u8";
+                bean.videoTitle = "m3u8视频";
+            } else {
+                bean.videoUrl = "https://reader-wx.ai160.com/reader/resource/video/1610961140479648.mp4";
+                bean.videoTitle = "https--MP4视频";
+            }
+            mDataList.add(bean);
+        }
+
+
+        rv.setLayoutManager(new LinearLayoutManager(mView.getActivity()));
+        rv.setNestedScrollingEnabled(false);
+        mViewPagerLayoutManager = new ViewPagerLayoutManager(mView.getActivity(), OrientationHelper.VERTICAL);
+        rv.setLayoutManager(mViewPagerLayoutManager);
+        mAdapter = new SlideVideoAdapter(mView.getActivity(), mDataList);
+        rv.setAdapter(mAdapter);
+        mViewPagerLayoutManager.setOnViewPagerListener(new OnViewPagerListener() {
+            @Override
+            public void onInitComplete() {
+                //自动播放第一条
+                autoPlayVideo(rv);
+            }
+
+            @Override
+            public void onPageRelease(boolean isNext, int position) {
+                if (mCurrentPosition == position) {
+                    Jzvd.releaseAllVideos();
+                }
+            }
+
+            @Override
+            public void onPageSelected(int position, boolean isBottom) {
+//                if (mCurrentPosition == position) {
+//                    Logger.e("mCurrentPosition == position..return了");
+//                    return;
+//                }
+                autoPlayVideo(rv);
+                mCurrentPosition = position;
+            }
+        });
+        rv.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
+            @Override
+            public void onChildViewAttachedToWindow(View view) {
+                scrollToNextFunction(rv, false);
+            }
+
+            @Override
+            public void onChildViewDetachedFromWindow(View view) {
+                Jzvd jzvd = view.findViewById(R.id.slidevideo_video);
+                if (jzvd != null && Jzvd.CURRENT_JZVD != null && jzvd.jzDataSource != null &&
+                        jzvd.jzDataSource.containsTheUrl(Jzvd.CURRENT_JZVD.jzDataSource.getCurrentUrl())) {
+                    if (Jzvd.CURRENT_JZVD != null && Jzvd.CURRENT_JZVD.screen != Jzvd.SCREEN_FULLSCREEN) {
+                        Jzvd.releaseAllVideos();
+                    }
+                }
+            }
+        });
+    }
+
+    private void autoPlayVideo(RecyclerView rv) {
+        if (rv == null || rv.getChildAt(0) == null) {
+            return;
+        }
+        RvListJzvdStd player = rv.getChildAt(0).findViewById(R.id.slidevideo_video);
+        if (player != null) {
+            if (player.state == Jzvd.STATE_PLAYING) {
+                //正在播放
+            } else {
+                player.startVideoAfterPreloading();
+                player.setVideoCallBack(new RvListJzvdStd.VideoCallBack() {
+                    @Override
+                    public void onComplete() {
+                        scrollToNextFunction(rv, true);
+                    }
+                });
+            }
+
+        }
+    }
+
+    private void scrollToNextFunction(RecyclerView rv, boolean needScroll) {
+        //获取当前显示的View 的数据
+        int childCount = rv.getChildCount();
+        //获取最后一具 Item 对应的View
+        View childAt = rv.getChildAt(childCount - 1);
+        //获取当前军舰中显示的最后一个 Item 的位置  Postion
+        int childLayoutPosition = rv.getChildLayoutPosition(childAt);
+        //如果不是最后一个就向下滑动
+
+        if (childLayoutPosition < rv.getAdapter().getItemCount() - 2) {
+            if (needScroll) {
+                rv.smoothScrollToPosition(childLayoutPosition + 1);
+            }
+        } else {
+//            Toast.makeText(this, "已滑动到底部了", Toast.LENGTH_LONG).show();
+//            CToast.makeText(mView.getActivity(), "滑动到最后一个啦", 3000).show();
+            Logger.e("倒数第二个,添加数据");
+            mAdapter.addMored(mDataList);
+
+        }
+    }
+
+    RecordServices mRecordServices;
+
+    @Override
+    public void bindRecordServices() {
+        if (mRecordServices == null) {
+            Intent intent = new Intent();
+            intent.setClass(mView.getActivity(), RecordServices.class);
+            mView.getActivity().bindService(intent, recordServicesConn, Context.BIND_AUTO_CREATE);
+        }
+    }
+
+    @Override
+    public void startRecord(String name, String refText) {
+        mRecordServices.startRecord(mView.getActivity(), name, refText);
+    }
+
+    @Override
+    public void stopRecord() {
+        mRecordServices.stopRecord();
+    }
+
+
+    public void rePlayRecord() {
+        mRecordServices.replayRecord();
+    }
+
+    @Override
+    public void activityDestroy() {
+        mRecordServices.unbindService(recordServicesConn);
+    }
+
+    @Override
+    public void activityPause() {
+
+    }
+
+    @Override
+    public void activityResume() {
+
+    }
+
+
+    private ServiceConnection recordServicesConn = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+            mRecordServices = ((RecordServices.RecordServicesBind) iBinder).getService();
+            mRecordServices.setRecordCallBack(new RecordServices.RecordCallBack() {
+                @Override
+                public void onRecordStart() {
+                    Logger.e("录音开始");
+                }
+
+                @Override
+                public void onRecordStop(int type, String tokenId, EvalResult result) {
+                    Logger.e("录音停止");
+                }
+
+                @Override
+                public void onRecordCancel() {
+                    Logger.e("录音取消");
+                }
+            });
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName componentName) {
+            mRecordServices = null;
+        }
+    };
+
+
+}

+ 160 - 0
app/src/main/java/com/edufound/reader/presenter/VideoPresenter.java

@@ -0,0 +1,160 @@
+package com.edufound.reader.presenter;
+
+import android.content.Intent;
+import android.media.MediaPlayer;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.edufound.reader.base.BasePresenter;
+import com.edufound.reader.contract.VideoContract;
+import com.edufound.reader.cusview.FullScreenVideoController;
+import com.edufound.reader.ijkplayer.media.IjkVideoView;
+import com.edufound.reader.model.VideoModel;
+import com.edufound.reader.util.CToast;
+
+import tv.danmaku.ijk.media.player.IMediaPlayer;
+
+public class VideoPresenter extends BasePresenter<VideoContract.View> implements VideoContract.Presenter {
+    VideoModel mModel;
+    IjkVideoView mVideoView;
+
+    public VideoPresenter() {
+        this.mModel = new VideoModel();
+    }
+
+    @Override
+    public void createVideoPlayer(FrameLayout frame) {
+        mView.showLoading();
+        mVideoView = new IjkVideoView(mView.getActivity());
+        mVideoView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+        mVideoView.setOnCompletionListener(new IMediaPlayer.OnCompletionListener() {
+            @Override
+            public void onCompletion(IMediaPlayer iMediaPlayer) {
+                onVideoCompletion(iMediaPlayer);
+            }
+        });
+        mVideoView.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() {
+            @Override
+            public void onPrepared(IMediaPlayer iMediaPlayer) {
+                onVideoPrepared(iMediaPlayer);
+            }
+        });
+        mVideoView.setOnInfoListener(new IMediaPlayer.OnInfoListener() {
+            @Override
+            public boolean onInfo(IMediaPlayer iMediaPlayer, int what, int extra) {
+                onVideoInfoListener(iMediaPlayer, what, extra);
+                return true;
+            }
+        });
+        mVideoView.setOnErrorListener(new IMediaPlayer.OnErrorListener() {
+            @Override
+            public boolean onError(IMediaPlayer iMediaPlayer, int what, int extra) {
+                onVideoError(iMediaPlayer, what, extra);
+                return true;
+            }
+        });
+        frame.addView(mVideoView);
+    }
+
+    @Override
+    public void initVideoPlayer(Intent intent) {
+        String play_url = intent.getStringExtra("playUrl");
+        mVideoView.setVideoPath(play_url);
+        mVideoView.start();
+    }
+
+    @Override
+    public void onVideoCompletion(IMediaPlayer mediaPlayer) {
+
+    }
+
+    @Override
+    public void onVideoError(IMediaPlayer mediaPlayer, int what, int extra) {
+        switch (what) {
+            case -10000:
+//                mVideoHandler.sendEmptyMessage(DISCONNECT_NETWORK);
+                CToast.makeText(mView.getActivity(), "网络出现异常", 3000).show();
+                break;
+            default:
+                mVideoView.stopPlayback();
+                mVideoView.stopBackgroundPlay();
+                CToast.makeText(mView.getActivity(), "视频异常--what:" + what + "--extra:" + extra, 3000).show();
+                mView.getActivity().finish();
+                break;
+        }
+    }
+
+    @Override
+    public void onVideoPrepared(IMediaPlayer mediaPlayer) {
+
+    }
+
+    @Override
+    public void onVideoInfoListener(IMediaPlayer mediaPlayer, int what, int extra) {
+        switch (what) {
+            case MediaPlayer.MEDIA_INFO_BUFFERING_START:
+                mView.showLoading();
+                break;
+            case MediaPlayer.MEDIA_INFO_BUFFERING_END:
+                mView.hideLoading();
+                break;
+        }
+    }
+
+    @Override
+    public void addVideoController(FrameLayout frame) {
+//        mVideoController = (FrameLayout) LayoutInflater.from(mView.getActivity()).inflate(R.layout.player_controller, null);
+//        frame.addView(mVideoController);
+        frame.addView(new FullScreenVideoController(mView.getActivity()).getController());
+    }
+
+    @Override
+    public void activityDestroy() {
+        mVideoView.stopPlayback();
+        mVideoView.stopBackgroundPlay();
+        mVideoView.release(true);
+        mVideoView = null;
+    }
+
+    @Override
+    public void activityPause() {
+
+    }
+
+    @Override
+    public void activityResume() {
+
+    }
+
+}
+
+
+
+/*
+ * 使用方法
+ * */
+//  mModel.login("", "").compose(RxScheduler.Obs_io_main()).to(mView.bindAutoDispose()).subscribe(new Observer<Object>() {
+//@Override
+//public void onSubscribe(@NonNull Disposable d) {
+//        //开始
+//        mView.showLoading();
+//        }
+//
+//@Override
+//public void onNext(@NonNull Object o) {
+//        //完成
+//
+//        }
+//
+//@Override
+//public void onError(@NonNull Throwable e) {
+//        //错误
+//        }
+//
+//@Override
+//public void onComplete() {
+//        //结束
+//        }
+//        });
+
+

+ 268 - 0
app/src/main/java/com/edufound/reader/services/RecordServices.java

@@ -0,0 +1,268 @@
+package com.edufound.reader.services;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.text.TextUtils;
+
+import com.chivox.aiengine.AudioSrc;
+import com.chivox.aiengine.Engine;
+import com.chivox.aiengine.EvalResult;
+import com.chivox.aiengine.EvalResultListener;
+import com.chivox.aiengine.RetValue;
+import com.chivox.aiengine.SdkInfo;
+import com.chivox.media.AudioPlayer;
+import com.edufound.reader.bean.ChivoxBean;
+import com.edufound.reader.util.AIEngineHelper;
+import com.edufound.reader.util.Consts;
+import com.edufound.reader.util.DeviceUuidFactory;
+import com.google.gson.Gson;
+import com.orhanobut.logger.Logger;
+
+import org.json.JSONObject;
+
+import java.io.File;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import androidx.annotation.Nullable;
+
+public class RecordServices extends Service {
+
+    private String RECORD_DIR = "";
+    private RecordServicesBind mBinder = new RecordServicesBind();
+    private ExecutorService workerThread = Executors.newFixedThreadPool(1);
+    private Engine mAiengine = null;
+    private String recFilePath;
+    private String mUploadBody;
+    private RecordCallBack mRecordCallBack;
+    boolean onInit = false;
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        if (TextUtils.isEmpty(RECORD_DIR)) {
+            RECORD_DIR = Consts.getmApplicAtion().getDir("record", MODE_PRIVATE).getAbsolutePath();
+        }
+
+    }
+
+
+    void runOnWorkerThread(Runnable runnable) {
+        workerThread.execute(runnable);
+    }
+
+    public void setRecordCallBack(RecordCallBack callBack) {
+        this.mRecordCallBack = callBack;
+    }
+
+
+    void initChiVox(Context context, String name, String refText) {
+        runOnWorkerThread(new Runnable() {
+            @Override
+            public void run() {
+                JSONObject cfg = new JSONObject();
+                try {
+                    String provisionPath = AIEngineHelper.extractResourceOnce(Consts.getmApplicAtion(), "aiengine.provision", false);
+                    cfg.put("appKey", ChivoxBean.appKey); // 设置AppKey
+                    cfg.put("secretKey", ChivoxBean.secretKey); // 设置SecretKey
+                    cfg.put("provision", provisionPath);
+
+                    //获取sdk版本
+                    String version = SdkInfo.singleton().version;//获取二次封装SDK版本
+                    String commonversion = SdkInfo.singleton().commonSdkVersion;//获取通用SDK版本
+                    { // cloud
+                        JSONObject cloud = new JSONObject();
+                        cloud.put("enable", 1);
+                        cfg.put("cloud", cloud);
+                    }
+
+                    // 创建引擎,本调用不会阻塞UI线程,待创建成功后,会通过Engine.CreateCallback回调
+                    Engine.create(Consts.getmApplicAtion(), cfg, new Engine.CreateCallback() {
+                        @Override
+                        public void onSuccess(Engine engine) {
+                            // 创建成功, 请保存engine对象用于后续评测
+                            mAiengine = engine;
+                            Logger.e("初始化录音成功");
+                            startRecord(context, name, refText);
+                        }
+
+                        @Override
+                        public void onFail(RetValue err) {
+                            // 创建失败, 请查看e.errId和e.error分析原因。
+                            Logger.e(err.errId + ", " + err.error);
+                        }
+                    });
+
+                } catch (Exception ex) {
+                    ex.printStackTrace();
+                }
+            }
+        });
+    }
+
+
+    /*
+     * 开始录音(如果没有初始化,就初始化,初始化了直接往下走)
+     * */
+    public void startRecord(Context context, String name, String refText) {
+        if (mAiengine == null) {
+            initChiVox(context, name, refText);
+            return;
+        }
+        JSONObject param = new JSONObject();
+        try {
+            param.put("coreProvideType", "cloud");
+            {
+                JSONObject app = new JSONObject();
+                app.put("userId", DeviceUuidFactory.getUuid()); // 终端用户标识,建议根据用户账号填写userId,方便排查问题。
+                param.put("app", app);
+                JSONObject audio = new JSONObject();
+                audio.put("audioType", "wav");
+                audio.put("channel", 1);
+                audio.put("sampleBytes", 2);
+                audio.put("sampleRate", 16000);
+                param.put("audio", audio);
+                JSONObject request = new JSONObject();
+                request.put("coreType", "cn.pred.raw");//评测内核,英文单词内核
+                request.put("refText", refText);//评测文本
+                request.put("rank", 100); //评分分制
+                request.put("attachAudioUrl", 1); //是否返回音频url。1: 返回。0: 不返回。
+                param.put("request", request);
+            }
+            AudioSrc.InnerRecorder innerRecorder = new AudioSrc.InnerRecorder();
+            innerRecorder.recordParam.sampleBytes = 2;
+            innerRecorder.recordParam.sampleRate = 16000;
+            if (TextUtils.isEmpty(RECORD_DIR)) {
+                RECORD_DIR = Consts.getmApplicAtion().getDir("record", MODE_PRIVATE).getAbsolutePath();
+            }
+            File savepath = new File(RECORD_DIR + "/" + name + ".wav");
+            Logger.e("录音保存地址:" + savepath.getAbsolutePath());
+            innerRecorder.recordParam.saveFile = savepath;
+            StringBuilder tokenId = new StringBuilder();
+            RetValue ret = mAiengine.start(context, innerRecorder, tokenId, param, new EvalResultListener() {
+                @Override
+                public void onError(String s, EvalResult evalResult) {
+                    mRecordCallBack.onRecordStop(-1, tokenId.toString(), evalResult);
+                }
+
+                @Override
+                public void onEvalResult(String s, EvalResult evalResult) {
+                    mRecordCallBack.onRecordStop(-0, tokenId.toString(), evalResult);
+                    recFilePath = evalResult.recFilePath();
+                    Logger.e("recFilePath:" + recFilePath);
+                }
+
+                @Override
+                public void onBinResult(String s, EvalResult evalResult) {
+
+                }
+
+                @Override
+                public void onVad(String s, EvalResult evalResult) {
+
+                }
+
+                @Override
+                public void onSoundIntensity(String s, EvalResult evalResult) {
+
+                }
+
+                @Override
+                public void onOther(String s, EvalResult evalResult) {
+
+                }
+            });
+            Logger.e(" ret.errId:" + ret.errId);
+            if (0 == ret.errId) {
+                mRecordCallBack.onRecordStart();
+            } else {
+                return;
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    /*
+     * 停止录音
+     * */
+    public void stopRecord() {
+        if (mAiengine == null) {
+            Logger.e("aiengine==null");
+            return;
+        }
+        RetValue ret = mAiengine.stop();
+        if (0 == ret.errId) {
+//            mRecordCallBack!!.onRecordStop(1, "", null)
+        }
+    }
+
+
+    /*
+     * 取消录音
+     * */
+    public void cancelRecord() {
+        if (mAiengine == null) {
+            return;
+        }
+        mAiengine.cancel();
+        mRecordCallBack.onRecordCancel();
+    }
+
+
+    /*
+     * 重播刚刚录音的音频
+     * */
+    public void replayRecord() {
+        AudioPlayer.sharedInstance().play(recFilePath, new AudioPlayer.Listener() {
+            @Override
+            public void onStarted(AudioPlayer audioPlayer) {
+                Logger.e("开始播放录音");
+            }
+
+            @Override
+            public void onStopped(AudioPlayer audioPlayer) {
+                Logger.e("停止播放录音");
+            }
+
+            @Override
+            public void onError(AudioPlayer audioPlayer, String s) {
+                Logger.e("播放录音报错:" + s);
+            }
+        });
+    }
+
+    /*
+     * 取消播放录音的音频
+     * */
+    public void cancelRePlay() {
+        AudioPlayer.sharedInstance().cancel();
+    }
+
+    public class RecordServicesBind extends Binder {
+        public RecordServices getService() {
+            return new RecordServices();
+        }
+    }
+
+    public interface RecordCallBack {
+        void onRecordStart();
+
+        //type=0是成功 type=-1是失败
+        void onRecordStop(int type, String tokenId, EvalResult result);
+
+        void onRecordCancel();
+    }
+
+}
+
+

+ 297 - 0
app/src/main/java/com/edufound/reader/util/AIEngineHelper.java

@@ -0,0 +1,297 @@
+package com.edufound.reader.util;
+
+import android.content.Context;
+import android.os.Environment;
+import android.util.Log;
+
+import com.chivox.AIEngine;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+//import org.apache.http.HttpResponse;
+//import org.apache.http.NameValuePair;
+//import org.apache.http.client.HttpClient;
+//import org.apache.http.client.entity.UrlEncodedFormEntity;
+//import org.apache.http.client.methods.HttpPost;
+//import org.apache.http.impl.client.DefaultHttpClient;
+//import org.apache.http.message.BasicNameValuePair;
+//import org.apache.http.params.BasicHttpParams;
+//import org.apache.http.util.EntityUtils;
+
+public class AIEngineHelper {
+
+	private static String TAG = "AIEngineHelper";
+	private static int BUFFER_SIZE = 4096;
+
+	private static String readFileAsString(File file) throws IOException {
+		String line;
+		StringBuffer sb = new StringBuffer();
+		BufferedReader br = new BufferedReader(new FileReader(file));
+		while ((line = br.readLine()) != null) {
+			sb.append(line);
+		}
+
+		br.close();
+		return sb.toString();
+	}
+
+	private static void writeFileAsString(File file, String str) throws IOException {
+		FileWriter fw = new FileWriter(file);
+		fw.write(str);
+		fw.close();
+	}
+
+	private static void writeFileAsBytes(File file, byte [] bytes) throws IOException {
+		FileOutputStream fstream = new FileOutputStream(file);
+		BufferedOutputStream stream = new BufferedOutputStream(fstream);
+		stream.write(bytes);
+		stream.close();
+		fstream.close();
+	}
+
+	private static void removeDirectory(File directory) {
+		if (directory.isDirectory()) {
+			File[] files = directory.listFiles();
+			for (int i = 0; i < files.length; i++) {
+				if (files[i].isDirectory()) {
+					removeDirectory(files[i]);
+				}
+				files[i].delete();
+			}
+			directory.delete();
+		}
+	}
+
+	/**
+	 * extract resource once, the resource should in zip formatting
+	 *
+	 * @param context
+	 * @param name
+	 * @return return resource directory contains resources for native aiengine cores on success, otherwise return null
+	 */
+	public static String extractResourceOnce(Context context, String name, boolean unzip) {
+
+		try {
+			if (unzip) {
+				String pureName = name.replaceAll("\\.[^.]*$", "");
+
+				File filesDir = getFilesDir(context);
+				File targetDir = new File(filesDir, pureName);
+
+				String md5sum = md5sum(context.getAssets().open(name));
+
+				File md5sumFile = new File(targetDir, ".md5sum");
+				if (targetDir.isDirectory()) {
+					if (md5sumFile.isFile()) {
+						String md5sum2 = readFileAsString(md5sumFile);
+						if (md5sum2.equals(md5sum)) {
+							return targetDir.getAbsolutePath(); /* already extracted */
+						}
+					}
+
+					removeDirectory(targetDir); /* remove old dirty resource */
+				}
+
+				unzip(context.getAssets().open(name), targetDir);
+				writeFileAsString(md5sumFile, md5sum);
+
+				return targetDir.getAbsolutePath();
+			} else {
+				File targetFile = new File(getFilesDir(context), name);
+				copyInputStreamToFile(context.getAssets().open(name), targetFile);
+				return targetFile.getAbsolutePath();
+			}
+		} catch (Exception e) {
+			Log.e(TAG, "failed to extract resource", e);
+		}
+
+		return null;
+	}
+
+	/**
+	 * register device once
+	 * @param appKey
+	 * @param secretKey
+	 * @param userId
+	 * @return return serialNumber on success, otherwise return null
+	 */
+	public static String registerDeviceOnce(Context context, String appKey, String secretKey, String userId) {
+		File filesDir = getFilesDir(context);
+		File serialNumberFile = new File(filesDir, "aiengine.serial");
+		String serialNumber = "";
+		String serialNumberInfo = "";
+		if (serialNumberFile.isFile()) {
+			try {
+				serialNumber = readFileAsString(serialNumberFile);
+				Log.d(TAG, "yes");
+				return serialNumber;
+			} catch (IOException e) {
+				/* ignore */
+			}
+		}
+		String sig = String.format("{\"appKey\":\"%s\",\"secretKey\":\"%s\",\"userId\":\"%s\"}", appKey,secretKey,userId);
+		JSONObject sig_json = null;
+		try {
+			sig_json = new JSONObject(sig);
+		} catch (JSONException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		byte cfg_b[] = Arrays.copyOf(sig_json.toString().getBytes(), 1024);
+		int ret = AIEngine.aiengine_opt(0, 6, cfg_b, 1024);
+		if (ret > 0) {
+			serialNumberInfo = new String(cfg_b, 0, ret);
+		} else {
+			serialNumberInfo = new String(cfg_b);
+		}
+		try {
+			serialNumber = (new JSONObject(serialNumberInfo)).getString("serialNumber");
+		} catch (JSONException e1) {
+			// TODO Auto-generated catch block
+			e1.printStackTrace();
+			return "";
+		}
+		if(serialNumber.length()==0){
+			return "";
+		}
+		try {
+			writeFileAsString(serialNumberFile, serialNumber);
+		} catch (Exception e1) {
+			/* ignore */
+		}
+		return serialNumber;
+	}
+
+	public static File getFilesDir(Context context) {
+
+		File targetDir = null;
+
+		if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+			// targetDir = context.getExternalFilesDir(null); // not support android 2.1
+			targetDir = new File(Environment.getExternalStorageDirectory(), "Android/data/" + context.getApplicationInfo().packageName + "/files");
+			if (!targetDir.exists()) {
+				targetDir.mkdirs();
+			}
+		}
+
+		if (targetDir == null || !targetDir.exists()) {
+			targetDir = context.getFilesDir();
+		}
+
+		return targetDir;
+	}
+
+	private static void unzip(InputStream is, File targetDir)
+			throws IOException {
+
+		ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is,
+				BUFFER_SIZE));
+
+		ZipEntry ze;
+		while ((ze = zis.getNextEntry()) != null) {
+			if (ze.isDirectory()) {
+				new File(targetDir, ze.getName()).mkdirs();
+			} else {
+
+				File file = new File(targetDir, ze.getName());
+				File parentdir = file.getParentFile();
+				if (parentdir != null && (!parentdir.exists())) {
+					parentdir.mkdirs();
+				}
+
+				int pos;
+				byte[] buf = new byte[BUFFER_SIZE];
+				OutputStream bos = new FileOutputStream(file);
+				while ((pos = zis.read(buf, 0, BUFFER_SIZE)) > 0) {
+					bos.write(buf, 0, pos);
+				}
+				bos.flush();
+				bos.close();
+
+				Log.d(TAG, file.getAbsolutePath());
+			}
+		}
+
+		zis.close();
+		is.close();
+	}
+
+	private static void copyInputStreamToFile(InputStream is, File file)
+			throws Exception {
+		int bytes;
+		byte[] buf = new byte[BUFFER_SIZE];
+
+		FileOutputStream fos = new FileOutputStream(file);
+		while ((bytes = is.read(buf, 0, BUFFER_SIZE)) > 0) {
+			fos.write(buf, 0, bytes);
+		}
+
+		is.close();
+		fos.close();
+	};
+
+	private static String sha1(String message) {
+		try {
+			MessageDigest md = MessageDigest.getInstance("SHA-1");
+			md.update(message.getBytes(), 0, message.length());
+			return bytes2hex(md.digest());
+		} catch (Exception e) {
+			/* ignore */
+		}
+		return null;
+	}
+
+	private static String bytes2hex(byte[] bytes) {
+		StringBuffer sb = new StringBuffer(bytes.length * 2);
+		for (int i = 0; i < bytes.length; i++) {
+			int v = bytes[i] & 0xff;
+			if (v < 16) {
+				sb.append('0');
+			}
+			sb.append(Integer.toHexString(v));
+		}
+		return sb.toString();
+	}
+
+	private static String md5sum(InputStream is) {
+		int bytes;
+		byte buf[] = new byte[BUFFER_SIZE];
+		try {
+			MessageDigest md = MessageDigest.getInstance("MD5");
+			while ((bytes = is.read(buf, 0, BUFFER_SIZE)) > 0) {
+				md.update(buf, 0, bytes);
+			}
+			is.close();
+			return bytes2hex(md.digest());
+		} catch (Exception e) {
+			/* ignore */
+		}
+		return null;
+	}
+
+	public static String getProvisionTmpFile(Context context)
+	{
+		File filesDir = getFilesDir(context);
+		String str = filesDir.getAbsolutePath() + "/aiengine.provision.temp";
+
+		Log.d(TAG, "provison tmp file path: " + str);
+
+		return str;
+	}
+}

+ 237 - 0
app/src/main/java/com/edufound/reader/util/CToast.java

@@ -0,0 +1,237 @@
+package com.edufound.reader.util;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.os.Handler;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class CToast {
+
+    public static CToast makeText(Context context, CharSequence text, int duration) {
+        CToast result = new CToast(context);
+
+        LinearLayout mLayout = new LinearLayout(context);
+        TextView tv = new TextView(context);
+        tv.setText(text);
+        tv.setTextColor(Color.WHITE);
+        tv.setGravity(Gravity.CENTER);
+//        mLayout.setBackgroundResource(R.drawable.widget_toast_bg);
+
+        int w = context.getResources().getDisplayMetrics().widthPixels / 2;
+        int h = context.getResources().getDisplayMetrics().widthPixels / 10;
+        mLayout.addView(tv, w, h);
+        result.mNextView = mLayout;
+        result.mDuration = duration;
+
+        return result;
+    }
+
+    public static final int LENGTH_SHORT = 2000;
+    public static final int LENGTH_LONG = 3500;
+
+    private final Handler mHandler = new Handler();
+    private int mDuration = LENGTH_SHORT;
+    private int mGravity = Gravity.CENTER;
+    private int mX, mY;
+    private float mHorizontalMargin;
+    private float mVerticalMargin;
+    private View mView;
+    private View mNextView;
+
+    private WindowManager mWM;
+    private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
+
+
+    public CToast(Context context) {
+        init(context);
+    }
+
+    /**
+     * Set the view to show.
+     *
+     * @see #getView
+     */
+    public void setView(View view) {
+        mNextView = view;
+    }
+
+    /**
+     * Return the view.
+     *
+     * @see #setView
+     */
+    public View getView() {
+        return mNextView;
+    }
+
+    /**
+     * Set how long to show the view for.
+     *
+     * @see #LENGTH_SHORT
+     * @see #LENGTH_LONG
+     */
+    public void setDuration(int duration) {
+        mDuration = duration;
+    }
+
+    /**
+     * Return the duration.
+     *
+     * @see #setDuration
+     */
+    public int getDuration() {
+        return mDuration;
+    }
+
+    /**
+     * Set the margins of the view.
+     *
+     * @param horizontalMargin The horizontal margin, in percentage of the
+     *                         container width, between the container's edges and the
+     *                         notification
+     * @param verticalMargin   The vertical margin, in percentage of the
+     *                         container height, between the container's edges and the
+     *                         notification
+     */
+    public void setMargin(float horizontalMargin, float verticalMargin) {
+        mHorizontalMargin = horizontalMargin;
+        mVerticalMargin = verticalMargin;
+    }
+
+    /**
+     * Return the horizontal margin.
+     */
+    public float getHorizontalMargin() {
+        return mHorizontalMargin;
+    }
+
+    /**
+     * Return the vertical margin.
+     */
+    public float getVerticalMargin() {
+        return mVerticalMargin;
+    }
+
+    /**
+     * Set the location at which the notification should appear on the screen.
+     *
+     * @see android.view.Gravity
+     * @see #getGravity
+     */
+    public void setGravity(int gravity, int xOffset, int yOffset) {
+        mGravity = gravity;
+        mX = xOffset;
+        mY = yOffset;
+    }
+
+    /**
+     * Get the location at which the notification should appear on the screen.
+     *
+     * @see android.view.Gravity
+     * @see #getGravity
+     */
+    public int getGravity() {
+        return mGravity;
+    }
+
+    /**
+     * Return the X offset in pixels to apply to the gravity's location.
+     */
+    public int getXOffset() {
+        return mX;
+    }
+
+    /**
+     * Return the Y offset in pixels to apply to the gravity's location.
+     */
+    public int getYOffset() {
+        return mY;
+    }
+
+    /**
+     * schedule handleShow into the right thread
+     */
+    public void show() {
+        mHandler.post(mShow);
+
+        if (mDuration > 0) {
+            mHandler.postDelayed(mHide, mDuration);
+        }
+    }
+
+    /**
+     * schedule handleHide into the right thread
+     */
+    public void hide() {
+        mHandler.post(mHide);
+    }
+
+    private final Runnable mShow = new Runnable() {
+        public void run() {
+            handleShow();
+        }
+    };
+
+    private final Runnable mHide = new Runnable() {
+        public void run() {
+            handleHide();
+        }
+    };
+
+    private void init(Context context) {
+        final WindowManager.LayoutParams params = mParams;
+        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
+        params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+        params.format = PixelFormat.TRANSLUCENT;
+        params.windowAnimations = android.R.style.Animation_Toast;
+        params.type = WindowManager.LayoutParams.TYPE_TOAST;
+        params.setTitle("Toast");
+
+        mWM = (WindowManager) context.getApplicationContext()
+                .getSystemService(Context.WINDOW_SERVICE);
+    }
+
+
+    private void handleShow() {
+
+        if (mView != mNextView) {
+            // remove the old view if necessary
+            handleHide();
+            mView = mNextView;
+//            mWM = WindowManagerImpl.getDefault();
+            final int gravity = mGravity;
+            mParams.gravity = gravity;
+            if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
+                mParams.horizontalWeight = 1.0f;
+            }
+            if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
+                mParams.verticalWeight = 1.0f;
+            }
+            mParams.x = mX;
+            mParams.y = mY;
+            mParams.verticalMargin = mVerticalMargin;
+            mParams.horizontalMargin = mHorizontalMargin;
+            if (mView.getParent() != null) {
+                mWM.removeView(mView);
+            }
+            mWM.addView(mView, mParams);
+        }
+    }
+
+    private void handleHide() {
+        if (mView != null) {
+            if (mView.getParent() != null) {
+                mWM.removeView(mView);
+            }
+            mView = null;
+        }
+    }
+}

+ 27 - 0
app/src/main/java/com/edufound/reader/util/Consts.java

@@ -0,0 +1,27 @@
+package com.edufound.reader.util;
+
+import android.app.Application;
+
+public class Consts {
+    private static Application mApplicAtion;
+
+    private static int[] screenSize;
+
+
+    public static Application getmApplicAtion() {
+        return mApplicAtion;
+    }
+
+    public static void setmApplicAtion(Application mApplicAtion) {
+        Consts.mApplicAtion = mApplicAtion;
+    }
+
+    public static int[] getScreenSize() {
+        return screenSize;
+    }
+
+    public static void setScreenSize(int[] screenSize) {
+        Consts.screenSize = screenSize;
+    }
+
+}

+ 1 - 1
app/src/main/java/com/efunbox/reader/util/CrashHandler.java

@@ -1,4 +1,4 @@
-package com.efunbox.reader.util;
+package com.edufound.reader.util;
 
 
 import android.content.Context;

+ 1 - 2
app/src/main/java/com/efunbox/reader/util/DeviceUtil.java

@@ -1,11 +1,10 @@
-package com.efunbox.reader.util;
+package com.edufound.reader.util;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.Build;
-import android.telephony.TelephonyManager;
 import android.util.DisplayMetrics;
 
 import java.io.UnsupportedEncodingException;

+ 1 - 1
app/src/main/java/com/efunbox/reader/util/DeviceUuidFactory.java

@@ -1,4 +1,4 @@
-package com.efunbox.reader.util;
+package com.edufound.reader.util;
 
 import android.annotation.SuppressLint;
 import android.content.Context;

+ 24 - 0
app/src/main/java/com/edufound/reader/util/EfunboxUtil.java

@@ -0,0 +1,24 @@
+package com.edufound.reader.util;
+
+import android.app.Activity;
+import android.app.Application;
+import android.util.DisplayMetrics;
+
+import io.reactivex.rxjava3.annotations.NonNull;
+
+public class EfunboxUtil {
+
+
+    public static void setCustomDensity(@NonNull Activity activity, @NonNull Application application) {
+        final DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();
+        final float targetDensity = appDisplayMetrics.heightPixels / 720;
+        final float targetSxaledDensity = targetDensity * (appDisplayMetrics.scaledDensity / appDisplayMetrics.density);
+        final int targetDensityDpi = (int) (160 * targetDensity);
+        appDisplayMetrics.density = appDisplayMetrics.scaledDensity = targetSxaledDensity;
+        appDisplayMetrics.densityDpi = targetDensityDpi;
+        final DisplayMetrics activiyDisplayMetrics = activity.getResources().getDisplayMetrics();
+        activiyDisplayMetrics.density = activiyDisplayMetrics.scaledDensity = targetSxaledDensity;
+        activiyDisplayMetrics.densityDpi = targetDensityDpi;
+
+    }
+}

+ 2 - 2
app/src/main/java/com/efunbox/reader/util/GlideUtils.java

@@ -1,4 +1,4 @@
-package com.efunbox.reader.util;
+package com.edufound.reader.util;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
@@ -13,7 +13,7 @@ import com.bumptech.glide.load.resource.bitmap.CenterCrop;
 import com.bumptech.glide.request.RequestListener;
 import com.bumptech.glide.request.RequestOptions;
 import com.bumptech.glide.request.target.Target;
-import com.efunbox.reader.R;
+import com.edufound.reader.R;
 
 import java.io.File;
 

+ 1 - 1
app/src/main/java/com/efunbox/reader/util/HttpInterceptor.java

@@ -1,4 +1,4 @@
-package com.efunbox.reader.util;
+package com.edufound.reader.util;
 
 import com.okhttplib.HttpInfo;
 import com.okhttplib.interceptor.ExceptionInterceptor;

+ 1 - 3
app/src/main/java/com/efunbox/reader/util/LiuHaiScreenUtil.java

@@ -1,10 +1,8 @@
-package com.efunbox.reader.util;
+package com.edufound.reader.util;
 
 import android.app.Activity;
 import android.os.Build;
-import android.view.DisplayCutout;
 import android.view.View;
-import android.view.WindowInsets;
 import android.view.WindowManager;
 
 import androidx.annotation.RequiresApi;

+ 188 - 0
app/src/main/java/com/edufound/reader/util/MMKVUtil.java

@@ -0,0 +1,188 @@
+package com.edufound.reader.util;
+
+
+import android.os.Parcelable;
+
+import com.tencent.mmkv.MMKV;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class MMKVUtil {
+
+    private static MMKV mkv;
+
+    private MMKVUtil() {
+        mkv = MMKV.defaultMMKV();
+    }
+
+    public static MMKVUtil getInstance() {
+        return SingletonHolder.sInstance;
+    }
+
+    //静态内部类
+    private static class SingletonHolder {
+        private static final MMKVUtil sInstance = new MMKVUtil();
+    }
+
+    /**
+     * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法
+     *
+     * @param key
+     * @param object
+     */
+    public static void encode(String key, Object object) {
+        if (object instanceof String) {
+            mkv.encode(key, (String) object);
+        } else if (object instanceof Integer) {
+            mkv.encode(key, (Integer) object);
+        } else if (object instanceof Boolean) {
+            mkv.encode(key, (Boolean) object);
+        } else if (object instanceof Float) {
+            mkv.encode(key, (Float) object);
+        } else if (object instanceof Long) {
+            mkv.encode(key, (Long) object);
+        } else if (object instanceof Double) {
+            mkv.encode(key, (Double) object);
+        } else if (object instanceof byte[]) {
+            mkv.encode(key, (byte[]) object);
+        } else {
+            mkv.encode(key, object.toString());
+        }
+    }
+
+    public static void encodeSet(String key, Set<String> sets) {
+        mkv.encode(key, sets);
+    }
+
+    public static void encodeParcelable(String key, Parcelable obj) {
+        mkv.encode(key, obj);
+    }
+
+    /**
+     * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值
+     *
+     * @param key
+     * @param defaultObject
+     * @return
+     */
+    public static Object decode(String key, Object defaultObject) {
+        if (defaultObject instanceof String) {
+            return mkv.decodeString(key, (String) defaultObject);
+        } else if (defaultObject instanceof Integer) {
+            return mkv.decodeInt(key, (Integer) defaultObject);
+        } else if (defaultObject instanceof Boolean) {
+            return mkv.decodeBool(key, (Boolean) defaultObject);
+        } else if (defaultObject instanceof Float) {
+            return mkv.decodeFloat(key, (Float) defaultObject);
+        } else if (defaultObject instanceof Long) {
+            return mkv.decodeLong(key, (Long) defaultObject);
+        } else if (defaultObject instanceof Double) {
+            return mkv.decodeDouble(key, (Double) defaultObject);
+        } else if (defaultObject instanceof byte[]) {
+            return mkv.decodeBytes(key, (byte[]) defaultObject);
+        }
+        return defaultObject;
+    }
+
+    /**
+     * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值
+     */
+    public static Integer decodeInt(String key) {
+        return mkv.decodeInt(key, 0);
+    }
+
+    public static Double decodeDouble(String key) {
+        return mkv.decodeDouble(key, 0.00);
+    }
+
+    public static Long decodeLong(String key) {
+        return mkv.decodeLong(key, 0L);
+    }
+
+    public static Boolean decodeBoolean(String key) {
+        return mkv.decodeBool(key, false);
+    }
+
+    public static Float decodeFloat(String key) {
+        return mkv.decodeFloat(key, 0F);
+    }
+
+    public static byte[] decodeBytes(String key) {
+        return mkv.decodeBytes(key);
+    }
+
+    public static String decodeString(String key) {
+        return mkv.decodeString(key, "");
+    }
+
+    public static Set<String> decodeStringSet(String key) {
+        return mkv.decodeStringSet(key, Collections.<String>emptySet());
+    }
+
+    public static Parcelable decodeParcelable(String key, Class clz) {
+        return mkv.decodeParcelable(key, clz);
+    }
+
+    /**
+     * 移除某个key对
+     *
+     * @param key
+     */
+    public static void removeKey(String key) {
+        mkv.removeValueForKey(key);
+    }
+
+    /**
+     * 移除多个key对
+     *
+     * @param key
+     */
+    public static void removeKeys(String[] key) {
+        mkv.removeValuesForKeys(key);
+    }
+
+    /**
+     * 获取全部key对
+     */
+    public static String[] getAllKeys() {
+        return mkv.allKeys();
+    }
+
+    /**
+     * 含有某个key
+     *
+     * @param key
+     * @return
+     */
+    public static boolean hasKey(String key) {
+        return mkv.containsKey(key);
+    }
+
+    /**
+     * 含有某个key
+     *
+     * @param key
+     * @return
+     */
+    public static boolean have(String key) {
+        return mkv.contains(key);
+    }
+
+    /**
+     * 清除所有key
+     */
+    public static void clearAll() {
+        mkv.clearAll();
+    }
+
+    /**
+     * 获取操作对象
+     *
+     * @return
+     */
+    public static MMKV getMkv() {
+        return mkv;
+    }
+
+}

+ 22 - 0
app/src/main/java/com/edufound/reader/util/PermissionsUtil.java

@@ -0,0 +1,22 @@
+package com.edufound.reader.util;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.pm.PackageManager;
+
+import com.orhanobut.logger.Logger;
+
+import androidx.core.app.ActivityCompat;
+
+public class PermissionsUtil {
+    private static int GET_RECODE_AUDIO = 1;
+    private static String[] PERMISSION_AUDIO = new String[]{Manifest.permission.RECORD_AUDIO};
+
+    public static void verifyAudioPermissions(Activity activity) {
+        int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO);
+        if (permission != PackageManager.PERMISSION_GRANTED) {
+            Logger.e("获取录音权限:permission != PackageManager.PERMISSION_GRANTED");
+            ActivityCompat.requestPermissions(activity, PERMISSION_AUDIO, GET_RECODE_AUDIO);
+        }
+    }
+}

+ 1 - 1
app/src/main/java/com/efunbox/reader/util/SizeUtils.java

@@ -1,4 +1,4 @@
-package com.efunbox.reader.util;
+package com.edufound.reader.util;
 
 import android.content.Context;
 import android.util.DisplayMetrics;

+ 230 - 0
app/src/main/java/com/edufound/reader/videoutil/JZMediaIjk.java

@@ -0,0 +1,230 @@
+package com.edufound.reader.videoutil;
+
+
+import android.graphics.SurfaceTexture;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.view.Surface;
+
+import java.io.IOException;
+
+import cn.jzvd.JZMediaInterface;
+import cn.jzvd.Jzvd;
+import tv.danmaku.ijk.media.player.IMediaPlayer;
+import tv.danmaku.ijk.media.player.IjkMediaPlayer;
+import tv.danmaku.ijk.media.player.IjkTimedText;
+
+/**
+ * Created by Nathen on 2017/11/18.
+ * ijk兼容SO库:https://github.com/NamHofstadter/IjkPlayerSos
+ * ijk默认不支持https协议,需要的请自行下载so库
+ */
+
+public class JZMediaIjk extends JZMediaInterface implements IMediaPlayer.OnPreparedListener, IMediaPlayer.OnVideoSizeChangedListener, IMediaPlayer.OnCompletionListener, IMediaPlayer.OnErrorListener, IMediaPlayer.OnInfoListener, IMediaPlayer.OnBufferingUpdateListener, IMediaPlayer.OnSeekCompleteListener, IMediaPlayer.OnTimedTextListener {
+    IjkMediaPlayer ijkMediaPlayer;
+
+    public JZMediaIjk(Jzvd jzvd) {
+        super(jzvd);
+    }
+
+    @Override
+    public void start() {
+        if (ijkMediaPlayer != null) ijkMediaPlayer.start();
+    }
+
+    @Override
+    public void prepare() {
+
+        release();
+        mMediaHandlerThread = new HandlerThread("JZVD");
+        mMediaHandlerThread.start();
+        mMediaHandler = new Handler(mMediaHandlerThread.getLooper());//主线程还是非主线程,就在这里
+        handler = new Handler();
+
+        mMediaHandler.post(() -> {
+
+            ijkMediaPlayer = new IjkMediaPlayer();
+
+            ijkMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            ////1为硬解 0为软解
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1);
+            //使用opensles把文件从java层拷贝到native层
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0);
+            //视频格式
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
+            //跳帧处理(-1~120)。CPU处理慢时,进行跳帧处理,保证音视频同步
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
+            //0为一进入就播放,1为进入时不播放
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
+            ////域名检测
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
+            //设置是否开启环路过滤: 0开启,画面质量高,解码开销大,48关闭,画面质量差点,解码开销小
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
+            //最大缓冲大小,单位kb
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", 1024 * 1024);
+            //某些视频在SeekTo的时候,会跳回到拖动前的位置,这是因为视频的关键帧的问题,通俗一点就是FFMPEG不兼容,视频压缩过于厉害,seek只支持关键帧,出现这个情况就是原始的视频文件中i 帧比较少
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1);
+            //是否重连
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "reconnect", 1);
+            //http重定向https
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", 1);
+            //设置seekTo能够快速seek到指定位置并播放
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "fastseek");
+            //播放前的探测Size,默认是1M, 改小一点会出画面更快
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 1024 * 10);
+            //1变速变调状态 0变速不变调状态
+            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", 1);
+
+            ijkMediaPlayer.setOnPreparedListener(JZMediaIjk.this);
+            ijkMediaPlayer.setOnVideoSizeChangedListener(JZMediaIjk.this);
+            ijkMediaPlayer.setOnCompletionListener(JZMediaIjk.this);
+            ijkMediaPlayer.setOnErrorListener(JZMediaIjk.this);
+            ijkMediaPlayer.setOnInfoListener(JZMediaIjk.this);
+            ijkMediaPlayer.setOnBufferingUpdateListener(JZMediaIjk.this);
+            ijkMediaPlayer.setOnSeekCompleteListener(JZMediaIjk.this);
+            ijkMediaPlayer.setOnTimedTextListener(JZMediaIjk.this);
+
+            try {
+                ijkMediaPlayer.setDataSource(jzvd.jzDataSource.getCurrentUrl().toString());
+                ijkMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+                ijkMediaPlayer.setScreenOnWhilePlaying(true);
+                ijkMediaPlayer.prepareAsync();
+
+                ijkMediaPlayer.setSurface(new Surface(jzvd.textureView.getSurfaceTexture()));
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        });
+
+    }
+
+    @Override
+    public void pause() {
+        ijkMediaPlayer.pause();
+    }
+
+    @Override
+    public boolean isPlaying() {
+        return ijkMediaPlayer.isPlaying();
+    }
+
+    @Override
+    public void seekTo(long time) {
+        ijkMediaPlayer.seekTo(time);
+    }
+
+    @Override
+    public void release() {
+        if (mMediaHandler != null && mMediaHandlerThread != null && ijkMediaPlayer != null) {//不知道有没有妖孽
+            HandlerThread tmpHandlerThread = mMediaHandlerThread;
+            IjkMediaPlayer tmpMediaPlayer = ijkMediaPlayer;
+            JZMediaInterface.SAVED_SURFACE = null;
+
+            mMediaHandler.post(() -> {
+                tmpMediaPlayer.setSurface(null);
+                tmpMediaPlayer.release();
+                tmpHandlerThread.quit();
+            });
+            ijkMediaPlayer = null;
+        }
+    }
+
+    @Override
+    public long getCurrentPosition() {
+        if (ijkMediaPlayer == null) {
+            return 0;
+        }
+        return ijkMediaPlayer.getCurrentPosition();
+    }
+
+    @Override
+    public long getDuration() {
+        if (ijkMediaPlayer == null) return 0;
+        return ijkMediaPlayer.getDuration();
+    }
+
+    @Override
+    public void setVolume(float leftVolume, float rightVolume) {
+        ijkMediaPlayer.setVolume(leftVolume, rightVolume);
+    }
+
+    @Override
+    public void setSpeed(float speed) {
+        ijkMediaPlayer.setSpeed(speed);
+    }
+
+    @Override
+    public void onPrepared(IMediaPlayer iMediaPlayer) {
+        handler.post(() -> jzvd.onPrepared());
+    }
+
+    @Override
+    public void onVideoSizeChanged(IMediaPlayer iMediaPlayer, int i, int i1, int i2, int i3) {
+        handler.post(() -> jzvd.onVideoSizeChanged(iMediaPlayer.getVideoWidth(), iMediaPlayer.getVideoHeight()));
+    }
+
+    @Override
+    public boolean onError(IMediaPlayer iMediaPlayer, final int what, final int extra) {
+        handler.post(() -> jzvd.onError(what, extra));
+        return true;
+    }
+
+    @Override
+    public boolean onInfo(IMediaPlayer iMediaPlayer, final int what, final int extra) {
+        handler.post(() -> jzvd.onInfo(what, extra));
+        return false;
+    }
+
+    @Override
+    public void onBufferingUpdate(IMediaPlayer iMediaPlayer, final int percent) {
+        handler.post(() -> jzvd.setBufferProgress(percent));
+    }
+
+    @Override
+    public void onSeekComplete(IMediaPlayer iMediaPlayer) {
+        handler.post(() -> jzvd.onSeekComplete());
+    }
+
+    @Override
+    public void onTimedText(IMediaPlayer iMediaPlayer, IjkTimedText ijkTimedText) {
+
+    }
+
+    @Override
+    public void setSurface(Surface surface) {
+        ijkMediaPlayer.setSurface(surface);
+    }
+
+    @Override
+    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+        if (SAVED_SURFACE == null) {
+            SAVED_SURFACE = surface;
+            prepare();
+        } else {
+            jzvd.textureView.setSurfaceTexture(SAVED_SURFACE);
+        }
+    }
+
+    @Override
+    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+
+    }
+
+    @Override
+    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+        return false;
+    }
+
+    @Override
+    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+
+    }
+
+    @Override
+    public void onCompletion(IMediaPlayer iMediaPlayer) {
+        handler.post(() -> jzvd.onCompletion());
+    }
+}

+ 16 - 0
app/src/main/java/com/edufound/reader/videoutil/OnViewPagerListener.java

@@ -0,0 +1,16 @@
+package com.edufound.reader.videoutil;
+
+
+public interface OnViewPagerListener {
+
+    /*初始化完成*/
+    void onInitComplete();
+
+    /*释放的监听*/
+    void onPageRelease(boolean isNext, int position);
+
+    /*选中的监听以及判断是否滑动到底部*/
+    void onPageSelected(int position, boolean isBottom);
+
+
+}

+ 133 - 0
app/src/main/java/com/edufound/reader/videoutil/ViewPagerLayoutManager.java

@@ -0,0 +1,133 @@
+package com.edufound.reader.videoutil;
+
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.PagerSnapHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+
+public class ViewPagerLayoutManager extends LinearLayoutManager {
+    private static final String TAG = "ViewPagerLayoutManager";
+    private PagerSnapHelper mPagerSnapHelper;
+    private OnViewPagerListener mOnViewPagerListener;
+    private RecyclerView mRecyclerView;
+    private int mDrift;//位移,用来判断移动方向
+    private RecyclerView.OnChildAttachStateChangeListener mChildAttachStateChangeListener = new RecyclerView.OnChildAttachStateChangeListener() {
+        @Override
+        public void onChildViewAttachedToWindow(View view) {
+            if (mOnViewPagerListener != null && getChildCount() == 1) {
+                mOnViewPagerListener.onInitComplete();
+            }
+        }
+
+        @Override
+        public void onChildViewDetachedFromWindow(View view) {
+            if (mDrift >= 0) {
+                if (mOnViewPagerListener != null)
+                    mOnViewPagerListener.onPageRelease(true, getPosition(view));
+            } else {
+                if (mOnViewPagerListener != null)
+                    mOnViewPagerListener.onPageRelease(false, getPosition(view));
+            }
+
+        }
+    };
+
+    public ViewPagerLayoutManager(Context context, int orientation) {
+        super(context, orientation, false);
+        init();
+    }
+
+    public ViewPagerLayoutManager(Context context, int orientation, boolean reverseLayout) {
+        super(context, orientation, reverseLayout);
+        init();
+    }
+
+    private void init() {
+        mPagerSnapHelper = new PagerSnapHelper();
+
+    }
+
+    @Override
+    public void onAttachedToWindow(RecyclerView view) {
+        super.onAttachedToWindow(view);
+        mPagerSnapHelper.attachToRecyclerView(view);
+        this.mRecyclerView = view;
+        mRecyclerView.addOnChildAttachStateChangeListener(mChildAttachStateChangeListener);
+    }
+
+    @Override
+    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+        super.onLayoutChildren(recycler, state);
+    }
+
+
+    @Override
+    public boolean canScrollHorizontally() {
+        return false;
+    }
+
+    /**
+     * 滑动状态的改变
+     * 缓慢拖拽-> SCROLL_STATE_DRAGGING
+     * 快速滚动-> SCROLL_STATE_SETTLING
+     * 空闲状态-> SCROLL_STATE_IDLE
+     *
+     * @param state
+     */
+    @Override
+    public void onScrollStateChanged(int state) {
+        switch (state) {
+            case RecyclerView.SCROLL_STATE_IDLE:
+                View viewIdle = mPagerSnapHelper.findSnapView(this);
+                if (viewIdle != null) {
+                    int positionIdle = getPosition(viewIdle);
+                    if (mOnViewPagerListener != null && getChildCount() == 1) {
+                        mOnViewPagerListener.onPageSelected(positionIdle, positionIdle == getItemCount() - 1);
+                    }
+                }
+                break;
+        }
+    }
+
+    /**
+     * 监听竖直方向的相对偏移量
+     *
+     * @param dy
+     * @param recycler
+     * @param state
+     * @return
+     */
+    @Override
+    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
+        this.mDrift = dy;
+        return super.scrollVerticallyBy(dy, recycler, state);
+    }
+
+    /**
+     * 监听水平方向的相对偏移量
+     *
+     * @param dx
+     * @param recycler
+     * @param state
+     * @return
+     */
+    @Override
+    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
+        this.mDrift = dx;
+        return super.scrollHorizontallyBy(dx, recycler, state);
+//        return 0;
+    }
+
+    /**
+     * 设置监听
+     *
+     * @param listener
+     */
+    public void setOnViewPagerListener(OnViewPagerListener listener) {
+        this.mOnViewPagerListener = listener;
+    }
+}

+ 0 - 50
app/src/main/java/com/efunbox/reader/activity/MainActivity.java

@@ -1,50 +0,0 @@
-package com.efunbox.reader.activity;
-
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.efunbox.reader.R;
-import com.efunbox.reader.annotation.BindView;
-import com.efunbox.reader.base.BaseMvpActivity;
-import com.efunbox.reader.contract.MainContract;
-import com.efunbox.reader.presenter.MainPresenter;
-import com.efunbox.reader.util.GlideUtils;
-
-public class MainActivity extends BaseMvpActivity<MainPresenter> implements MainContract.View {
-
-
-    @BindView(id = R.id.text_test)
-    TextView mText;
-
-    @BindView(id = R.id.image_test)
-    ImageView mImage;
-
-
-    @Override
-    public int getLayoutId() {
-        return R.layout.activity_main;
-    }
-
-    @Override
-    public void initView() {
-        mPresenter = new MainPresenter();
-        mPresenter.attachView(this);
-        mText.setText("测试一下bindview");
-        GlideUtils.loadImageSizeKipMemoryCache(this, "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.zhimg.com%2F50%2Fv2-a96199f59f76eef6698e572c19944e40_hd.jpg&refer=http%3A%2F%2Fpic1.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1640338680&t=511cd171a44a99e646ce6063b751c78f", mImage);
-    }
-
-    @Override
-    public void showLoading() {
-
-    }
-
-    @Override
-    public void hideLoading() {
-
-    }
-
-    @Override
-    public void onError(String errMessage) {
-
-    }
-}

+ 0 - 20
app/src/main/java/com/efunbox/reader/contract/MainContract.java

@@ -1,20 +0,0 @@
-package com.efunbox.reader.contract;
-
-import com.efunbox.reader.base.BaseView;
-
-public interface MainContract {
-    interface Model {
-    }
-
-    interface View extends BaseView {
-    }
-
-    interface Presenter {
-        /**
-         * 是否登录
-         *
-         * @param deviceCode
-         */
-        void isLogin(String deviceCode);
-    }
-}

+ 0 - 6
app/src/main/java/com/efunbox/reader/model/MainModel.java

@@ -1,6 +0,0 @@
-package com.efunbox.reader.model;
-
-import com.efunbox.reader.contract.MainContract;
-
-public class MainModel implements MainContract.Model {
-}

+ 0 - 6
app/src/main/java/com/efunbox/reader/model/MainModelImpl.java

@@ -1,6 +0,0 @@
-package com.efunbox.reader.model;
-
-import com.efunbox.reader.contract.MainContract;
-
-public class MainModelImpl implements MainContract.Model {
-}

+ 0 - 11
app/src/main/java/com/efunbox/reader/presenter/MainPresenter.java

@@ -1,11 +0,0 @@
-package com.efunbox.reader.presenter;
-
-import com.efunbox.reader.base.BasePresenter;
-import com.efunbox.reader.contract.MainContract;
-
-public class MainPresenter extends BasePresenter implements MainContract.Presenter {
-    @Override
-    public void isLogin(String deviceCode) {
-        
-    }
-}

+ 0 - 10
app/src/main/java/com/efunbox/reader/presenter/MainPresenterImpl.java

@@ -1,10 +0,0 @@
-package com.efunbox.reader.presenter;
-
-import com.efunbox.reader.contract.MainContract;
-
-public class MainPresenterImpl implements MainContract.Presenter {
-    @Override
-    public void isLogin(String deviceCode) {
-        
-    }
-}

+ 0 - 15
app/src/main/java/com/efunbox/reader/util/Consts.java

@@ -1,15 +0,0 @@
-package com.efunbox.reader.util;
-
-import android.app.Application;
-
-public class Consts {
-    public static Application getmApplicAtion() {
-        return mApplicAtion;
-    }
-
-    public static void setmApplicAtion(Application mApplicAtion) {
-        Consts.mApplicAtion = mApplicAtion;
-    }
-
-    private static Application mApplicAtion;
-}

+ 28 - 0
app/src/main/res/drawable/cusjzplayer_controller_seekbar.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@android:id/background">
+        <shape>
+            <solid android:color="#a5ffffff" />
+            <size android:height="10dp" />
+            <corners android:radius="0dp" />
+        </shape>
+    </item>
+    <item android:id="@android:id/secondaryProgress">
+        <clip>
+            <shape>
+                <solid android:color="#a5ffffff" />
+                <size android:height="3dp" />
+                <corners android:radius="0dp" />
+            </shape>
+        </clip>
+    </item>
+    <item android:id="@android:id/progress">
+        <clip>
+            <shape>
+                <solid android:color="@color/colorAccent" />
+                <size android:height="10dp" />
+                <corners android:radius="0dp" />
+            </shape>
+        </clip>
+    </item>
+</layer-list>

BIN
app/src/main/res/drawable/edufound_player_pause.png


BIN
app/src/main/res/drawable/edufound_player_play.png


BIN
app/src/main/res/drawable/loading.png


BIN
app/src/main/res/drawable/player_back.png


BIN
app/src/main/res/drawable/player_little_icon_pause.png


BIN
app/src/main/res/drawable/player_little_icon_play.png


+ 34 - 0
app/src/main/res/drawable/player_seekbar_list.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@android:id/background">
+        <shape>
+            <corners android:radius="5dp" />
+            <gradient
+                android:centerColor="@color/white"
+                android:endColor="@color/white"
+                android:startColor="@color/white" />
+        </shape>
+    </item>
+    <item android:id="@android:id/secondaryProgress">
+        <clip>
+            <shape>
+                <corners android:radius="5dp" />
+                <gradient
+                    android:centerColor="@color/transparent"
+                    android:endColor="@color/transparent"
+                    android:startColor="@color/transparent" />
+            </shape>
+        </clip>
+    </item>
+    <item android:id="@android:id/progress">
+        <clip>
+            <shape>
+                <corners android:radius="5dp" />
+                <gradient
+                    android:centerColor="#FF29D70E"
+                    android:endColor="#FF29D70E"
+                    android:startColor="#FF29D70E" />
+            </shape>
+        </clip>
+    </item>
+</layer-list>

BIN
app/src/main/res/drawable/seekbar_thumb.png


+ 32 - 13
app/src/main/res/layout/activity_main.xml

@@ -3,18 +3,37 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-    <TextView
-            android:id="@+id/text_test"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+
+    <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/main_recyclerview"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical"></androidx.recyclerview.widget.RecyclerView>
+
+
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
             android:layout_gravity="center"
-            android:gravity="center"
-            android:text="123"
-            android:textColor="@color/black"
-            android:textSize="30sp"></TextView>
-
-    <ImageView
-            android:id="@+id/image_test"
-            android:layout_width="300dp"
-            android:layout_height="300dp"></ImageView>
+            android:gravity="center">
+
+        <Button
+                android:id="@+id/main_initrecord"
+                android:layout_width="wrap_content"
+                android:layout_height="100dp"
+                android:text="开始录音"></Button>
+
+        <Button
+                android:id="@+id/main_stoprecord"
+                android:layout_width="wrap_content"
+                android:layout_height="100dp"
+                android:text="停止录音"></Button>
+
+
+        <Button
+                android:id="@+id/main_playrecord"
+                android:layout_width="wrap_content"
+                android:layout_height="100dp"
+                android:text="播放录音"></Button>
+    </LinearLayout>
 </FrameLayout>

+ 7 - 0
app/src/main/res/layout/activity_video.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/player_frame"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+</FrameLayout>

+ 10 - 0
app/src/main/res/layout/adapter_item_slidevideo.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <com.edufound.reader.cusview.RvListJzvdStd
+            android:id="@+id/slidevideo_video"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"></com.edufound.reader.cusview.RvListJzvdStd>
+</FrameLayout>

+ 255 - 0
app/src/main/res/layout/cusjzplayer_controller.xml

@@ -0,0 +1,255 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/black"
+        android:descendantFocusability="afterDescendants">
+
+    <FrameLayout
+            android:id="@+id/surface_container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+    </FrameLayout>
+
+    <ImageView
+            android:id="@+id/poster"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_alignParentStart="true"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentBottom="true"
+            android:adjustViewBounds="true"
+            android:background="#000000"
+            android:scaleType="fitXY" />
+
+    <LinearLayout
+            android:id="@+id/layout_bottom"
+            android:layout_width="match_parent"
+            android:layout_height="50dp"
+            android:layout_alignParentBottom="true"
+            android:background="@drawable/jz_bottom_bg"
+            android:gravity="center_vertical"
+            android:orientation="horizontal"
+            android:visibility="invisible">
+
+        <TextView
+                android:id="@+id/current"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="14dp"
+                android:text="00:00"
+                android:textColor="#ffffff"
+                android:visibility="invisible" />
+
+        <SeekBar
+                android:id="@+id/bottom_seek_progress"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_weight="1.0"
+                android:background="@null"
+                android:max="100"
+                android:maxHeight="2dp"
+                android:minHeight="2dp"
+                android:paddingLeft="12dp"
+                android:paddingTop="8dp"
+                android:paddingRight="12dp"
+                android:paddingBottom="8dp"
+                android:progressDrawable="@drawable/cusjzplayer_controller_seekbar"
+                android:thumb="@drawable/jz_bottom_seek_poster" />
+
+        <TextView
+                android:id="@+id/total"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="00:00"
+                android:textColor="#ffffff"
+                android:visibility="invisible" />
+
+        <TextView
+                android:id="@+id/clarity"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:clickable="true"
+                android:paddingLeft="20dp"
+                android:text="clarity"
+                android:textAlignment="center"
+                android:textColor="#ffffff"
+                android:visibility="invisible" />
+
+        <ImageView
+                android:id="@+id/fullscreen"
+                android:layout_width="52.5dp"
+                android:layout_height="fill_parent"
+                android:paddingLeft="14dp"
+                android:paddingRight="14dp"
+                android:scaleType="centerInside"
+                android:src="@drawable/jz_enlarge"
+                android:visibility="invisible" />
+    </LinearLayout>
+
+    <ProgressBar
+            android:id="@+id/bottom_progress"
+            style="?android:attr/progressBarStyleHorizontal"
+            android:layout_width="match_parent"
+            android:layout_height="3dp"
+            android:layout_alignParentBottom="true"
+            android:max="100"
+            android:progressDrawable="@drawable/cusjzplayer_controller_seekbar" />
+
+    <ImageView
+            android:id="@+id/back_tiny"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_marginLeft="6dp"
+            android:layout_marginTop="6dp"
+            android:background="@drawable/jz_click_back_tiny_selector"
+            android:visibility="gone" />
+
+    <RelativeLayout
+            android:id="@+id/layout_top"
+            android:layout_width="match_parent"
+            android:layout_height="48dp"
+            android:layout_alignParentStart="true"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentTop="true"
+            android:background="@drawable/jz_title_bg"
+            android:paddingStart="10dp"
+            android:paddingLeft="10dp"
+            android:visibility="gone">
+
+        <ImageView
+                android:id="@+id/back"
+                android:layout_width="26dp"
+                android:layout_height="26dp"
+                android:layout_alignParentStart="true"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentTop="true"
+                android:layout_marginTop="12dp"
+                android:padding="3dp"
+                android:scaleType="centerInside"
+                android:src="@drawable/jz_click_back_selector"
+                android:visibility="gone" />
+
+
+        <TextView
+                android:id="@+id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                android:layout_marginEnd="12dp"
+                android:layout_marginRight="12dp"
+                android:layout_toLeftOf="@+id/battery_time_layout"
+                android:layout_toEndOf="@+id/back"
+                android:layout_toRightOf="@+id/back"
+                android:ellipsize="end"
+                android:maxLines="2"
+                android:textColor="#ffffff"
+                android:textSize="18sp" />
+
+
+        <LinearLayout
+                android:id="@+id/battery_time_layout"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:layout_alignParentRight="true"
+                android:layout_centerVertical="true"
+                android:layout_marginEnd="14dp"
+                android:layout_marginRight="14dp"
+                android:gravity="center_vertical"
+                android:orientation="vertical"
+                android:visibility="invisible">
+
+            <ImageView
+                    android:id="@+id/battery_level"
+                    android:layout_width="23dp"
+                    android:layout_height="10dp"
+                    android:layout_gravity="center_horizontal"
+                    android:background="@drawable/jz_battery_level_10"
+                    android:visibility="invisible" />
+
+            <TextView
+                    android:id="@+id/video_current_time"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"
+                    android:gravity="center_vertical"
+                    android:maxLines="1"
+                    android:textColor="#ffffffff"
+                    android:textSize="12.0sp"
+                    android:visibility="invisible" />
+        </LinearLayout>
+    </RelativeLayout>
+
+    <ProgressBar
+            android:id="@+id/loading"
+            android:layout_width="@dimen/jz_start_button_w_h_normal"
+            android:layout_height="@dimen/jz_start_button_w_h_normal"
+            android:layout_centerHorizontal="true"
+            android:layout_centerVertical="true"
+            android:indeterminateDrawable="@drawable/jz_loading"
+            android:visibility="invisible" />
+
+    <LinearLayout
+            android:id="@+id/start_layout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:layout_centerVertical="true"
+            android:layout_gravity="center_vertical">
+
+        <ImageView
+                android:id="@+id/start"
+                android:layout_width="80dp"
+                android:layout_height="80dp"
+                android:src="@drawable/jz_click_play_selector" />
+    </LinearLayout>
+
+
+    <TextView
+            android:id="@+id/replay_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/start_layout"
+            android:layout_centerHorizontal="true"
+            android:layout_marginTop="6dp"
+            android:text="@string/replay"
+            android:textColor="#ffffff"
+            android:textSize="12sp"
+            android:visibility="invisible" />
+
+
+    <LinearLayout
+            android:id="@+id/retry_layout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:layout_centerVertical="true"
+            android:gravity="center_horizontal"
+            android:orientation="vertical"
+            android:visibility="invisible">
+
+        <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/video_loading_failed"
+                android:textColor="@android:color/white"
+                android:textSize="14sp" />
+
+        <TextView
+                android:id="@+id/retry_btn"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="15dp"
+                android:background="@drawable/jz_retry"
+                android:paddingLeft="9dp"
+                android:paddingTop="4dp"
+                android:paddingRight="9dp"
+                android:paddingBottom="4dp"
+                android:text="@string/click_to_restart"
+                android:textColor="@android:color/white"
+                android:textSize="14sp" />
+    </LinearLayout>
+</RelativeLayout>

+ 124 - 0
app/src/main/res/layout/player_controller.xml

@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <LinearLayout
+            android:id="@+id/player_controller_layout"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical">
+
+        <FrameLayout
+                android:layout_width="match_parent"
+                android:layout_height="120dp"
+                android:background="@color/translucent_background">
+
+            <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:gravity="center"
+                    android:orientation="horizontal">
+
+                <ImageView
+                        android:id="@+id/player_controller_back"
+                        android:layout_width="0dp"
+                        android:layout_height="60dp"
+                        android:layout_gravity="center|left"
+                        android:layout_weight="1"
+                        android:src="@drawable/player_back" />
+
+                <TextView
+                        android:id="@+id/player_controller_videoname"
+                        android:layout_width="0dp"
+                        android:layout_height="match_parent"
+                        android:layout_weight="6"
+                        android:gravity="center|left"
+                        android:paddingLeft="20dp"
+                        android:text="123123123"
+                        android:textColor="@color/white"
+                        android:textSize="50dp" />
+            </LinearLayout>
+        </FrameLayout>
+
+        <Space
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="6"></Space>
+
+        <FrameLayout
+                android:layout_width="match_parent"
+                android:layout_height="120dp"
+                android:background="@color/translucent_background">
+
+            <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:layout_marginLeft="30dp"
+                    android:layout_marginRight="30dp"
+                    android:gravity="center"
+                    android:orientation="horizontal">
+
+                <ImageView
+                        android:id="@+id/player_controller_little_icon"
+                        android:layout_width="60dp"
+                        android:layout_height="60dp"
+                        android:layout_gravity="center"
+                        android:layout_weight="1"
+                        android:src="@drawable/player_little_icon_pause" />
+
+
+                <SeekBar
+                        android:id="@+id/player_controller_seekbar"
+                        android:layout_width="300dp"
+                        android:layout_height="match_parent"
+                        android:layout_gravity="center"
+                        android:layout_marginLeft="20dp"
+                        android:layout_marginRight="20dp"
+                        android:layout_weight="5"
+                        android:focusable="false"
+                        android:maxHeight="5dp"
+                        android:minHeight="5dp"
+                        android:progressDrawable="@drawable/player_seekbar_list"
+                        android:thumb="@drawable/seekbar_thumb"
+                        android:thumbOffset="0dp" />
+
+                <TextView
+                        android:id="@+id/player_controller_current"
+                        android:layout_width="0dp"
+                        android:layout_height="match_parent"
+                        android:layout_gravity="center"
+                        android:layout_weight="1"
+                        android:gravity="center"
+                        android:text="00:00"
+                        android:textColor="@color/white"
+                        android:textSize="50dp" />
+
+
+            </LinearLayout>
+        </FrameLayout>
+    </LinearLayout>
+    <!-- 这个FrameLayout 装loading以及播放器状态显示图片或者更多工具-->
+    <FrameLayout
+            android:id="@+id/player_controller_other_frame"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center">
+
+        <ImageView
+                android:id="@+id/player_controller_status"
+                android:layout_width="150dp"
+                android:layout_height="150dp"
+                android:layout_gravity="center"
+                android:src="@drawable/edufound_player_play"
+                android:visibility="gone" />
+
+        <ImageView
+                android:id="@+id/player_controller_loading"
+                android:layout_width="60dp"
+                android:layout_height="60dp"
+                android:layout_gravity="center"
+                android:background="@drawable/loading" />
+    </FrameLayout>
+
+</FrameLayout>

+ 2 - 0
app/src/main/res/values/strings.xml

@@ -1,3 +1,5 @@
 <resources>
     <string name="app_name">朗读配音</string>
+
+
 </resources>

+ 123 - 0
app/src/main/res/values/strings_pref.xml

@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- -->
+    <string name="pref_title_general">General</string>
+
+    <string name="pref_key_enable_background_play">pref.enable_background_play</string>
+    <string name="pref_title_enable_background_play">Enable background play</string>
+    <string name="pref_summary_enable_background_play">need Android 4.0+</string>
+
+    <string name="pref_key_using_android_player">pref.using_android_player</string>
+    <string name="pref_title_using_android_player">Using system player</string>
+    <string name="pref_summary_using_android_player"></string>
+
+    <string name="pref_key_player">pref.player</string>
+    <string name="pref_title_player">Choose Player</string>
+    <string-array name="pref_entries_player">
+        <item>Auto Select</item>
+        <item>AndroidMediaPlayer</item>
+        <item>IjkMediaPlayer</item>
+        <item>IjkExoMediaPlayer</item>
+    </string-array>
+    <string-array name="pref_entry_values_player">
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        <item>3</item>
+    </string-array>
+    <string-array name="pref_entry_summaries_player">
+        <item>Auto Select</item>
+        <item>AndroidMediaPlayer</item>
+        <item>IjkMediaPlayer</item>
+        <item>IjkExoMediaPlayer</item>
+    </string-array>
+
+    <!-- -->
+    <string name="pref_title_ijkplayer_video">Video: ijkplayer</string>
+
+    <string name="pref_key_using_media_codec">pref.using_media_codec</string>
+    <string name="pref_title_using_media_codec">Using MediaCodec</string>
+    <string name="pref_summary_using_media_codec"></string>
+
+    <string name="pref_key_using_media_codec_auto_rotate">pref.using_media_codec_auto_rotate</string>
+    <string name="pref_title_using_media_codec_auto_rotate">Using MediaCodec auto rotate</string>
+    <string name="pref_summary_using_media_codec_auto_rotate"></string>
+
+    <string name="pref_key_media_codec_handle_resolution_change">pref.media_codec_handle_resolution_change</string>
+    <string name="pref_title_media_codec_handle_resolution_change">MediaCodec handle resolution change</string>
+    <string name="pref_summary_media_codec_handle_resolution_change"></string>
+
+    <string name="pref_key_pixel_format">pref.pixel_format</string>
+    <string name="pref_title_pixel_format">Pixel Format</string>
+    <string-array name="pref_entries_pixel_format">
+        <item>Auto Select</item>
+        <item>RGB 565</item>
+        <item>RGB 888</item>
+        <item>RGBX 8888</item>
+        <item>YV12</item>
+        <item>OpenGL ES2</item>
+    </string-array>
+    <string-array name="pref_entry_values_pixel_format">
+        <item></item>
+        <item>fcc-rv16</item>
+        <item>fcc-rv24</item>
+        <item>fcc-rv32</item>
+        <item>fcc-yv12</item>
+        <item>fcc-_es2</item>
+    </string-array>
+    <string-array name="pref_entry_summaries_pixel_format">
+        <item>Auto Select</item>
+        <item>RGB 565</item>
+        <item>RGB 888</item>
+        <item>RGBX 8888</item>
+        <item>YV12</item>
+        <item>OpenGL ES2</item>
+    </string-array>
+
+    <!-- -->
+    <string name="pref_title_ijkplayer_audio">Audio: ijkplayer</string>
+
+    <string name="pref_key_using_opensl_es">pref.using_opensl_es</string>
+    <string name="pref_title_using_opensl_es">Using OpenSL ES</string>
+    <string name="pref_summary_using_opensl_es"></string>
+
+    <!-- -->
+    <string name="pref_title_render_view">RenderView</string>
+
+    <string name="pref_key_enable_no_view">pref.enable_no_view</string>
+    <string name="pref_title_enable_no_view">Enable NoView</string>
+    <string name="pref_summary_enable_no_view"></string>
+
+    <string name="pref_key_enable_surface_view">pref.enable_surface_view</string>
+    <string name="pref_title_enable_surface_view">Enable SurfaceView</string>
+    <string name="pref_summary_enable_surface_view"></string>
+
+    <string name="pref_key_enable_texture_view">pref.enable_texture_view</string>
+    <string name="pref_title_enable_texture_view">Enable TextureView</string>
+    <string name="pref_summary_enable_texture_view"></string>
+
+    <string name="pref_key_enable_detached_surface_texture">pref.enable_detached_surface_texture</string>
+    <string name="pref_title_enable_detached_surface_texture">Enable detached SurfaceTexture</string>
+    <string name="pref_summary_enable_detached_surface_texture"></string>
+
+    <!-- -->
+    <string name="pref_title_misc">Misc</string>
+    <string name="pref_key_using_mediadatasource">pref.using_mediadatasource</string>
+    <string name="pref_title_using_mediadatasource">Using MediaDataSource</string>
+    <string name="pref_summary_using_mediadatasource"></string>
+
+    <!-- -->
+    <string name="pref_key_last_directory"></string>
+
+    <!-- ijkplayer strings-->
+    <string name="VideoView_error_text_invalid_progressive_playback">Invalid progressive playback</string>
+    <string name="VideoView_error_text_unknown">Unknown</string>
+    <string name="VideoView_error_button">OK</string>
+    <string name="TrackType_video">Video</string>
+    <string name="TrackType_audio">Audio</string>
+    <string name="TrackType_subtitle">Subtitle</string>
+    <string name="TrackType_timedtext">Timed text</string>
+    <string name="TrackType_metadata">Meta data</string>
+    <string name="TrackType_unknown">Unknown</string>
+
+</resources>

+ 1 - 1
app/src/test/java/com/efunbox/reader/ExampleUnitTest.java

@@ -1,4 +1,4 @@
-package com.efunbox.reader;
+package com.edufound.reader;
 
 import org.junit.Test;