提交 caf58ffa authored 作者: kongdywang's avatar kongdywang

SuperPlayer supports screen autorotation for Android&iOS platform

上级 5f170bba
......@@ -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();
......
......@@ -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();
});
......@@ -90,8 +85,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
}
});
// only register listen once
_pipSubscription =
SuperPlayerPlugin.instance.onExtraEventBroadcast.listen((event) {
_pipSubscription = SuperPlayerPlugin.instance.onExtraEventBroadcast.listen((event) {
int eventCode = event["event"];
if (eventCode == TXVodPlayEvent.EVENT_PIP_MODE_ALREADY_EXIT) {
// exit floatingMode
......@@ -133,8 +127,7 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
print('$eventCode');
}
});
_volumeSubscription =
SuperPlayerPlugin.instance.onEventBroadcast.listen((event) {
_volumeSubscription = SuperPlayerPlugin.instance.onEventBroadcast.listen((event) {
int eventCode = event["event"];
if (_isFloatingMode && _isPlaying) {
if (eventCode == TXVodPlayEvent.EVENT_AUDIO_FOCUS_PAUSE) {
......@@ -143,6 +136,12 @@ 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 +213,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 +253,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 +278,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 +474,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 +553,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 +612,8 @@ class SuperPlayerViewState extends State<SuperPlayerView> with WidgetsBindingObs
if (!_isShowControlView || !mounted) {
return;
}
/// 隐藏moreView
_moreViewKey.currentState?.hideShowMoreView();
setState(() {
_isShowQualityListView = false;
_isShowControlView = false;
......@@ -654,11 +643,6 @@ class SuperPlayerFullScreenState extends State<SuperPlayerFullScreenView> {
@override
void initState() {
super.initState();
///关闭状态栏,与底部虚拟操作按钮
SystemChrome.setPreferredOrientations([landscapeOrientation]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
AutoOrientation.landscapeAutoMode();
}
@override
......@@ -834,3 +818,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;
......
......@@ -44,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 */
......@@ -24,6 +24,7 @@
FTXPlayerEventSinkQueue *_pipEventSink;
FTXAudioManager *audioManager;
FTXDownloadManager *_FTXDownloadManager;
int mCurrentOrientation;
}
SuperPlayerPlugin* instance;
......@@ -43,6 +44,7 @@ SuperPlayerPlugin* instance;
if (nil != _FTXDownloadManager) {
[_FTXDownloadManager destroy];
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (instancetype)initWithRegistrar:
......@@ -62,10 +64,18 @@ SuperPlayerPlugin* instance;
_eventChannel = [FlutterEventChannel eventChannelWithName:@"cloud.tencent.com/playerPlugin/event" binaryMessenger:[registrar messenger]];
_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];
// pip
[_pipEventChannel setStreamHandler:self];
// download
_FTXDownloadManager = [[FTXDownloadManager alloc] initWithRegistrar:registrar];
// orientation
mCurrentOrientation = ORIENTATION_PORTRAIT_UP;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onDeviceOrientationChange:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
return self;
}
......@@ -203,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];
}
}
......@@ -215,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];
}
}
......@@ -247,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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论