Sfoglia il codice sorgente

增加了锁屏的功能和网络监听广播

yangchong211 7 anni fa
parent
commit
3457f47fce

+ 25 - 12
README.md

@@ -42,6 +42,7 @@
 - 9.4 v2.0.0 更新于2018年1月18日
 - 9.5 v2.4.5 更新于2018年4月21日
 - 9.6 v2.4.6 更新于2018年8月2日
+- 9.7 v2.4.8 更新于2018年8月12日
 - **10.关于参考文档说明**
 - 10.1 参考的项目
 - 10.2 参考的博客
@@ -64,18 +65,22 @@
 ### 1.关于此视频封装库介绍
 #### 1.1 能够满足那些业务需求
 ##### A基础功能
-- 1.1.1 能够自定义视频加载loading类型,设置视频标题,设置视频底部图片,设置播放时长等基础功能
-- 1.1.2 可以切换播放器的视频播放状态,播放错误,播放未开始,播放开始,播放准备中,正在播放,暂停播放,正在缓冲等等状态
-- 1.1.3 可以自由设置播放器的播放模式,比如,正常播放,全屏播放,和小屏幕播放。其中全屏播放支持旋转屏幕。
-- 1.1.4 可以支持多种视频播放类型,比如,原生封装视频播放器,还有基于ijkplayer封装的播放器。
-- 1.1.5 可以设置是否隐藏播放音量,播放进度,播放亮度等,可以通过拖动seekBar改变视频进度。还支持设置n秒后不操作则隐藏头部和顶部布局功能
-- 1.1.6 可以设置竖屏模式下全屏模式和横屏模式下的全屏模式,方便多种使用场景
+- A.1.1 能够自定义视频加载loading类型,设置视频标题,设置视频底部图片,设置播放时长等基础功能
+- A.1.2 可以切换播放器的视频播放状态,播放错误,播放未开始,播放开始,播放准备中,正在播放,暂停播放,正在缓冲等等状态
+- A.1.3 可以自由设置播放器的播放模式,比如,正常播放,全屏播放,和小屏幕播放。其中全屏播放支持旋转屏幕。
+- A.1.4 可以支持多种视频播放类型,比如,原生封装视频播放器,还有基于ijkPlayer封装的播放器。
+- A.1.5 可以设置是否隐藏播放音量,播放进度,播放亮度等,可以通过拖动seekBar改变视频进度。还支持设置n秒后不操作则隐藏头部和顶部布局功能
+- A.1.6 可以设置竖屏模式下全屏模式和横屏模式下的全屏模式,方便多种使用场景
+- A.1.7 top和bottom面版消失和显示:点击视频画面会显示、隐藏操作面板;显示后不操作会5秒后自动消失【也可以设置】
 ##### B高级功能
-- 1.1.6 支持一遍播放一遍缓冲的功能,其中缓冲包括两部分,第一种是播放过程中缓冲,第二种是暂停过程中缓冲
-- 1.1.7 基于ijkPlayer的封装播放器,支持多种格式视频播放
-- 1.1.8 可以设置是否记录播放位置,设置播放速度,设置屏幕比例
-- 1.1.9 支持滑动改变音量【屏幕右边】,改变屏幕亮度【屏幕左边】,支持切换视频清晰度模式
-- 1.1.0 支持list页面中视频播放,滚动后暂停播放,播放可以自由设置是否记录状态。并且还支持删除视频播放位置状态。
+- B.1.1 支持一遍播放一遍缓冲的功能,其中缓冲包括两部分,第一种是播放过程中缓冲,第二种是暂停过程中缓冲
+- B.1.2 基于ijkPlayer的封装播放器,支持多种格式视频播放
+- B.1.3 可以设置是否记录播放位置,设置播放速度,设置屏幕比例
+- B.1.4 支持滑动改变音量【屏幕右边】,改变屏幕亮度【屏幕左边】,屏幕底测左右滑动调节进度
+- B.1.5 支持list页面中视频播放,滚动后暂停播放,播放可以自由设置是否记录状态。并且还支持删除视频播放位置状态。
+- B.1.6 切换横竖屏:切换全屏时,隐藏状态栏,显示自定义top(显示电量);竖屏时恢复原有状态
+- B.1.7 支持切换视频清晰度模式
+- B.1.8 添加锁屏功能,竖屏不提供锁屏按钮,横屏全屏时显示,并且锁屏时,屏蔽手势处理
 ##### C拓展功能【这块根据实际情况选择是否需要使用,一般视频付费App会有这个工鞥】
 - **C1产品需求:类似优酷,爱奇艺视频播放器部分逻辑。比如如果用户没有登录也没有看视频权限,则提示试看视频[自定义布局];如果用户没有登录但是有看视频权限,则正常观看;如果用户登录,但是没有充值会员,部分需要权限视频则进入试看模式,试看结束后弹出充值会员界面;如果用户余额不足,比如余额只有99元,但是视频观看要199元,则又有其他提示。**
 - C2自身需求:比如封装好了视频播放库,那么点击视频上登录按钮则跳到登录页面;点击充值会员页面也跳到充值页面。这个通过定义接口,可以让使用者通过方法调用,灵活处理点击事件。
@@ -1044,9 +1049,17 @@ public class VideoPlayerManager {
 - 9.5.1 触摸滑动事件中,优化了只有全屏的时候才能拖动位置、亮度、声音
 - 9.5.2 优化了只有在播放,暂停,缓冲的时候才能改变亮度,声音,和拖动位置
 - 9.5.3 滑动改变亮度,声音和拖动位置时,隐藏控制器中间播放位置变化图,亮度变化视图和音量变化视图
-##### 9.6 v1.3.0 更新于2018年8月2日
+##### 9.6 v2.4.6 更新于2018年8月2日
 - 9.6.1 添加了竖屏下的全屏播放模式
 - 9.6.2 解决了横屏下全屏播放模式的导航栏显示问题
+##### 9.7 v2.4.7 更新于2018年8月12日
+- 9.7.1 添加了锁屏的功能,锁屏时,返回键不做任何处理,并且隐藏top和bottom面版控件
+- 9.7.2 优化了全屏播放视频时,左右滑动可以设置快进和快退的功能
+- 9.7.3 优化了播放视频中,没有网络,点击重试按钮提示用户检查网络是否异常吐司
+- 9.7.4 注册一个网络变化监听广播,在网络变更时进行对应处理,从有网切换到没有网络时,切换播放状态
+- 9.7.5 修改播放异常条件下,还有声音播放的问题
+
+
 
 
 ### 10.关于参考文档说明

+ 1 - 1
YCVideoPlayerLib/build.gradle

@@ -51,7 +51,7 @@ group = "cn.yc"
 //发布到JCenter上的项目名字,必须填写
 def libName = "YCVideoPlayerLib"
 // 版本号,下次更新是只需要更改版本号即可
-version = "2.4.6"
+version = "2.4.8"
 /**  上面配置后上传至jcenter后的编译路径是这样的: compile 'cn.yc:YCVideoPlayerLib:2.4'  **/
 
 //生成源文件

+ 123 - 4
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/AbsVideoPlayerController.java

@@ -2,6 +2,8 @@ package org.yczbj.ycvideoplayerlib;
 
 import android.content.Context;
 import android.support.annotation.DrawableRes;
+import android.util.DisplayMetrics;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.WindowManager;
@@ -38,14 +40,24 @@ import java.util.concurrent.ScheduledExecutorService;
  */
 public abstract class AbsVideoPlayerController extends FrameLayout implements View.OnTouchListener {
 
+    private final DisplayMetrics dm;
     private Context mContext;
     protected InterVideoPlayer mVideoPlayer;
     private Timer mUpdateProgressTimer;
     private TimerTask mUpdateProgressTimerTask;
     private float mDownX;
     private float mDownY;
+    /**
+     * 是否需要改变播放的进度
+     */
     private boolean mNeedChangePosition;
+    /**
+     * 是否需要改变播放的声音
+     */
     private boolean mNeedChangeVolume;
+    /**
+     * 是否需要改变播放的亮度
+     */
     private boolean mNeedChangeBrightness;
     private static final int THRESHOLD = 80;
     private long mGestureDownPosition;
@@ -59,6 +71,7 @@ public abstract class AbsVideoPlayerController extends FrameLayout implements Vi
         super(context);
         mContext = context;
         this.setOnTouchListener(this);
+        dm = new DisplayMetrics();//获取屏幕宽高,处理越界的时候用到
     }
 
     public void setVideoPlayer(InterVideoPlayer videoPlayer) {
@@ -240,6 +253,7 @@ public abstract class AbsVideoPlayerController extends FrameLayout implements Vi
      */
     protected abstract void updateProgress();
 
+
     /**
      * 滑动处理调节声音和亮度的逻辑
      * @param v                         v
@@ -248,6 +262,112 @@ public abstract class AbsVideoPlayerController extends FrameLayout implements Vi
      */
     @Override
     public boolean onTouch(View v, MotionEvent event) {
+        //不能用这个做判断,如果是小窗口播放状态,那么这个返回时false
+        //boolean tinyWindow = mVideoPlayer.isTinyWindow();
+        int playType = mVideoPlayer.getPlayType();
+        //如果是小窗口模式,则可以拖拽。其他情况则正常处理
+        if(playType == VideoPlayer.PlayMode.MODE_TINY_WINDOW){
+            return setTinyWindowTouch(v,event);
+        }else {
+            //处理全屏播放时,滑动处理调节声音和亮度的逻辑
+            return setOnTouch(v,event);
+        }
+    }
+
+
+    /**
+     * 如果是小窗口模式,则可以拖拽。其他情况则正常处理
+     * 问题:
+     * 1.按钮拖动的界限限定。
+     * 2.按钮单击和拖动之间的冲突。
+     * 3.在界面未显示之前,获得View的高/宽。
+     * @param v                         v
+     * @param event                     event
+     * @return                          是否自己处理滑动事件
+     */
+    private boolean setTinyWindowTouch(View v, MotionEvent event) {
+        switch (event.getAction() & MotionEvent.ACTION_MASK){
+            case MotionEvent.ACTION_DOWN:
+                onTouchDown(event);
+                break;
+            case MotionEvent.ACTION_MOVE:
+                onTouchMove(v,event);
+                break;
+            case MotionEvent.ACTION_UP:
+                isOver(v);
+                break;
+        }
+        //v.invalidate();
+        return true;
+    }
+
+    //相对于父控件的触摸位置,用于处理拖拽
+    private float xDown,yDown,xUp,yUp;
+    private int extra;
+
+    /** 按下 **/
+    private void onTouchDown(MotionEvent event){
+        xDown = event.getX();
+        yDown = event.getY();
+        xUp = event.getRawX();
+        yUp = event.getRawY();
+        extra = (int) (yUp - yDown - getTop());
+        VideoLogUtil.i("AbsPlayer"+"onTouchDown"+xDown+"----"+yDown+"----"+xUp+"----"+yUp+"----"+extra);
+    }
+
+
+    /** 拖拽 **/
+    private void onTouchMove(View view, MotionEvent event) {
+        int left, right, top, bottom;
+        top = (int) (yUp - yDown) - extra;
+        bottom = top + getHeight();
+        left = (int) (xUp - xDown);
+        right = left + getWidth();
+        //Top position, relative to parent这是该方法其中top参数的官方解释,反正我是被误导了,我不知道这个父亲到底是怎样定义的
+        // 我目前的理解是它的所有参数位置是相对于该view放置的那个xml布局的位置,那个xml布局最外面的那个layout才是父view。
+        //为什么要说的这么绕,就是它的位置不是相对屏幕的,因为你的应用可能有tab占了位置,那块位置就不能算。如果理解了这些话就能知道为什么会有extra了。
+        view.layout(left, top, right, bottom);
+        xUp = event.getRawX();
+        yUp = event.getRawY();
+        VideoLogUtil.i("AbsPlayer"+"onTouchMove"+left+"----"+top+"----"+right+"----"+bottom);
+    }
+
+    /**
+     * 拖拽时判断是否越界
+     */
+    private void isOver(View v) {
+        int width = this.getWidth()/3;
+        int height = this.getHeight()/3;
+        int left = getLeft(),right=getRight(),top=getTop(),bottom=getBottom();
+        //针对整个可用屏幕的越界,必须还能看到控件的1/3
+        if (this.getBottom() < height){
+            bottom = height;
+            top = bottom - getHeight();
+        }
+        if (this.getRight() <  width){
+            right = width;
+            left = right - getWidth();
+        }
+        if (this.getTop() > dm.heightPixels - extra - height){
+            top = dm.heightPixels - extra - height;
+            bottom = top + getHeight();
+        }
+        if (this.getLeft() > dm.widthPixels - width){
+            left = dm.widthPixels - width;
+            right = left + getWidth();
+        }
+        if (this.getBottom() < height || this.getLeft() < - width || this.getRight() > dm.widthPixels - width || this.getTop() > dm.heightPixels - extra - height) {
+            v.layout(left, top, right, bottom);
+        }
+    }
+
+    /**
+     * 处理全屏播放时,滑动处理调节声音和亮度的逻辑
+     * @param v                         v
+     * @param event                     event
+     * @return                          是否自己处理滑动事件
+     */
+    private boolean setOnTouch(View v, MotionEvent event) {
         // 只有全屏的时候才能拖动位置、亮度、声音
         if (!mVideoPlayer.isFullScreen()) {
             return false;
@@ -312,8 +432,7 @@ public abstract class AbsVideoPlayerController extends FrameLayout implements Vi
                     float newBrightness = mGestureDownBrightness + deltaBrightness;
                     newBrightness = Math.max(0, Math.min(newBrightness, 1));
                     float newBrightnessPercentage = newBrightness;
-                    WindowManager.LayoutParams params = VideoPlayerUtils.scanForActivity(mContext)
-                            .getWindow().getAttributes();
+                    WindowManager.LayoutParams params = VideoPlayerUtils.scanForActivity(mContext).getWindow().getAttributes();
                     params.screenBrightness = newBrightnessPercentage;
                     VideoPlayerUtils.scanForActivity(mContext).getWindow().setAttributes(params);
                     int newBrightnessProgress = (int) (100f * newBrightnessPercentage);
@@ -330,9 +449,9 @@ public abstract class AbsVideoPlayerController extends FrameLayout implements Vi
                     showChangeVolume(newVolumeProgress);
                 }
                 break;
-                //滑动结束
+            //滑动结束
             case MotionEvent.ACTION_CANCEL:
-                //滑动手指抬起
+            //滑动手指抬起
             case MotionEvent.ACTION_UP:
                 if (mNeedChangePosition) {
                     mVideoPlayer.seekTo(mNewPosition);

+ 7 - 0
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/InterVideoPlayer.java

@@ -99,6 +99,13 @@ public interface InterVideoPlayer {
      */
     int getMaxVolume();
 
+    /**
+     * 获取当前播放状态
+     *
+     * @return  播放状态
+     */
+    int getPlayType();
+
     /**
      * 获取当前音量
      *

+ 36 - 4
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/VideoPlayer.java

@@ -11,6 +11,8 @@ import android.os.Build;
 import android.support.annotation.RequiresApi;
 import android.util.AttributeSet;
 import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.TextureView;
 import android.view.ViewGroup;
@@ -18,6 +20,8 @@ import android.widget.FrameLayout;
 import android.widget.Toast;
 
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.Map;
 
@@ -123,9 +127,23 @@ public class VideoPlayer extends FrameLayout implements InterVideoPlayer{
     private int mCurrentState = STATE_IDLE;
     /**
      * 播放模式,普通模式,小窗口模式,正常模式等等
+     * 存在局限性:比如小窗口下的正在播放模式,那么mCurrentMode就是STATE_PLAYING,而不是MODE_TINY_WINDOW并存
      **/
     private int mCurrentMode = MODE_NORMAL;
+    /**
+     * 默认时普通模式
+     */
+    private int mPlayType = PlayMode.MODE_NORMAL;
 
+    /**
+     * 播放模式,普通模式,小窗口模式,正常模式三种其中一种
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    protected @interface PlayMode {
+        int MODE_NORMAL = 1001;
+        int MODE_FULL_SCREEN = 1002;
+        int MODE_TINY_WINDOW = 1003;
+    }
 
     private Context mContext;
     private AudioManager mAudioManager;
@@ -141,6 +159,7 @@ public class VideoPlayer extends FrameLayout implements InterVideoPlayer{
     private boolean continueFromLastPosition = true;
     private long skipToPosition;
 
+
     public VideoPlayer(Context context) {
         this(context, null);
     }
@@ -163,6 +182,8 @@ public class VideoPlayer extends FrameLayout implements InterVideoPlayer{
         this.addView(mContainer, params);
     }
 
+
+
     /*--------------setUp为必须设置的方法,二选其一--------------------------------------*/
     /**
      * 设置,必须设置
@@ -196,6 +217,12 @@ public class VideoPlayer extends FrameLayout implements InterVideoPlayer{
     }
 
 
+    /**
+     * @return                          获取当前播放模式
+     */
+    public int getPlayType() {
+        return mPlayType;
+    }
 
     /**
      * 设置播放器类型,必须设置
@@ -670,7 +697,6 @@ public class VideoPlayer extends FrameLayout implements InterVideoPlayer{
             Toast.makeText(mContext,"视频链接不能为空",Toast.LENGTH_SHORT).show();
             return;
         }
-        //避免出现Uri解析空指针异常[NullPointerException uriString]
         Uri path = Uri.parse(mUrl);
         try {
             mMediaPlayer.setDataSource(mContext.getApplicationContext(), path, mHeaders);
@@ -868,6 +894,7 @@ public class VideoPlayer extends FrameLayout implements InterVideoPlayer{
         contentView.addView(mContainer, params);
 
         mCurrentMode = MODE_FULL_SCREEN;
+        mPlayType = PlayMode.MODE_FULL_SCREEN;
         mController.onPlayModeChanged(mCurrentMode);
         VideoLogUtil.d("MODE_FULL_SCREEN");
     }
@@ -894,6 +921,7 @@ public class VideoPlayer extends FrameLayout implements InterVideoPlayer{
         contentView.addView(mContainer, params);
 
         mCurrentMode = MODE_FULL_SCREEN;
+        mPlayType = PlayMode.MODE_FULL_SCREEN;
         mController.onPlayModeChanged(mCurrentMode);
         VideoLogUtil.d("MODE_FULL_SCREEN");
     }
@@ -921,6 +949,7 @@ public class VideoPlayer extends FrameLayout implements InterVideoPlayer{
             LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
             this.addView(mContainer, params);
             mCurrentMode = MODE_NORMAL;
+            mPlayType = PlayMode.MODE_NORMAL;
             mController.onPlayModeChanged(mCurrentMode);
             VideoLogUtil.d("MODE_NORMAL");
             return true;
@@ -931,6 +960,7 @@ public class VideoPlayer extends FrameLayout implements InterVideoPlayer{
 
     /**
      * 进入小窗口播放,小窗口播放的实现原理与全屏播放类似。
+     * 注意:小窗口播放视频比例是        16:9
      */
     @Override
     public void enterTinyWindow() {
@@ -938,9 +968,9 @@ public class VideoPlayer extends FrameLayout implements InterVideoPlayer{
         if (mCurrentMode == MODE_TINY_WINDOW) {
             return;
         }
+        //先移除
         this.removeView(mContainer);
-        ViewGroup contentView = (ViewGroup) VideoPlayerUtils.scanForActivity(mContext)
-                .findViewById(android.R.id.content);
+        ViewGroup contentView = (ViewGroup) VideoPlayerUtils.scanForActivity(mContext).findViewById(android.R.id.content);
         // 小窗口的宽度为屏幕宽度的60%,长宽比默认为16:9,右边距、下边距为8dp。
         LayoutParams params = new LayoutParams(
                 (int) (VideoPlayerUtils.getScreenWidth(mContext) * 0.6f),
@@ -950,8 +980,8 @@ public class VideoPlayer extends FrameLayout implements InterVideoPlayer{
         params.bottomMargin = VideoPlayerUtils.dp2px(mContext, 8f);
 
         contentView.addView(mContainer, params);
-
         mCurrentMode = MODE_TINY_WINDOW;
+        mPlayType = PlayMode.MODE_TINY_WINDOW;
         mController.onPlayModeChanged(mCurrentMode);
         VideoLogUtil.d("MODE_TINY_WINDOW");
     }
@@ -967,6 +997,7 @@ public class VideoPlayer extends FrameLayout implements InterVideoPlayer{
             LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
             this.addView(mContainer, params);
             mCurrentMode = MODE_NORMAL;
+            mPlayType = PlayMode.MODE_NORMAL;
             mController.onPlayModeChanged(mCurrentMode);
             VideoLogUtil.d("MODE_NORMAL");
             return true;
@@ -1001,6 +1032,7 @@ public class VideoPlayer extends FrameLayout implements InterVideoPlayer{
             exitTinyWindow();
         }
         mCurrentMode = MODE_NORMAL;
+        mPlayType = PlayMode.MODE_NORMAL;
 
         // 释放播放器
         releasePlayer();

+ 177 - 48
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/VideoPlayerController.java

@@ -5,12 +5,15 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.graphics.Color;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
 import android.os.BatteryManager;
 import android.os.CountDownTimer;
 import android.support.annotation.DrawableRes;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
-import android.view.animation.AnimationUtils;
 import android.widget.Button;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -127,6 +130,10 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
      * 是否已经注册了电池广播
      */
     private boolean hasRegisterBatteryReceiver;
+    /**
+     * 是否已经注册了网络监听广播
+     */
+    private boolean hasRegisterNetReceiver;
     /**
      * 试看类型 setMemberType 如果不设置该方法,那么默认视频都是可以看的
      */
@@ -165,6 +172,102 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
      */
     private boolean mIsTopVisibility = false;
 
+    /**
+     * 网络变化监听广播,在网络变更时进行对应处理
+     */
+    private NetChangedReceiver netChangedReceiver;
+    private class NetChangedReceiver extends BroadcastReceiver {
+        private String getConnectionType(int type) {
+            String connType = "";
+            if (type == ConnectivityManager.TYPE_MOBILE) {
+                connType = "3G,4G网络数据";
+            } else if (type == ConnectivityManager.TYPE_WIFI) {
+                connType = "WIFI网络";
+            }
+            return connType;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // 监听网络连接,包括wifi和移动数据的打开和关闭,以及连接上可用的连接都会接到监听
+            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
+                //获取联网状态的NetworkInfo对象
+                NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
+                if (info != null) {
+                    //如果当前的网络连接成功并且网络连接可用
+                    if (NetworkInfo.State.CONNECTED == info.getState() && info.isAvailable()) {
+                        if (info.getType() == ConnectivityManager.TYPE_WIFI || info.getType() == ConnectivityManager.TYPE_MOBILE) {
+                            VideoLogUtil.i(getConnectionType(info.getType()) + "连上");
+                        }
+                    } else {
+                        VideoLogUtil.i(getConnectionType(info.getType()) + "断开");
+                        onPlayStateChanged(VideoPlayer.STATE_ERROR);
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 电池状态即电量变化广播接收器
+     */
+    private BroadcastReceiver mBatterReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
+            if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
+                // 充电中
+                mBattery.setImageResource(R.drawable.battery_charging);
+            } else if (status == BatteryManager.BATTERY_STATUS_FULL) {
+                // 充电完成
+                mBattery.setImageResource(R.drawable.battery_full);
+            } else {
+                int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+                int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
+                int percentage = (int) (((float) level / scale) * 100);
+                if (percentage <= 10) {
+                    mBattery.setImageResource(R.drawable.battery_10);
+                } else if (percentage <= 20) {
+                    mBattery.setImageResource(R.drawable.battery_20);
+                } else if (percentage <= 50) {
+                    mBattery.setImageResource(R.drawable.battery_50);
+                } else if (percentage <= 80) {
+                    mBattery.setImageResource(R.drawable.battery_80);
+                } else if (percentage <= 100) {
+                    mBattery.setImageResource(R.drawable.battery_100);
+                }
+            }
+        }
+    };
+
+    /**
+     * 如果锁屏,则屏蔽返回键
+     */
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if(keyCode == KeyEvent.KEYCODE_BACK){
+            VideoLogUtil.i("1如果锁屏,则屏蔽返回键");
+            if(mIsLock){
+                //如果锁屏,那就屏蔽返回键
+                return true;
+            }
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+
+    /**
+     * 如果锁屏,则屏蔽滑动事件
+     */
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if(mIsLock){
+            //如果锁屏了,那就就不需要处理滑动的逻辑
+            return false;
+        }
+        return super.onTouchEvent(event);
+    }
 
     public VideoPlayerController(Context context) {
         super(context);
@@ -172,10 +275,43 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
         init();
     }
 
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        unRegisterNetChangedReceiver();//会导致崩溃
+    }
+
+    public void registerNetChangedReceiver() {
+        if (!hasRegisterNetReceiver) {
+            if (netChangedReceiver == null) {
+                netChangedReceiver = new NetChangedReceiver();
+                IntentFilter filter = new IntentFilter();
+                filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
+                mContext.registerReceiver(netChangedReceiver, filter);
+                VideoLogUtil.i("注册网络监听广播");
+            }
+            hasRegisterNetReceiver = true;
+        }
+    }
+
+    public void unRegisterNetChangedReceiver() {
+        if (hasRegisterNetReceiver) {
+            if (netChangedReceiver != null) {
+                mContext.unregisterReceiver(netChangedReceiver);
+                VideoLogUtil.i("解绑注册网络监听广播");
+            }
+            hasRegisterNetReceiver = false;
+        }
+    }
+
+    /**
+     * 初始化操作
+     */
     private void init() {
         LayoutInflater.from(mContext).inflate(R.layout.custom_video_player, this, true);
         initFindViewById();
         initListener();
+        registerNetChangedReceiver();
     }
 
     private void initFindViewById() {
@@ -552,6 +688,9 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
                 setTopBottomVisible(false);
                 mTop.setVisibility(View.VISIBLE);
                 mError.setVisibility(View.VISIBLE);
+                if(mVideoPlayer.isError()){
+                    mVideoPlayer.pause();
+                }
                 break;
             //播放完成
             case VideoPlayer.STATE_COMPLETED:
@@ -598,6 +737,7 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
                     mContext.unregisterReceiver(mBatterReceiver);
                     hasRegisterBatteryReceiver = false;
                 }
+                mIsLock = false;
                 break;
             //全屏模式
             case VideoPlayer.MODE_FULL_SCREEN:
@@ -620,44 +760,13 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
                 mFlLock.setVisibility(View.GONE);
                 mBack.setVisibility(View.VISIBLE);
                 mClarity.setVisibility(View.GONE);
+                mIsLock = false;
                 break;
             default:
                 break;
         }
     }
 
-    /**
-     * 电池状态即电量变化广播接收器
-     */
-    private BroadcastReceiver mBatterReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
-            if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
-                // 充电中
-                mBattery.setImageResource(R.drawable.battery_charging);
-            } else if (status == BatteryManager.BATTERY_STATUS_FULL) {
-                // 充电完成
-                mBattery.setImageResource(R.drawable.battery_full);
-            } else {
-                int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
-                int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
-                int percentage = (int) (((float) level / scale) * 100);
-                if (percentage <= 10) {
-                    mBattery.setImageResource(R.drawable.battery_10);
-                } else if (percentage <= 20) {
-                    mBattery.setImageResource(R.drawable.battery_20);
-                } else if (percentage <= 50) {
-                    mBattery.setImageResource(R.drawable.battery_50);
-                } else if (percentage <= 80) {
-                    mBattery.setImageResource(R.drawable.battery_80);
-                } else if (percentage <= 100) {
-                    mBattery.setImageResource(R.drawable.battery_100);
-                }
-            }
-        }
-    };
-
 
     /**
      * 重新设置
@@ -721,17 +830,21 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
                 }
             }
         } else if (v == mRestartPause) {
-            //重新播放或者暂停
-            if (mVideoPlayer.isPlaying() || mVideoPlayer.isBufferingPlaying()) {
-                mVideoPlayer.pause();
-                if(mOnPlayOrPauseListener!=null){
-                    mOnPlayOrPauseListener.onPlayOrPauseClick(true);
-                }
-            } else if (mVideoPlayer.isPaused() || mVideoPlayer.isBufferingPaused()) {
-                mVideoPlayer.restart();
-                if(mOnPlayOrPauseListener!=null){
-                    mOnPlayOrPauseListener.onPlayOrPauseClick(false);
+            if(VideoPlayerUtils.isConnected(mContext)){
+                //重新播放或者暂停
+                if (mVideoPlayer.isPlaying() || mVideoPlayer.isBufferingPlaying()) {
+                    mVideoPlayer.pause();
+                    if(mOnPlayOrPauseListener!=null){
+                        mOnPlayOrPauseListener.onPlayOrPauseClick(true);
+                    }
+                } else if (mVideoPlayer.isPaused() || mVideoPlayer.isBufferingPaused()) {
+                    mVideoPlayer.restart();
+                    if(mOnPlayOrPauseListener!=null){
+                        mOnPlayOrPauseListener.onPlayOrPauseClick(false);
+                    }
                 }
+            }else {
+                Toast.makeText(mContext,"请检测是否有网络",Toast.LENGTH_SHORT).show();
             }
         } else if (v == mFullScreen) {
             //全屏模式,重置锁屏,设置为未选中状态
@@ -753,10 +866,18 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
         } else if (v == mRetry) {
             //点击重试
             //不论是否记录播放位置,都是从零开始播放
-            mVideoPlayer.restart();
+            if(VideoPlayerUtils.isConnected(mContext)){
+                mVideoPlayer.restart();
+            }else {
+                Toast.makeText(mContext,"请检测是否有网络",Toast.LENGTH_SHORT).show();
+            }
         } else if (v == mReplay) {
             //重新播放
-            mRetry.performClick();
+            if(VideoPlayerUtils.isConnected(mContext)){
+                mRetry.performClick();
+            }else {
+                Toast.makeText(mContext,"请检测是否有网络",Toast.LENGTH_SHORT).show();
+            }
         } else if (v == mShare) {
             //分享
             Toast.makeText(mContext, "分享", Toast.LENGTH_SHORT).show();
@@ -788,6 +909,7 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
         } else if(v == mFlLock){
             //点击锁屏按钮,则进入锁屏模式
             setLock(mIsLock);
+
         } else if(v == mIvDownload){
             if(mVideoControlListener==null){
                 VideoLogUtil.d("请在初始化的时候设置下载监听事件");
@@ -866,10 +988,8 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
         }
         if(visible){
             mBottom.setVisibility(View.VISIBLE);
-            mBottom.animate().translationY(0);
         }else {
             mBottom.setVisibility(View.GONE);
-            mBottom.animate().translationY(-mBottom.getHeight());
         }
         topBottomVisible = visible;
         if (visible) {
@@ -920,7 +1040,7 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
     /**
      * 设置锁屏模式,默认是未锁屏的
      * 当为true时,则锁屏;否则为未锁屏
-     * @param isLock        是否锁屏
+     * @param isLock            是否锁屏
      */
     private void setLock(boolean isLock){
         if(isLock){
@@ -930,8 +1050,17 @@ public class VideoPlayerController extends AbsVideoPlayerController implements V
             mIsLock = true;
             mIvLock.setImageResource(R.drawable.player_locked_btn);
         }
+        /*
+         * 设置锁屏时的布局
+         * 1.横屏全屏时显示,其他不展示;
+         * 2.锁屏时隐藏控制面板除锁屏按钮外其他所有控件
+         * 3.当从全屏切换到正常或者小窗口时,则默认不锁屏
+         */
+        setTopBottomVisible(!mIsLock);
     }
 
+
+
     /**
      * 获取是否是锁屏模式
      * @return              true表示锁屏

+ 32 - 0
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/VideoPlayerUtils.java

@@ -4,6 +4,8 @@ import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
 import android.support.v7.app.ActionBar;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.view.ContextThemeWrapper;
@@ -162,4 +164,34 @@ public class VideoPlayerUtils {
         context.getSharedPreferences("VIDEO_PLAYER_PLAY_POSITION", Context.MODE_PRIVATE).getAll().clear();
     }
 
+
+    /**
+     * 判断网络是否连接
+     * <p>需添加权限 {@code <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>}</p>
+     *
+     * @return {@code true}: 是<br>{@code false}: 否
+     */
+    public static boolean isConnected(Context context) {
+        NetworkInfo info = getActiveNetworkInfo(context);
+        return info != null && info.isConnected();
+    }
+
+
+
+    /**
+     * 获取活动网络信息
+     * <p>需添加权限
+     * {@code <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />}</p>
+     *
+     * @return NetworkInfo
+     */
+    @SuppressLint("MissingPermission")
+    private static NetworkInfo getActiveNetworkInfo(Context context) {
+        ConnectivityManager manager = (ConnectivityManager)
+                context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (manager == null) return null;
+        return manager.getActiveNetworkInfo();
+    }
+
+
 }

+ 2 - 0
YCVideoPlayerLib/src/main/java/org/yczbj/ycvideoplayerlib/VideoTextureView.java

@@ -1,6 +1,7 @@
 package org.yczbj.ycvideoplayerlib;
 
 import android.content.Context;
+import android.view.SurfaceView;
 import android.view.TextureView;
 
 /**
@@ -36,6 +37,7 @@ public class VideoTextureView extends TextureView {
     }
 
     /**
+     * 记得一定要重新写这个方法,如果角度发生了变化,就重新绘制布局
      * 设置视频旋转角度
      * @param rotation                  角度
      */

+ 0 - 7
app/src/main/AndroidManifest.xml

@@ -42,7 +42,6 @@
 
 
         <!--下面这些均是测试视频播放器-->
-        <activity android:name=".ui.test.test3.ui.activity.GlideCropActivity"/>
         <activity android:name=".ui.test.test1.TestActivity"
             android:configChanges="orientation|keyboardHidden|screenSize"
             android:screenOrientation="portrait"/>
@@ -133,12 +132,6 @@
             android:screenOrientation="portrait"/>
         <activity android:name=".ui.test.test4.MediaPlayerActivity"/>
 
-        <activity android:name=".ui.test.test3.ui.activity.DLSingleTestActivity"/>
-        <activity android:name=".ui.test.test3.ui.activity.DLHybridTestActivity"/>
-        <activity android:name=".ui.test.test3.ui.activity.DLManyTestActivity"/>
-        <activity android:name=".ui.test.test3.ui.activity.DLMyFileTestActivity"/>
-        <activity android:name=".ui.test.test3.ui.activity.DLNotificationTestActivity"/>
-
     </application>
 
 </manifest>

+ 1 - 0
app/src/main/java/org/yczbj/ycvideoplayer/ui/main/view/activity/MainActivity.java

@@ -9,6 +9,7 @@ import android.support.v4.app.FragmentTransaction;
 import android.view.KeyEvent;
 import android.widget.FrameLayout;
 
+import com.blankj.utilcode.util.NetworkUtils;
 import com.blankj.utilcode.util.ToastUtils;
 import com.flyco.tablayout.CommonTabLayout;
 import com.flyco.tablayout.listener.CustomTabEntity;

+ 32 - 0
app/src/main/java/org/yczbj/ycvideoplayer/ui/video/view/activity/VideoContentActivity.java

@@ -11,10 +11,13 @@ import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.Button;
 import android.widget.LinearLayout;
+import android.widget.Toast;
 
 import com.blankj.utilcode.util.SizeUtils;
 import com.blankj.utilcode.util.Utils;
+import com.pedaily.yc.ycdialoglib.customToast.ToastUtil;
 
 import org.yczbj.ycrefreshviewlib.YCRefreshView;
 import org.yczbj.ycrefreshviewlib.adapter.RecyclerArrayAdapter;
@@ -151,6 +154,12 @@ public class VideoContentActivity extends BaseMVPActivity {
 
     private void addHeader() {
         adapter.addHeader(new RecyclerArrayAdapter.ItemView() {
+
+            private Button mBtn1;
+            private Button mBtn2;
+            private Button mBtn3;
+            private Button mBtn4;
+
             @Override
             public View onCreateView(ViewGroup parent) {
                 return LayoutInflater.from(VideoContentActivity.this).inflate
@@ -160,6 +169,29 @@ public class VideoContentActivity extends BaseMVPActivity {
             @Override
             public void onBindView(View headerView) {
                 videoPlayer = headerView.findViewById(R.id.video_player);
+                mBtn1 =  headerView.findViewById(R.id.btn_1);
+                mBtn2 =  headerView.findViewById(R.id.btn_2);
+                mBtn3 =  headerView.findViewById(R.id.btn_3);
+                mBtn4 =  headerView.findViewById(R.id.btn_4);
+
+                View.OnClickListener listener = new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        switch (v.getId()){
+                            case R.id.btn_1:
+                                if (videoPlayer.isIdle()) {
+                                    ToastUtil.showToast(VideoContentActivity.this,"要点击播放后才能进入小窗口");
+                                } else {
+                                    videoPlayer.enterTinyWindow();
+                                }
+                                break;
+                        }
+                    }
+                };
+                mBtn1.setOnClickListener(listener);
+                mBtn2.setOnClickListener(listener);
+                mBtn3.setOnClickListener(listener);
+                mBtn4.setOnClickListener(listener);
             }
         });
     }

+ 30 - 3
app/src/main/res/layout/head_video_player.xml

@@ -1,11 +1,38 @@
 <?xml version="1.0" encoding="utf-8"?>
-<FrameLayout
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
     <!--视频播放器-->
     <org.yczbj.ycvideoplayerlib.VideoPlayer
         android:id="@+id/video_player"
         android:layout_width="match_parent"
         android:layout_height="200dp" />
-</FrameLayout>
+
+
+    <Button
+        android:id="@+id/btn_1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="小窗口播放"/>
+
+    <Button
+        android:id="@+id/btn_2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="竖屏全屏播放"/>
+
+    <Button
+        android:id="@+id/btn_3"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="横屏全屏播放"/>
+
+    <Button
+        android:id="@+id/btn_4"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="重新播放"/>
+
+</LinearLayout>