diff --git a/Video.js b/Video.js index 3e52592c..86a7c48d 100644 --- a/Video.js +++ b/Video.js @@ -4,7 +4,6 @@ const { StyleSheet, requireNativeComponent, PropTypes, - NativeModules, } = React; const VideoResizeMode = require('./VideoResizeMode'); @@ -50,16 +49,16 @@ class Video extends Component { let nativeResizeMode; if (resizeMode === VideoResizeMode.stretch) { - nativeResizeMode = NativeModules.VideoManager.ScaleToFill; + nativeResizeMode = RCTVideo.ScaleToFill; } else if (resizeMode === VideoResizeMode.contain) { - nativeResizeMode = NativeModules.VideoManager.ScaleAspectFit; + nativeResizeMode = RCTVideo.ScaleAspectFit; } else if (resizeMode === VideoResizeMode.cover) { - nativeResizeMode = NativeModules.VideoManager.ScaleAspectFill; + nativeResizeMode = RCTVideo.ScaleAspectFill; } else { - nativeResizeMode = NativeModules.VideoManager.ScaleNone; + nativeResizeMode = RCTVideo.ScaleNone; } - let nativeProps = Object.assign({}, this.props); + const nativeProps = Object.assign({}, this.props); Object.assign(nativeProps, { style: [styles.base, style], resizeMode: nativeResizeMode, @@ -67,7 +66,7 @@ class Video extends Component { uri: uri, isNetwork, isAsset, - type: source.type || 'mp4' + type: source.type || 'mp4', }, seek: seekTime, onVideoLoad: this._onLoad, @@ -135,7 +134,7 @@ Video.propTypes = { rotation: React.PropTypes.number, }; -var RCTVideo = requireNativeComponent('RCTVideo', Video, { +const RCTVideo = requireNativeComponent('RCTVideo', Video, { nativeOnly: { src: true, seek: true, diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoPackage.java b/android/src/main/java/com/brentvatne/react/ReactVideoPackage.java index 4bc2bf37..22c48a00 100644 --- a/android/src/main/java/com/brentvatne/react/ReactVideoPackage.java +++ b/android/src/main/java/com/brentvatne/react/ReactVideoPackage.java @@ -13,12 +13,6 @@ import java.util.List; public class ReactVideoPackage implements ReactPackage { - private Activity mActivity = null; - - public ReactVideoPackage(Activity activity) { - mActivity = activity; - } - @Override public List createNativeModules(ReactApplicationContext reactContext) { return Collections.emptyList(); @@ -31,6 +25,6 @@ public class ReactVideoPackage implements ReactPackage { @Override public List createViewManagers(ReactApplicationContext reactContext) { - return Arrays.asList(new ReactVideoViewManager(mActivity)); + return Arrays.asList(new ReactVideoViewManager()); } } diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoViewManager.java b/android/src/main/java/com/brentvatne/react/ReactVideoViewManager.java index f1bc48bc..a834ceff 100644 --- a/android/src/main/java/com/brentvatne/react/ReactVideoViewManager.java +++ b/android/src/main/java/com/brentvatne/react/ReactVideoViewManager.java @@ -1,37 +1,168 @@ package com.brentvatne.react; -import android.app.Activity; +import android.graphics.Color; +import android.media.MediaPlayer; +import android.widget.MediaController; import android.net.Uri; import android.support.annotation.Nullable; +import android.widget.FrameLayout; import android.widget.VideoView; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.common.MapBuilder; import com.facebook.react.uimanager.ReactProp; import com.facebook.react.uimanager.SimpleViewManager; import com.facebook.react.uimanager.ThemedReactContext; -public class ReactVideoViewManager extends SimpleViewManager { +import java.util.Map; + +import static junit.framework.Assert.assertTrue; + +public class ReactVideoViewManager extends SimpleViewManager { public static final String REACT_CLASS = "RCTVideo"; private static final String PROP_SRC = "src"; + private static final String PROP_RESIZE_MODE = "resizeMode"; + private static final String PROP_REPEAT = "repeat"; + private static final String PROP_PAUSED = "paused"; - private Activity mActivity = null; - - public ReactVideoViewManager(Activity activity) { - mActivity = activity; + private enum ResizeMode { + SCALE_NONE, SCALE_TO_FILL, SCALE_ASPECT_FIT, SCALE_ASPECT_FILL } + private boolean mRepeat = false; + private boolean mPaused = false; + @Override public String getName() { return REACT_CLASS; } @Override - protected VideoView createViewInstance(ThemedReactContext themedReactContext) { - return new VideoView(themedReactContext); + protected FrameLayout createViewInstance(ThemedReactContext themedReactContext) { + final FrameLayout container = new FrameLayout(themedReactContext); + final VideoView videoView = new VideoView(themedReactContext); + + MediaController mediaController = new MediaController(themedReactContext); + mediaController.setAnchorView(videoView); + mediaController.setVisibility(VideoView.GONE); + videoView.setMediaController(mediaController); + videoView.setLayoutParams(new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT)); + container.addView(videoView); + + return container; + } + + @Override + public + @Nullable + Map getExportedViewConstants() { + return MapBuilder.of( + "ScaleNone", ResizeMode.SCALE_NONE.ordinal(), + "ScaleToFill", ResizeMode.SCALE_TO_FILL.ordinal(), + "ScaleAspectFit", ResizeMode.SCALE_ASPECT_FIT.ordinal(), + "ScaleAspectFill", ResizeMode.SCALE_ASPECT_FILL.ordinal()); } @ReactProp(name = PROP_SRC) - public void setSrc(VideoView videoView, @Nullable ReadableMap src) { - videoView.setVideoURI(Uri.parse(src.getString("uri"))); + public void setSrc(final FrameLayout container, @Nullable ReadableMap src) { + final VideoView videoView = (VideoView) container.getChildAt(0); + + try { + final String uriString = src.getString("uri"); + final boolean isNetwork = src.getBoolean("isNetwork"); + + videoView.stopPlayback(); + + if (isNetwork) { + videoView.setVideoPath(uriString); + videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + @Override + public void onPrepared(MediaPlayer mp) { + applyModifiers(container); + } + }); + } else { + videoView.setVideoURI(Uri.parse("android.resource://" + videoView.getContext().getPackageName() + + "/raw/" + uriString)); + applyModifiers(container); + } + } catch (Exception e) { + assertTrue("failed to set video source", false); + } + } + + @ReactProp(name = PROP_RESIZE_MODE) + public void setResizeMode(final FrameLayout container, int resizeModeOrdinal) { + final VideoView videoView = (VideoView) container.getChildAt(0); + + try { + final ResizeMode resizeMode = ResizeMode.values()[resizeModeOrdinal]; + + FrameLayout.LayoutParams layoutParams = null; + switch (resizeMode) { + case SCALE_NONE: + layoutParams = new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT); + break; + + case SCALE_TO_FILL: + layoutParams = new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT); + break; + + case SCALE_ASPECT_FIT: + break; + + case SCALE_ASPECT_FILL: + break; + } + + if (layoutParams != null) { + videoView.setLayoutParams(layoutParams); + container.updateViewLayout(videoView, layoutParams); + } + } catch (Exception e) { + assertTrue("failed to set video resize mode", false); + } + } + + @ReactProp(name = PROP_REPEAT) + public void setRepeat(final FrameLayout container, final boolean repeat) { + mRepeat = repeat; + + if (!mRepeat) { return; } + + final VideoView videoView = (VideoView) container.getChildAt(0); + videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + @Override + public void onCompletion(MediaPlayer mp) { + + if (mRepeat) { + mp.seekTo(0); + setPaused(container, mPaused); + } + } + }); + } + + @ReactProp(name = PROP_PAUSED) + public void setPaused(final FrameLayout container, final boolean paused) { + final VideoView videoView = (VideoView) container.getChildAt(0); + videoView.requestFocus(); + if (!paused) { + videoView.start(); + } else { + videoView.pause(); + } + mPaused = paused; + } + + private void applyModifiers(final FrameLayout container) { + setRepeat(container, mRepeat); + setPaused(container, mPaused); } }