Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android fullscreen via bridging #38

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,36 @@ All other props are passed to the react-native-video component.
- [x] Make player customizable.
- [ ] Add volume control
- [X] Add fullscreen button
- [ ] Add fullscreen button for android
- [X] Add fullscreen button for android
- [ ] Add loader

## Setting up fullscreen on Android

Step 1:

Go to your ```android\app\src\main\java\your\package\directory``` folder where you can find ```MainActivity.java```. Copy the java files from the repo's ```android\app\src\main\java``` folder and paste them there. Open those files in any editor(Android Studio recommended) and change the package names according to your project. After that, go to your ```MainApplication.java``` file
and under the ```new MainReactPackage()```, copy and paste this: ```new BridgePackage()``` and it should look similar to the code below if you do not have other packages.
```
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new BridgePackage()
);
}
```
Step 2:

Make a folder in your ```android\app\src\main\res``` directory and name it ```layout```, then copy the player_fullscreen.xml from the repo's ```android\app\src\main\res\layout``` directory and paste it into your directory and then go to your ```AndroidManifest.xml``` and add this before the ending application tag:
```
<activity android:name=".VideoActivity"
android:screenOrientation="sensorLandscape"
android:configChanges="orientation|screenSize"
/>
```


If you want to remove the action bar, change your theme or change the theme for your activity from the manifest

And then your fullscreen should be working and ready to go!

28 changes: 28 additions & 0 deletions android/app/src/main/java/BridgeModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.my.package;

import android.content.Context;
import android.content.Intent;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

public class BridgeModule extends ReactContextBaseJavaModule{
public BridgeModule(ReactApplicationContext reactContext) {
super(reactContext);
}

@Override
public String getName() {
return "BridgeModule";
}
@ReactMethod
public void showFullscreen(String videoUri) {
Context context = getReactApplicationContext();
Intent intent = new Intent(context, VideoActivity.class); // mContext got from your overriden constructor
intent.putExtra("VIDEO_URL",videoUri);
getCurrentActivity().startActivity(intent);
}
}
36 changes: 36 additions & 0 deletions android/app/src/main/java/BridgePackage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.my.package;


import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class BridgePackage implements ReactPackage {

@Override
Copy link

@EthanLin-TWer EthanLin-TWer Oct 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from RN 0.47.x on the createJSModules() method has been removed and thus the @Override implement here will fail the gradle compilation. Simply remove the @Override and keep the method call should resolve this issue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks man!

public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}

@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();

modules.add(new BridgeModule(reactContext));

return modules;
}

}
67 changes: 67 additions & 0 deletions android/app/src/main/java/VideoActivity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.my.package;

import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.media.MediaPlayer;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.MediaController;
import android.widget.Toast;
import android.widget.VideoView;

public class VideoActivity extends AppCompatActivity {
private String videoPath;

private static ProgressDialog progressDialog;
VideoView myVideoView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
setContentView(R.layout.player_fullscreen);
Intent i = getIntent();
if(i != null){
myVideoView = (VideoView) findViewById(R.id.videoView);
videoPath = i.getStringExtra("VIDEO_URL");
progressDialog = ProgressDialog.show(VideoActivity.this, "", "Buffering video...", true);
progressDialog.setCancelable(true);
PlayVideo();
}
else{
Toast.makeText(VideoActivity.this, "VideoURL not found", Toast.LENGTH_SHORT).show();
}


}

private void PlayVideo() {
try {
getWindow().setFormat(PixelFormat.TRANSLUCENT);
MediaController mediaController = new MediaController(VideoActivity.this);
mediaController.setAnchorView(myVideoView);

Uri video = Uri.parse(videoPath);
myVideoView.setMediaController(mediaController);
myVideoView.setVideoURI(video);
myVideoView.requestFocus();
myVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
progressDialog.dismiss();
myVideoView.start();
}
});


} catch (Exception e) {
progressDialog.dismiss();
System.out.println("Video Play Error :" + e.toString());
finish();
}

}
}
18 changes: 18 additions & 0 deletions android/app/src/main/res/layout/player_fullscreen.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".VideoActivity">

<VideoView
android:id="@+id/videoView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
/>
</RelativeLayout>
97 changes: 48 additions & 49 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Image, Platform, StyleSheet, TouchableOpacity, View, ViewPropTypes } from 'react-native';
import React, { Component, PropTypes } from 'react';
import { View, StyleSheet, Image, TouchableOpacity, Platform, NativeModules } from 'react-native';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reasons for changing to use the built-in PropTypes instead of the separate prop-types library?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

built-in PropTypes are breaking since react does not support them, we should use separate prop-types library.

import Icon from 'react-native-vector-icons/MaterialIcons';
import Video from 'react-native-video'; // eslint-disable-line

import Video from 'react-native-video';
const styles = StyleSheet.create({
preloadingPlaceholder: {
backgroundColor: 'black',
Expand All @@ -26,7 +24,7 @@ const styles = StyleSheet.create({
playArrow: {
color: 'white',
},
video: {
video: Platform.Version >= 24 ? {} : {
backgroundColor: 'black',
},
controls: {
Expand Down Expand Up @@ -84,6 +82,9 @@ const styles = StyleSheet.create({
});

export default class VideoPlayer extends Component {



constructor(props) {
super(props);

Expand Down Expand Up @@ -131,6 +132,14 @@ export default class VideoPlayer extends Component {
}
}

stop() {
//console.log('this in the video', this);
//this.player.seek(0);
this.setState({
isPlaying: false,
});
}

onLayout(event) {
const { width } = event.nativeEvent.layout;
this.setState({
Expand All @@ -139,16 +148,15 @@ export default class VideoPlayer extends Component {
}

onStartPress() {
if (this.props.onStart) {
this.props.onStart();
}

this.setState({
isPlaying: true,
isStarted: true,
});

this.hideControls();
if(this.props.onStartPress){
this.props.onStartPress(this);
};
}

onProgress(event) {
Expand All @@ -172,8 +180,6 @@ export default class VideoPlayer extends Component {
this.setState({ isStarted: false });
}

this.setState({ progress: 1 });

this.player.seek(0);
if (!this.props.loop) {
this.setState({
Expand All @@ -192,14 +198,14 @@ export default class VideoPlayer extends Component {
}

onPlayPress() {
if (this.props.onPlayPress) {
this.props.onPlayPress();
}

console.log('onPlayPress :: videoComponent')
this.setState({
isPlaying: !this.state.isPlaying,
});
this.showControls();
if(this.props.onPlayPress){
this.props.onPlayPress();
};
}

onMutePress() {
Expand All @@ -210,7 +216,15 @@ export default class VideoPlayer extends Component {
}

onToggleFullScreen() {
this.player.presentFullscreenPlayer();
if(Platform.OS === "android")
{
var uri = this.props.video.uri;
NativeModules.BridgeModule.showFullscreen(uri);
}
else
{
this.player.presentFullscreenPlayer();
}
}

onSeekBarLayout({ nativeEvent }) {
Expand Down Expand Up @@ -277,10 +291,6 @@ export default class VideoPlayer extends Component {
}

hideControls() {
if (this.props.onHideControls) {
this.props.onHideControls();
}

if (this.props.disableControlsAutoHide) {
return;
}
Expand All @@ -295,10 +305,6 @@ export default class VideoPlayer extends Component {
}

showControls() {
if (this.props.onShowControls) {
this.props.onShowControls();
}

this.setState({
isControlsVisible: true,
});
Expand Down Expand Up @@ -404,15 +410,13 @@ export default class VideoPlayer extends Component {
/>
</TouchableOpacity>
)}
{(Platform.OS === 'android' || this.props.disableFullscreen) ? null : (
<TouchableOpacity onPress={this.onToggleFullScreen} style={customStyles.controlButton}>
<Icon
style={[styles.extraControl, customStyles.controlIcon]}
name="fullscreen"
size={32}
/>
</TouchableOpacity>
)}
<TouchableOpacity onPress={this.onToggleFullScreen} style={customStyles.controlButton}>
<Icon
style={[styles.extraControl, customStyles.controlIcon]}
name="fullscreen"
size={32}
/>
</TouchableOpacity>
</View>
);
}
Expand Down Expand Up @@ -492,40 +496,35 @@ VideoPlayer.propTypes = {
autoplay: PropTypes.bool,
defaultMuted: PropTypes.bool,
muted: PropTypes.bool,
style: ViewPropTypes.style,
style: View.propTypes.style,
controlsTimeout: PropTypes.number,
disableControlsAutoHide: PropTypes.bool,
disableFullscreen: PropTypes.bool,
loop: PropTypes.bool,
resizeMode: Video.propTypes.resizeMode,
hideControlsOnStart: PropTypes.bool,
endWithThumbnail: PropTypes.bool,
customStyles: PropTypes.shape({
wrapper: ViewPropTypes.style,
wrapper: View.propTypes.style,
video: Video.propTypes.style,
videoWrapper: ViewPropTypes.style,
controls: ViewPropTypes.style,
videoWrapper: View.propTypes.style,
controls: View.propTypes.style,
playControl: TouchableOpacity.propTypes.style,
controlButton: TouchableOpacity.propTypes.style,
controlIcon: Icon.propTypes.style,
playIcon: Icon.propTypes.style,
seekBar: ViewPropTypes.style,
seekBarFullWidth: ViewPropTypes.style,
seekBarProgress: ViewPropTypes.style,
seekBarKnob: ViewPropTypes.style,
seekBarKnobSeeking: ViewPropTypes.style,
seekBarBackground: ViewPropTypes.style,
seekBar: View.propTypes.style,
seekBarFullWidth: View.propTypes.style,
seekBarProgress: View.propTypes.style,
seekBarKnob: View.propTypes.style,
seekBarKnobSeeking: View.propTypes.style,
seekBarBackground: View.propTypes.style,
thumbnail: Image.propTypes.style,
playButton: TouchableOpacity.propTypes.style,
playArrow: Icon.propTypes.style,
}),
onEnd: PropTypes.func,
onProgress: PropTypes.func,
onLoad: PropTypes.func,
onStart: PropTypes.func,
onPlayPress: PropTypes.func,
onHideControls: PropTypes.func,
onShowControls: PropTypes.func,
};

VideoPlayer.defaultProps = {
Expand Down