提交 5e964a51 authored 作者: kongdywang's avatar kongdywang

SuperPlayer supports screen autorotation for Android&iOS platform

上级 3cdf0ab2
......@@ -58,4 +58,17 @@ public class FTXEvent {
// pip 错误,当前界面已销毁
public static final int ERROR_PIP_ACTIVITY_DESTROYED = -103;
// 屏幕旋转事件
public static final int EVENT_ORIENTATION_CHANGED = 401;
// 屏幕旋转方向
public static final String EXTRA_NAME_ORIENTATION = "orientation";
// 正竖屏
public static final int ORIENTATION_PORTRAIT_UP = 411;
// 横屏,底部在右
public static final int ORIENTATION_LANDSCAPE_RIGHT = 412;
// 竖屏,顶部在下
public static final int ORIENTATION_PORTRAIT_DOWN = 413;
// 横屏,底部在左
public static final int ORIENTATION_LANDSCAPE_LEFT = 414;
}
......@@ -7,8 +7,10 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.SparseArray;
import android.view.OrientationEventListener;
import android.view.Window;
import android.view.WindowManager;
......@@ -58,6 +60,9 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti
private FTXAudioManager mTxAudioManager;
private FTXPIPManager mTxPipManager;
private OrientationEventListener mOrientationManager;
private int mCurrentOrientation = FTXEvent.ORIENTATION_PORTRAIT_UP;
private final FTXAudioManager.AudioFocusChangeListener audioFocusChangeListener =
new FTXAudioManager.AudioFocusChangeListener() {
@Override
......@@ -78,8 +83,7 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti
channel.setMethodCallHandler(this);
mPlayers = new SparseArray();
initAudioManagerIfNeed();
mEventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "cloud.tencent" +
".com/playerPlugin/event");
mEventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "cloud.tencent.com/playerPlugin/event");
mEventChannel.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object o, EventChannel.EventSink eventSink) {
......@@ -92,6 +96,31 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti
}
});
mFTXDownloadManager = new FTXDownloadManager(mFlutterPluginBinding);
mOrientationManager = new OrientationEventListener(flutterPluginBinding.getApplicationContext()) {
@Override
public void onOrientationChanged(int orientation) {
if (isAutoRotateOn()) {
int orientationEvent = mCurrentOrientation;
// 每个方向判断当前方向正负30度,共计60度的区间
if (((orientation >= 0) && (orientation < 30)) || (orientation > 330)) {
orientationEvent = FTXEvent.ORIENTATION_PORTRAIT_UP;
} else if (orientation > 240 && orientation < 300) {
orientationEvent = FTXEvent.ORIENTATION_LANDSCAPE_RIGHT;
} else if (orientation > 150 && orientation < 210) {
orientationEvent = FTXEvent.ORIENTATION_PORTRAIT_DOWN;
} else if (orientation > 60 && orientation < 110) {
orientationEvent = FTXEvent.ORIENTATION_LANDSCAPE_LEFT;
}
if (orientationEvent != mCurrentOrientation) {
mCurrentOrientation = orientationEvent;
Bundle bundle = new Bundle();
bundle.putInt(FTXEvent.EXTRA_NAME_ORIENTATION, orientationEvent);
mEventSink.success(getParams(FTXEvent.EVENT_ORIENTATION_CHANGED, bundle));
}
}
}
};
mOrientationManager.enable();
}
@Override
......@@ -228,6 +257,9 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti
if (null != mTxPipManager) {
mTxPipManager.releaseReceiver();
}
if (null != mOrientationManager) {
mOrientationManager.disable();
}
unregisterReceiver();
}
......@@ -240,10 +272,20 @@ public class SuperPlayerPlugin implements FlutterPlugin, MethodCallHandler, Acti
}
/**
* 注册音量广播接收器
* 系统是否允许自动旋转屏幕
*
* @return
*/
protected boolean isAutoRotateOn() {
//获取系统是否允许自动旋转屏幕
return (android.provider.Settings.System.getInt(
mFlutterPluginBinding.getApplicationContext().getContentResolver(),
Settings.System.ACCELEROMETER_ROTATION, 0) == 1);
}
/**
* 注册音量广播接收器
*/
public void registerReceiver() {
// volume receiver
mVolumeBroadcastReceiver = new VolumeBroadcastReceiver();
......
package com.tencent.liteav.demo.superplayer;
import android.app.Activity;
import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
import android.provider.MediaStore;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.tencent.liteav.basic.log.TXCLog;
import com.tencent.liteav.demo.superplayer.model.SuperPlayer;
import com.tencent.liteav.demo.superplayer.model.SuperPlayerImpl;
import com.tencent.liteav.demo.superplayer.model.SuperPlayerObserver;
import com.tencent.liteav.demo.superplayer.model.entity.PlayImageSpriteInfo;
import com.tencent.liteav.demo.superplayer.model.entity.PlayKeyFrameDescInfo;
import com.tencent.liteav.demo.superplayer.model.entity.VideoQuality;
import com.tencent.liteav.demo.superplayer.model.net.LogReport;
import com.tencent.liteav.demo.superplayer.model.utils.NetWatcher;
import com.tencent.liteav.demo.superplayer.ui.player.FloatPlayer;
import com.tencent.liteav.demo.superplayer.ui.player.FullScreenPlayer;
import com.tencent.liteav.demo.superplayer.ui.player.Player;
import com.tencent.liteav.demo.superplayer.ui.player.WindowPlayer;
import com.tencent.liteav.demo.superplayer.ui.view.DanmuView;
import com.tencent.rtmp.TXLivePlayer;
import com.tencent.rtmp.ui.TXCloudVideoView;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;
/**
*
* 超级播放器view
*
* 具备播放器基本功能,此外还包括横竖屏切换、悬浮窗播放、画质切换、硬件加速、倍速播放、镜像播放、手势控制等功能,同时支持直播与点播
* 使用方式极为简单,只需要在布局文件中引入并获取到该控件,通过{@link #playWithModel(SuperPlayerModel)}传入{@link SuperPlayerModel}即可实现视频播放
*
* 1、播放视频{@link #playWithModel(SuperPlayerModel)}
* 2、设置回调{@link #setPlayerViewCallback(OnSuperPlayerViewCallback)}
* 3、controller回调实现{@link #mControllerCallback}
* 4、退出播放释放内存{@link #resetPlayer()}
*/
public class SuperPlayerView extends RelativeLayout {
private static final String TAG = "SuperPlayerView";
private final int OP_SYSTEM_ALERT_WINDOW = 24; // 支持TYPE_TOAST悬浮窗的最高API版本
private Context mContext;
private ViewGroup mRootView; // SuperPlayerView的根view
private TXCloudVideoView mTXCloudVideoView; // 腾讯云视频播放view
private FullScreenPlayer mFullScreenPlayer; // 全屏模式控制view
private WindowPlayer mWindowPlayer; // 窗口模式控制view
private FloatPlayer mFloatPlayer; // 悬浮窗模式控制view
private DanmuView mDanmuView; // 弹幕
private ViewGroup.LayoutParams mLayoutParamWindowMode; // 窗口播放时SuperPlayerView的布局参数
private ViewGroup.LayoutParams mLayoutParamFullScreenMode; // 全屏播放时SuperPlayerView的布局参数
private LayoutParams mVodControllerWindowParams; // 窗口controller的布局参数
private LayoutParams mVodControllerFullScreenParams; // 全屏controller的布局参数
private WindowManager mWindowManager; // 悬浮窗窗口管理器
private WindowManager.LayoutParams mWindowParams; // 悬浮窗布局参数
private OnSuperPlayerViewCallback mPlayerViewCallback; // SuperPlayerView回调
private NetWatcher mWatcher; // 网络质量监视器
private SuperPlayer mSuperPlayer;
public SuperPlayerView(Context context) {
super(context);
initialize(context);
}
public SuperPlayerView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context);
}
public SuperPlayerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context);
}
private void initialize(Context context) {
mContext = context;
initView();
initPlayer();
}
/**
* 初始化view
*/
private void initView() {
mRootView = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.superplayer_vod_view, null);
mTXCloudVideoView = (TXCloudVideoView) mRootView.findViewById(R.id.superplayer_cloud_video_view);
mFullScreenPlayer = (FullScreenPlayer) mRootView.findViewById(R.id.superplayer_controller_large);
mWindowPlayer = (WindowPlayer) mRootView.findViewById(R.id.superplayer_controller_small);
mFloatPlayer = (FloatPlayer) mRootView.findViewById(R.id.superplayer_controller_float);
mDanmuView = (DanmuView) mRootView.findViewById(R.id.superplayer_danmuku_view);
mVodControllerWindowParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mVodControllerFullScreenParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mFullScreenPlayer.setCallback(mControllerCallback);
mWindowPlayer.setCallback(mControllerCallback);
mFloatPlayer.setCallback(mControllerCallback);
removeAllViews();
mRootView.removeView(mDanmuView);
mRootView.removeView(mTXCloudVideoView);
mRootView.removeView(mWindowPlayer);
mRootView.removeView(mFullScreenPlayer);
mRootView.removeView(mFloatPlayer);
addView(mTXCloudVideoView);
addView(mDanmuView);
}
private void initPlayer() {
mSuperPlayer = new SuperPlayerImpl(mContext, mTXCloudVideoView);
mSuperPlayer.setObserver(mSuperPlayerObserver);
if (mSuperPlayer.getPlayerMode() == SuperPlayerDef.PlayerMode.FULLSCREEN) {
addView(mFullScreenPlayer);
mFullScreenPlayer.hide();
} else if (mSuperPlayer.getPlayerMode() == SuperPlayerDef.PlayerMode.WINDOW) {
addView(mWindowPlayer);
mWindowPlayer.hide();
}
post(new Runnable() {
@Override
public void run() {
if (mSuperPlayer.getPlayerMode() == SuperPlayerDef.PlayerMode.WINDOW) {
mLayoutParamWindowMode = getLayoutParams();
}
try {
// 依据上层Parent的LayoutParam类型来实例化一个新的fullscreen模式下的LayoutParam
Class parentLayoutParamClazz = getLayoutParams().getClass();
Constructor constructor = parentLayoutParamClazz.getDeclaredConstructor(int.class, int.class);
mLayoutParamFullScreenMode = (ViewGroup.LayoutParams) constructor.newInstance(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
} catch (Exception e) {
e.printStackTrace();
}
}
});
LogReport.getInstance().setAppName(mContext);
LogReport.getInstance().setPackageName(mContext);
if (mWatcher == null) {
mWatcher = new NetWatcher(mContext);
}
}
/**
* 播放视频
*
* @param model
*/
public void playWithModel(final SuperPlayerModel model) {
if (model.videoId != null) {
mSuperPlayer.play(model.appId, model.videoId.fileId, model.videoId.pSign);
} else if (model.videoIdV2 != null) {
} else if (model.multiURLs != null && !model.multiURLs.isEmpty()) {
mSuperPlayer.play(model.appId, model.multiURLs, model.playDefaultIndex);
} else {
mSuperPlayer.play(model.url);
}
}
/**
* 开始播放
*
* @param url 视频地址
*/
public void play(String url) {
mSuperPlayer.play(url);
}
/**
* 开始播放
*
* @param appId 腾讯云视频appId
* @param url 直播播放地址
*/
public void play(int appId, String url) {
mSuperPlayer.play(appId, url);
}
/**
* 开始播放
*
* @param appId 腾讯云视频appId
* @param fileId 腾讯云视频fileId
* @param psign 防盗链签名,开启防盗链的视频必填,非防盗链视频可不填
*/
public void play(int appId, String fileId, String psign) {
mSuperPlayer.play(appId, fileId, psign);
}
/**
* 多分辨率播放
*
* @param appId 腾讯云视频appId
* @param superPlayerURLS 不同分辨率数据
* @param defaultIndex 默认播放Index
*/
public void play(int appId, List<SuperPlayerModel.SuperPlayerURL> superPlayerURLS, int defaultIndex) {
mSuperPlayer.play(appId, superPlayerURLS, defaultIndex);
}
/**
* 更新标题
*
* @param title 视频名称
*/
private void updateTitle(String title) {
mWindowPlayer.updateTitle(title);
mFullScreenPlayer.updateTitle(title);
}
/**
* resume生命周期回调
*/
public void onResume() {
if (mDanmuView != null && mDanmuView.isPrepared() && mDanmuView.isPaused()) {
mDanmuView.resume();
}
mSuperPlayer.resume();
}
/**
* pause生命周期回调
*/
public void onPause() {
if (mDanmuView != null && mDanmuView.isPrepared()) {
mDanmuView.pause();
}
mSuperPlayer.pauseVod();
}
/**
* 重置播放器
*/
public void resetPlayer() {
if (mDanmuView != null) {
mDanmuView.release();
mDanmuView = null;
}
stopPlay();
}
/**
* 停止播放
*/
private void stopPlay() {
mSuperPlayer.stop();
if (mWatcher != null) {
mWatcher.stop();
}
}
/**
* 设置超级播放器的回掉
*
* @param callback
*/
public void setPlayerViewCallback(OnSuperPlayerViewCallback callback) {
mPlayerViewCallback = callback;
}
/**
* 控制是否全屏显示
*/
private void fullScreen(boolean isFull) {
if (getContext() instanceof Activity) {
Activity activity = (Activity) getContext();
if (isFull) {
//隐藏虚拟按键,并且全屏
View decorView = activity.getWindow().getDecorView();
if (decorView == null) return;
if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api
decorView.setSystemUiVisibility(View.GONE);
} else if (Build.VERSION.SDK_INT >= 19) {
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
}
} else {
View decorView = activity.getWindow().getDecorView();
if (decorView == null) return;
if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api
decorView.setSystemUiVisibility(View.VISIBLE);
} else if (Build.VERSION.SDK_INT >= 19) {
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
}
}
}
/**
* 初始化controller回调
*/
private Player.Callback mControllerCallback = new Player.Callback() {
@Override
public void onSwitchPlayMode(SuperPlayerDef.PlayerMode playerMode) {
if (playerMode == SuperPlayerDef.PlayerMode.FULLSCREEN) {
fullScreen(true);
} else {
fullScreen(false);
}
mFullScreenPlayer.hide();
mWindowPlayer.hide();
mFloatPlayer.hide();
//请求全屏模式
if (playerMode == SuperPlayerDef.PlayerMode.FULLSCREEN) {
if (mLayoutParamFullScreenMode == null) {
return;
}
removeView(mWindowPlayer);
addView(mFullScreenPlayer, mVodControllerFullScreenParams);
setLayoutParams(mLayoutParamFullScreenMode);
rotateScreenOrientation(SuperPlayerDef.Orientation.LANDSCAPE);
if (mPlayerViewCallback != null) {
mPlayerViewCallback.onStartFullScreenPlay();
}
} else if (playerMode == SuperPlayerDef.PlayerMode.WINDOW) {// 请求窗口模式
// 当前是悬浮窗
if (mSuperPlayer.getPlayerMode() == SuperPlayerDef.PlayerMode.FLOAT) {
try {
Context viewContext = getContext();
Intent intent = null;
if (viewContext instanceof Activity) {
intent = new Intent(viewContext, viewContext.getClass());
} else {
showToast(R.string.superplayer_float_play_fail);
return;
}
mContext.startActivity(intent);
mSuperPlayer.pause();
if (mLayoutParamWindowMode == null) {
return;
}
mWindowManager.removeView(mFloatPlayer);
mSuperPlayer.setPlayerView(mTXCloudVideoView);
mSuperPlayer.resume();
} catch (Exception e) {
e.printStackTrace();
}
} else if (mSuperPlayer.getPlayerMode() == SuperPlayerDef.PlayerMode.FULLSCREEN) { // 当前是全屏模式
if (mLayoutParamWindowMode == null) {
return;
}
removeView(mFullScreenPlayer);
addView(mWindowPlayer, mVodControllerWindowParams);
setLayoutParams(mLayoutParamWindowMode);
rotateScreenOrientation(SuperPlayerDef.Orientation.PORTRAIT);
if (mPlayerViewCallback != null) {
mPlayerViewCallback.onStopFullScreenPlay();
}
}
} else if (playerMode == SuperPlayerDef.PlayerMode.FLOAT) {//请求悬浮窗模式
TXCLog.i(TAG, "requestPlayMode Float :" + Build.MANUFACTURER);
SuperPlayerGlobalConfig prefs = SuperPlayerGlobalConfig.getInstance();
if (!prefs.enableFloatWindow) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 6.0动态申请悬浮窗权限
if (!Settings.canDrawOverlays(mContext)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + mContext.getPackageName()));
mContext.startActivity(intent);
return;
}
} else {
if (!checkOp(mContext, OP_SYSTEM_ALERT_WINDOW)) {
showToast(R.string.superplayer_enter_setting_fail);
return;
}
}
mSuperPlayer.pause();
mWindowManager = (WindowManager) mContext.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
mWindowParams = new WindowManager.LayoutParams();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mWindowParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
mWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mWindowParams.format = PixelFormat.TRANSLUCENT;
mWindowParams.gravity = Gravity.LEFT | Gravity.TOP;
SuperPlayerGlobalConfig.TXRect rect = prefs.floatViewRect;
mWindowParams.x = rect.x;
mWindowParams.y = rect.y;
mWindowParams.width = rect.width;
mWindowParams.height = rect.height;
try {
mWindowManager.addView(mFloatPlayer, mWindowParams);
} catch (Exception e) {
showToast(R.string.superplayer_float_play_fail);
return;
}
TXCloudVideoView videoView = mFloatPlayer.getFloatVideoView();
if (videoView != null) {
mSuperPlayer.setPlayerView(videoView);
mSuperPlayer.resume();
}
// 悬浮窗上报
LogReport.getInstance().uploadLogs(LogReport.ELK_ACTION_FLOATMOE, 0, 0);
}
mSuperPlayer.switchPlayMode(playerMode);
}
@Override
public void onBackPressed(SuperPlayerDef.PlayerMode playMode) {
switch (playMode) {
case FULLSCREEN:// 当前是全屏模式,返回切换成窗口模式
onSwitchPlayMode(SuperPlayerDef.PlayerMode.WINDOW);
break;
case WINDOW:// 当前是窗口模式,返回退出播放器
if (mPlayerViewCallback != null) {
mPlayerViewCallback.onSuperPlayerBackAction();
}
break;
case FLOAT:// 当前是悬浮窗,退出
mWindowManager.removeView(mFloatPlayer);
if (mPlayerViewCallback != null) {
mPlayerViewCallback.onClickFloatCloseBtn();
}
break;
default:
break;
}
}
@Override
public void onFloatPositionChange(int x, int y) {
mWindowParams.x = x;
mWindowParams.y = y;
mWindowManager.updateViewLayout(mFloatPlayer, mWindowParams);
}
@Override
public void onPause() {
mSuperPlayer.pause();
if (mSuperPlayer.getPlayerType() != SuperPlayerDef.PlayerType.VOD) {
if (mWatcher != null) {
mWatcher.stop();
}
}
}
@Override
public void onResume() {
if (mSuperPlayer.getPlayerState() == SuperPlayerDef.PlayerState.END) { //重播
mSuperPlayer.reStart();
} else if (mSuperPlayer.getPlayerState() == SuperPlayerDef.PlayerState.PAUSE) { //继续播放
mSuperPlayer.resume();
}
}
@Override
public void onSeekTo(int position) {
mSuperPlayer.seek(position);
}
@Override
public void onResumeLive() {
mSuperPlayer.resumeLive();
}
@Override
public void onDanmuToggle(boolean isOpen) {
if (mDanmuView != null) {
mDanmuView.toggle(isOpen);
}
}
@Override
public void onSnapshot() {
mSuperPlayer.snapshot(new TXLivePlayer.ITXSnapshotListener() {
@Override
public void onSnapshot(Bitmap bitmap) {
if (bitmap != null) {
showSnapshotWindow(bitmap);
} else {
showToast(R.string.superplayer_screenshot_fail);
}
}
});
}
@Override
public void onQualityChange(VideoQuality quality) {
mFullScreenPlayer.updateVideoQuality(quality);
mSuperPlayer.switchStream(quality);
}
@Override
public void onSpeedChange(float speedLevel) {
mSuperPlayer.setRate(speedLevel);
}
@Override
public void onMirrorToggle(boolean isMirror) {
mSuperPlayer.setMirror(isMirror);
}
@Override
public void onHWAccelerationToggle(boolean isAccelerate) {
mSuperPlayer.enableHardwareDecode(isAccelerate);
}
};
/**
* 显示截图窗口
*
* @param bmp
*/
private void showSnapshotWindow(final Bitmap bmp) {
final PopupWindow popupWindow = new PopupWindow(mContext);
popupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
View view = LayoutInflater.from(mContext).inflate(R.layout.superplayer_layout_new_vod_snap, null);
ImageView imageView = (ImageView) view.findViewById(R.id.superplayer_iv_snap);
imageView.setImageBitmap(bmp);
popupWindow.setContentView(view);
popupWindow.setOutsideTouchable(true);
popupWindow.showAtLocation(mRootView, Gravity.TOP, 1800, 300);
AsyncTask.execute(new Runnable() {
@Override
public void run() {
save2MediaStore(mContext, bmp);
}
});
postDelayed(new Runnable() {
@Override
public void run() {
popupWindow.dismiss();
}
}, 3000);
}
/**
* 旋转屏幕方向
*
* @param orientation
*/
private void rotateScreenOrientation(SuperPlayerDef.Orientation orientation) {
switch (orientation) {
case LANDSCAPE:
((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
break;
case PORTRAIT:
((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
break;
}
}
/**
* 检查悬浮窗权限
* <p>
* API <18,默认有悬浮窗权限,不需要处理。无法接收无法接收触摸和按键事件,不需要权限和无法接受触摸事件的源码分析
* API >= 19 ,可以接收触摸和按键事件
* API >=23,需要在manifest中申请权限,并在每次需要用到权限的时候检查是否已有该权限,因为用户随时可以取消掉。
* API >25,TYPE_TOAST 已经被谷歌制裁了,会出现自动消失的情况
*/
private boolean checkOp(Context context, int op) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
try {
Method method = AppOpsManager.class.getDeclaredMethod("checkOp", int.class, int.class, String.class);
return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());
} catch (Exception e) {
TXCLog.e(TAG, Log.getStackTraceString(e));
}
}
return true;
}
/**
* SuperPlayerView的回调接口
*/
public interface OnSuperPlayerViewCallback {
/**
* 开始全屏播放
*/
void onStartFullScreenPlay();
/**
* 结束全屏播放
*/
void onStopFullScreenPlay();
/**
* 点击悬浮窗模式下的x按钮
*/
void onClickFloatCloseBtn();
/**
* 点击小播放模式的返回按钮
*/
void onSuperPlayerBackAction();
/**
* 开始悬浮窗播放
*/
void onStartFloatWindowPlay();
/**
* 播放开始通知
*/
void onSuperPlayerDidStart();
/**
* 播放结束通知
*/
void onSuperPlayerDidEnd();
/**
* 播放错误通知
*/
void onSuperPlayerError();
}
public void release() {
if (mWindowPlayer != null) {
mWindowPlayer.release();
}
if (mFullScreenPlayer != null) {
mFullScreenPlayer.release();
}
if (mFloatPlayer != null) {
mFloatPlayer.release();
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
try {
release();
} catch (Throwable e) {
TXCLog.e(TAG, Log.getStackTraceString(e));
}
}
public void switchPlayMode(SuperPlayerDef.PlayerMode playerMode) {
if (playerMode == SuperPlayerDef.PlayerMode.WINDOW) {
if (mControllerCallback != null) {
mControllerCallback.onSwitchPlayMode(SuperPlayerDef.PlayerMode.WINDOW);
}
} else if (playerMode == SuperPlayerDef.PlayerMode.FLOAT) {
if (mPlayerViewCallback != null) {
mPlayerViewCallback.onStartFloatWindowPlay();
}
if (mControllerCallback != null) {
mControllerCallback.onSwitchPlayMode(SuperPlayerDef.PlayerMode.FLOAT);
}
}
}
public SuperPlayerDef.PlayerMode getPlayerMode() {
return mSuperPlayer.getPlayerMode();
}
public SuperPlayerDef.PlayerState getPlayerState() {
return mSuperPlayer.getPlayerState();
}
private SuperPlayerObserver mSuperPlayerObserver = new SuperPlayerObserver() {
@Override
public void onPlayBegin(String name) {
mWindowPlayer.updatePlayState(SuperPlayerDef.PlayerState.PLAYING);
mFullScreenPlayer.updatePlayState(SuperPlayerDef.PlayerState.PLAYING);
updateTitle(name);
mWindowPlayer.hideBackground();
if (mDanmuView != null && mDanmuView.isPrepared() && mDanmuView.isPaused()) {
mDanmuView.resume();
}
if (mWatcher != null) {
mWatcher.exitLoading();
}
if(mPlayerViewCallback != null) {
mPlayerViewCallback.onSuperPlayerDidStart();
}
}
@Override
public void onPlayPause() {
mWindowPlayer.updatePlayState(SuperPlayerDef.PlayerState.PAUSE);
mFullScreenPlayer.updatePlayState(SuperPlayerDef.PlayerState.PAUSE);
}
@Override
public void onPlayStop() {
mWindowPlayer.updatePlayState(SuperPlayerDef.PlayerState.END);
mFullScreenPlayer.updatePlayState(SuperPlayerDef.PlayerState.END);
// 清空关键帧和视频打点信息
if (mWatcher != null) {
mWatcher.stop();
}
if(null != mPlayerViewCallback) {
mPlayerViewCallback.onSuperPlayerDidEnd();
}
}
@Override
public void onPlayLoading() {
mWindowPlayer.updatePlayState(SuperPlayerDef.PlayerState.LOADING);
mFullScreenPlayer.updatePlayState(SuperPlayerDef.PlayerState.LOADING);
if (mWatcher != null) {
mWatcher.enterLoading();
}
}
@Override
public void onPlayProgress(long current, long duration) {
mWindowPlayer.updateVideoProgress(current, duration);
mFullScreenPlayer.updateVideoProgress(current, duration);
}
@Override
public void onSeek(int position) {
if (mSuperPlayer.getPlayerType() != SuperPlayerDef.PlayerType.VOD) {
if (mWatcher != null) {
mWatcher.stop();
}
}
}
@Override
public void onSwitchStreamStart(boolean success, SuperPlayerDef.PlayerType playerType, VideoQuality quality) {
if(playerType == SuperPlayerDef.PlayerType.LIVE) {
if (success) {
Toast.makeText(mContext, "正在切换到" + quality.title + "...", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mContext, "切换" + quality.title + "清晰度失败,请稍候重试", Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void onSwitchStreamEnd(boolean success, SuperPlayerDef.PlayerType playerType, VideoQuality quality) {
if (playerType == SuperPlayerDef.PlayerType.LIVE) {
if (success) {
Toast.makeText(mContext, "清晰度切换成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mContext, "清晰度切换失败", Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void onPlayerTypeChange(SuperPlayerDef.PlayerType playType) {
mWindowPlayer.updatePlayType(playType);
mFullScreenPlayer.updatePlayType(playType);
}
@Override
public void onPlayTimeShiftLive(TXLivePlayer player, String url) {
if (mWatcher == null) {
mWatcher = new NetWatcher(mContext);
}
mWatcher.start(url, player);
}
@Override
public void onVideoQualityListChange(List<VideoQuality> videoQualities, VideoQuality defaultVideoQuality) {
mFullScreenPlayer.setVideoQualityList(videoQualities);
mFullScreenPlayer.updateVideoQuality(defaultVideoQuality);
}
@Override
public void onVideoImageSpriteAndKeyFrameChanged(PlayImageSpriteInfo info, List<PlayKeyFrameDescInfo> list) {
mFullScreenPlayer.updateImageSpriteInfo(info);
mFullScreenPlayer.updateKeyFrameDescInfo(list);
}
@Override
public void onError(int code, String message) {
showToast(message);
if(null != mPlayerViewCallback) {
mPlayerViewCallback.onSuperPlayerError();
}
}
};
private void showToast(String message) {
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
}
private void showToast(int resId) {
Toast.makeText(mContext, resId, Toast.LENGTH_SHORT).show();
}
public static void save2MediaStore(Context context, Bitmap image) {
File sdcardDir = context.getExternalFilesDir(null);
if (sdcardDir == null) {
Log.e(TAG, "sdcardDir is null");
return;
}
File appDir = new File(sdcardDir, "superplayer");
if (!appDir.exists()) {
appDir.mkdir();
}
long dateSeconds = System.currentTimeMillis() / 1000;
String fileName = dateSeconds + ".jpg";
File file = new File(appDir, fileName);
String filePath = file.getAbsolutePath();
File f = new File(filePath);
if (f.exists()) {
f.delete();
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(f);
image.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
// Save the screenshot to the MediaStore
ContentValues values = new ContentValues();
ContentResolver resolver = context.getContentResolver();
values.put(MediaStore.Images.ImageColumns.DATA, filePath);
values.put(MediaStore.Images.ImageColumns.TITLE, fileName);
values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.ImageColumns.DATE_ADDED, dateSeconds);
values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, dateSeconds);
values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.ImageColumns.WIDTH, image.getWidth());
values.put(MediaStore.Images.ImageColumns.HEIGHT, image.getHeight());
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
OutputStream out = resolver.openOutputStream(uri);
image.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
// update file size in the database
values.clear();
values.put(MediaStore.Images.ImageColumns.SIZE, new File(filePath).length());
resolver.update(uri, values, null, null);
} catch (Exception e) {
TXCLog.e(TAG, Log.getStackTraceString(e));
}
}
public void disableGesture(boolean flag) {
if(null != mFullScreenPlayer) {
mFullScreenPlayer.disableGesture(flag);
}
if(null != mWindowPlayer) {
mWindowPlayer.disableGesture(flag);
}
}
public void setIsAutoPlay(boolean b) {
mSuperPlayer.setAutoPlay(b);
}
public void setStartTime(double startTime) {
mSuperPlayer.setStartTime((float) startTime);
}
public void setLoop(boolean b) {
mSuperPlayer.setLoop(b);
}
}
......@@ -2,7 +2,8 @@
part of demo_super_player_lib;
final double topBottomOffset = 0;
DeviceOrientation landscapeOrientation = DeviceOrientation.landscapeLeft;
int manualOrientationDirection = TXVodPlayEvent.ORIENTATION_LANDSCAPE_RIGHT;
FullScreenController _fullScreenController = FullScreenController();
/// superplayer view widget
class SuperPlayerView extends StatefulWidget {
......@@ -40,7 +41,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
late _BottomViewController _bottomViewController;
late _QualityListViewController _qualitListViewController;
late _VideoTitleController _titleViewController;
late _SuperPlayerFullScreenController _fullScreenController;
late _SuperPlayerFullScreenController _superPlayerFullUIController;
late _CoverViewController _coverViewController;
late _MoreViewController _moreViewController;
......@@ -59,14 +60,8 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
@override
void initState() {
super.initState();
// ios need landscapeRight,android need landscapeLeft
if (defaultTargetPlatform == TargetPlatform.iOS) {
landscapeOrientation = DeviceOrientation.landscapeRight;
} else {
landscapeOrientation = DeviceOrientation.landscapeLeft;
}
_playController = widget._controller;
_fullScreenController = _SuperPlayerFullScreenController(_updateState);
_superPlayerFullUIController = _SuperPlayerFullScreenController(_updateState);
_titleViewController = _VideoTitleController(_onTapBack, () {
_moreViewKey.currentState?.toggleShowMoreView();
});
......@@ -143,6 +138,11 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
_onResume();
}
}
// 画中画模式下,不进行旋转屏幕操作
if (eventCode == TXVodPlayEvent.EVENT_ORIENTATION_CHANGED && !_isFloatingMode) {
int orientation = event[TXVodPlayEvent.EXTRA_NAME_ORIENTATION];
_fullScreenController.switchToOrientation(orientation);
}
});
_registerObserver();
_initPlayerState();
......@@ -214,6 +214,22 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
// onDispose
_playController._observer = null; // close observer
});
_fullScreenController.setListener(() {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return SuperPlayerFullScreenView(_playController, _superPlayerFullUIController);
}));
WidgetsBinding.instance?.removeObserver(this);
_videoBottomKey.currentState?.updateFullScreen(true);
_videoTitleKey.currentState?.updateFullScreen(true);
_playController._updateFullScreenState(true);
hideControlView();
}, () {
Navigator.of(context).pop();
_videoBottomKey.currentState?.updateFullScreen(false);
_videoTitleKey.currentState?.updateFullScreen(false);
_playController._updateFullScreenState(false);
hideControlView();
});
WidgetsBinding.instance?.addObserver(this);
}
......@@ -238,11 +254,6 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
_isLoading = true;
break;
}
if (_isFullScreen) {
SystemChrome.setPreferredOrientations([landscapeOrientation]);
} else {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
}
}
void _updateState() {
......@@ -268,10 +279,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
Orientation currentOrientation = MediaQuery.of(context).orientation;
bool isLandscape = currentOrientation == Orientation.landscape;
if (!isLandscape) {
///关闭状态栏,与RootViewController底部虚拟操作按钮
SystemChrome.setPreferredOrientations([landscapeOrientation]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
AutoOrientation.landscapeAutoMode();
_fullScreenController.forceSwitchOrientation(manualOrientationDirection);
}
}
} else if (state == AppLifecycleState.inactive) {
......@@ -467,7 +475,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
forwardIcon: "images/ic_pip_play_forward.png");
if (null != result) {
String failedStr = "";
if(result != TXVodPlayEvent.NO_ERROR) {
if (result != TXVodPlayEvent.NO_ERROR) {
if (result == TXVodPlayEvent.ERROR_PIP_LOWER_VERSION) {
failedStr = "enterPip failed,because android version is too low,Minimum supported version is android 24";
} else if (result == TXVodPlayEvent.ERROR_PIP_DENIED_PERMISSION) {
......@@ -546,37 +554,17 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
}
}
void _fullScreenOrientation() {
if (_isFullScreen) {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return SuperPlayerFullScreenView(_playController, _fullScreenController);
}));
WidgetsBinding.instance?.removeObserver(this);
void _handleFullScreen(bool toSwitchFullScreen) {
if (toSwitchFullScreen) {
_fullScreenController.switchToOrientation(manualOrientationDirection);
} else {
// exit fullscreen widget
Navigator.of(context).pop();
///显示状态栏,与底部虚拟操作按钮
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values);
AutoOrientation.portraitAutoMode();
/// 隐藏moreView
_moreViewKey.currentState?.hideShowMoreView();
_fullScreenController.switchToOrientation(TXVodPlayEvent.ORIENTATION_PORTRAIT_UP);
}
_videoBottomKey.currentState?.updateFullScreen(_isFullScreen);
_videoTitleKey.currentState?.updateFullScreen(_isFullScreen);
}
void _onControlFullScreen() {
_isFullScreen = !_isFullScreen;
if (_isFullScreen) {
hideControlView();
_fullScreenOrientation();
} else {
hideControlView();
_fullScreenOrientation();
}
_handleFullScreen(_isFullScreen);
_playController._updateFullScreenState(_isFullScreen);
}
......@@ -625,6 +613,8 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
if (!_isShowControlView || !mounted) {
return;
}
/// 隐藏moreView
_moreViewKey.currentState?.hideShowMoreView();
setState(() {
_isShowQualityListView = false;
_isShowControlView = false;
......@@ -654,11 +644,6 @@ class SuperPlayerFullScreenState extends State<SuperPlayerFullScreenView> {
@override
void initState() {
super.initState();
///关闭状态栏,与底部虚拟操作按钮
SystemChrome.setPreferredOrientations([landscapeOrientation]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
AutoOrientation.landscapeAutoMode();
}
@override
......@@ -834,3 +819,58 @@ class _SuperPlayerFloatState extends State<SuperPlayerFloatView> {
sizeStreamSubscription?.cancel();
}
}
class FullScreenController {
bool _isInFullScreenUI = false;
int currentOrientation = TXVodPlayEvent.ORIENTATION_PORTRAIT_UP;
Function? onEnterFullScreenUI;
Function? onExitFullScreenUI;
FullScreenController();
void switchToOrientation(int orientationDirection) {
if (currentOrientation != orientationDirection) {
forceSwitchOrientation(orientationDirection);
}
}
void forceSwitchOrientation(int orientationDirection) {
currentOrientation = orientationDirection;
if (orientationDirection == TXVodPlayEvent.ORIENTATION_PORTRAIT_UP) {
exitFullScreen();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values);
AutoOrientation.portraitUpMode();
} else if (orientationDirection == TXVodPlayEvent.ORIENTATION_LANDSCAPE_RIGHT) {
enterFullScreen();
SystemChrome.setPreferredOrientations(Platform.isIOS ? [DeviceOrientation.landscapeRight] : [DeviceOrientation.landscapeLeft]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
AutoOrientation.landscapeRightMode();
} else if (orientationDirection == TXVodPlayEvent.ORIENTATION_PORTRAIT_DOWN) {
} else if (orientationDirection == TXVodPlayEvent.ORIENTATION_LANDSCAPE_LEFT) {
enterFullScreen();
SystemChrome.setPreferredOrientations(Platform.isIOS ? [DeviceOrientation.landscapeLeft] : [DeviceOrientation.landscapeRight]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
AutoOrientation.landscapeLeftMode();
}
}
void enterFullScreen() {
if (!_isInFullScreenUI) {
_isInFullScreenUI = true;
onEnterFullScreenUI?.call();
}
}
void exitFullScreen() {
if (_isInFullScreenUI) {
_isInFullScreenUI = false;
onExitFullScreenUI?.call();
}
}
void setListener(Function enterfullScreen, Function exitFullScreen) {
this.onExitFullScreenUI = exitFullScreen;
this.onEnterFullScreenUI = enterfullScreen;
}
}
......@@ -16,6 +16,8 @@
[volumeView sizeToFit];
// 单例slider
_volumeSlider = nil;
//开始接收遥控事件
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
for (UIView *view in [volumeView subviews]) {
if ([view.class.description isEqualToString:@"MPVolumeSlider"]) {
_volumeSlider = (UISlider *)view;
......
......@@ -28,27 +28,6 @@
// 画中画事件code
#define EVENT_PIP_MODE_ALREADY_ENTER 1
#define EVENT_PIP_MODE_ALREADY_EXIT 2
#define EVENT_PIP_MODE_REQUEST_START 3
#define EVENT_PIP_MODE_UI_STATE_CHANGED 4
#define EVENT_PIP_MODE_RESTORE_UI 5
#define EVENT_PIP_MODE_WILL_EXIT 6
// 画中画错误事件code
#define NO_ERROR 0 ///< 无错误
#define ERROR_IOS_PIP_DEVICE_NOT_SUPPORT -104 ///< 设备或系统版本不支持(iPad iOS9+ 才支持PIP)
#define ERROR_IOS_PIP_PLAYER_NOT_SUPPORT -105 ///< 播放器不支持
#define ERROR_IOS_PIP_VIDEO_NOT_SUPPORT -106 ///< 视频不支持
#define ERROR_IOS_PIP_IS_NOT_POSSIBLE -107 ///< PIP控制器不可用
#define ERROR_IOS_PIP_FROM_SYSTEM -108 ///< PIP控制器报错
#define ERROR_IOS_PIP_PLAYER_NOT_EXIST -109 ///< 播放器对象不存在
#define ERROR_IOS_PIP_IS_RUNNING -110 ///< PIP功能已经运行
#define ERROR_IOS_PIP_NOT_RUNNING -111 ///< PIP功能没有启动
// 视频预下载完成
#define EVENT_PREDOWNLOAD_ON_COMPLETE 200
// 视频预下载出错
......@@ -65,4 +44,17 @@
// 视频下载错误
#define EVENT_DOWNLOAD_ERROR 305
// 屏幕旋转事件
#define EVENT_ORIENTATION_CHANGED 401
// 屏幕旋转方向
#define EXTRA_NAME_ORIENTATION @"orientation"
// 正竖屏
#define ORIENTATION_PORTRAIT_UP 411
// 横屏,底部在右
#define ORIENTATION_LANDSCAPE_RIGHT 412
// 竖屏,顶部在下
#define ORIENTATION_PORTRAIT_DOWN 413
// 横屏,底部在左
#define ORIENTATION_LANDSCAPE_LEFT 414
#endif /* FTXEvent_h */
......@@ -25,6 +25,7 @@
FTXPlayerEventSinkQueue *_pipEventSink;
FTXAudioManager *audioManager;
FTXDownloadManager *_FTXDownloadManager;
int mCurrentOrientation;
}
SuperPlayerPlugin* instance;
......@@ -44,6 +45,7 @@ SuperPlayerPlugin* instance;
if (nil != _FTXDownloadManager) {
[_FTXDownloadManager destroy];
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (instancetype)initWithRegistrar:
......@@ -64,9 +66,16 @@ SuperPlayerPlugin* instance;
_pipEventChannel = [FlutterEventChannel eventChannelWithName:@"cloud.tencent.com/playerPlugin/pipEvent" binaryMessenger:[registrar messenger]];
[_eventChannel setStreamHandler:self];
[_pipEventChannel setStreamHandler:self];
[audioManager registerVolumeChangeListener:self selector:@selector(systemVolumeDidChangeNoti:) name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil];
_FTXDownloadManager = [[FTXDownloadManager alloc] initWithRegistrar:registrar];
// orientation
mCurrentOrientation = ORIENTATION_PORTRAIT_UP;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onDeviceOrientationChange:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
return self;
}
......@@ -204,6 +213,7 @@ SuperPlayerPlugin* instance;
if ([arguments isKindOfClass:NSString.class]) {
if ([arguments isEqualToString:@"event"]) {
[_eventSink setDelegate:events];
} else if ([arguments isEqualToString:@"pipEvent"]) {
[_pipEventSink setDelegate:events];
}
}
......@@ -216,6 +226,7 @@ SuperPlayerPlugin* instance;
if ([arguments isKindOfClass:NSString.class]) {
if ([arguments isEqualToString:@"event"]) {
[_eventSink setDelegate:nil];
} else if ([arguments isEqualToString:@"pipEvent"]) {
[_pipEventSink setDelegate:nil];
}
}
......@@ -248,4 +259,39 @@ SuperPlayerPlugin* instance;
[_pipEventSink success:@{@"event" : @(EVENT_PIP_MODE_RESTORE_UI)}];
}
#pragma mark - orientation
- (void)onDeviceOrientationChange:(NSNotification *)notification {
// IOS 此处不需要判断是否打开自动屏幕旋转/竖排锁定开关,当IOS打开锁定之后,这里默认是收不到回调的
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
UIInterfaceOrientation interfaceOrientation = (UIInterfaceOrientation)orientation;
int tempOrientationCode = mCurrentOrientation;
switch (interfaceOrientation) {
case UIInterfaceOrientationPortrait:
// 电池栏在上
tempOrientationCode = ORIENTATION_PORTRAIT_UP;
break;
case UIInterfaceOrientationLandscapeLeft:
// 电池栏在左
tempOrientationCode = ORIENTATION_LANDSCAPE_LEFT;
break;
case UIInterfaceOrientationPortraitUpsideDown:
// 电池栏在下
tempOrientationCode = ORIENTATION_PORTRAIT_DOWN;
break;
case UIInterfaceOrientationLandscapeRight:
// 电池栏在右
tempOrientationCode = ORIENTATION_LANDSCAPE_RIGHT;
break;
default:
break;
}
if(tempOrientationCode != mCurrentOrientation) {
mCurrentOrientation = tempOrientationCode;
[_eventSink success:@{
@"event" : @(EVENT_ORIENTATION_CHANGED),
EXTRA_NAME_ORIENTATION : @(tempOrientationCode)}];
}
}
@end
......@@ -27,7 +27,7 @@ class SuperPlayerPlugin {
eventChannel.receiveBroadcastStream("event").listen(_eventHandler, onError: _errorHandler);
EventChannel pipEventChanne = EventChannel("cloud.tencent.com/playerPlugin/pipEvent");
pipEventChanne.receiveBroadcastStream("event").listen(_pipEventHandler, onError: _errorHandler);
pipEventChanne.receiveBroadcastStream("pipEvent").listen(_pipEventHandler, onError: _errorHandler);
}
_pipEventHandler(event) {
......
......@@ -97,6 +97,12 @@ abstract class TXVodPlayEvent {
static const EVENT_IOS_PIP_MODE_RESTORE_UI = 5; // 重置UI only support iOS
static const EVENT_IOS_PIP_MODE_WILL_EXIT = 6; // 将要退出画中画 only support iOS
static const EVENT_ORIENTATION_CHANGED = 401; // 屏幕发生旋转
static const EXTRA_NAME_ORIENTATION = "orientation"; // 屏幕旋转方向
static const ORIENTATION_PORTRAIT_UP = 411; // 竖屏,顶部在上
static const ORIENTATION_LANDSCAPE_RIGHT = 412; // 横屏,顶部在左,底部在右
static const ORIENTATION_PORTRAIT_DOWN = 413; // 竖屏,顶部在下
static const ORIENTATION_LANDSCAPE_LEFT = 414; // 横屏,顶部在右,底部在左
static const NO_ERROR = 0;
static const ERROR_PIP_LOWER_VERSION = -101; // pip 错误,android版本过低
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论