Skip to content
This repository has been archived by the owner on Dec 5, 2022. It is now read-only.

Commit

Permalink
play in background with playback notification implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
nklhtv committed Jun 21, 2019
1 parent 20b4311 commit 5176693
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.stellarscript.vlcvideo;

import android.content.Intent;

import java.util.HashSet;
import java.util.Set;

public class VLCVideoCallbackManager {

interface IntentCallback {
boolean onNewIntent(final Intent intent);
}

private Set<IntentCallback> callbacks;

public VLCVideoCallbackManager() {
callbacks = new HashSet<>();
}

public boolean onNewIntent(final Intent intent) {
boolean handled = false;
for (final IntentCallback callback : callbacks) {
if (callback.onNewIntent(intent)) {
handled = true;
}
}

return handled;
}

void addCallback(final IntentCallback callback) {
callbacks.add(callback);
}

void removeCallback(final IntentCallback callback) {
callbacks.remove(callback);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,40 @@ public final class VLCVideoPackage implements ReactPackage {

private final View.OnKeyListener mOnKeyListener;
private final LibVLC mLibVLC;
private final VLCVideoCallbackManager mCallbackManager;

public VLCVideoPackage(final Application application) {
this(application, DEFAULT_VLC_OPTIONS, null);
this(application, DEFAULT_VLC_OPTIONS, null, null);
}

public VLCVideoPackage(final Application application, final ArrayList<String> libVLCOptions) {
this(application, libVLCOptions, null, null);
}

public VLCVideoPackage(final Application application, final View.OnKeyListener onKeyListener) {
this(application, DEFAULT_VLC_OPTIONS, onKeyListener);
this(application, DEFAULT_VLC_OPTIONS, onKeyListener, null);
}

public VLCVideoPackage(final Application application, final ArrayList<String> libVLCOptions) {
this(application, libVLCOptions, null);
public VLCVideoPackage(final Application application, final VLCVideoCallbackManager callbackManager) {
this(application, DEFAULT_VLC_OPTIONS, null, callbackManager);
}

public VLCVideoPackage(final Application application, final ArrayList<String> libVLCOptions, final View.OnKeyListener onKeyListener) {
this(application, libVLCOptions, onKeyListener, null);
}

public VLCVideoPackage(final Application application, final ArrayList<String> libVLCOptions, final VLCVideoCallbackManager callbackManager) {
this(application, libVLCOptions, null, callbackManager);
}

public VLCVideoPackage(final Application application, final View.OnKeyListener onKeyListener, final VLCVideoCallbackManager callbackManager) {
this(application, DEFAULT_VLC_OPTIONS, onKeyListener, callbackManager);
}

public VLCVideoPackage(final Application application, final ArrayList<String> libVLCOptions, final View.OnKeyListener onKeyListener, final VLCVideoCallbackManager callbackManager) {
mLibVLC = new LibVLC(application, libVLCOptions);
mOnKeyListener = onKeyListener;
mCallbackManager = callbackManager;
}

@Override
Expand All @@ -46,7 +64,7 @@ public List<NativeModule> createNativeModules(final ReactApplicationContext reac

@Override
public List<ViewManager> createViewManagers(final ReactApplicationContext reactApplicationContext) {
return Arrays.<ViewManager>asList(new VLCVideoViewManager(mOnKeyListener, mLibVLC));
return Arrays.<ViewManager>asList(new VLCVideoViewManager(mOnKeyListener, mLibVLC, mCallbackManager));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ final class VLCVideoProps {
static final boolean MEDIA_AUTOPLAY_DEFAULT_VALUE = true;
static final String MEDIA_HW_DECODER_ENABLED_PROP = "hwDecoderEnabled";
static final boolean MEDIA_HW_DECODER_ENABLED_DEFAULT_VALUE = true;
static final String MEDIA_TITLE_PROP = "title";
static final String MEDIA_TITLE_DEFAULT_VALUE = "";
static final String KEY_CONTROL_ENABLED_PROP = "keyControlEnabled";
static final boolean KEY_CONTROL_ENABLED_DEFAULT_VALUE = false;
static final String PLAY_IN_BACKGROUND_PROP = "playInBackground";
static final boolean PLAY_IN_BACKGROUND_DEFAULT_VALUE = false;

static final String PLAY_COMMAND_NAME = "play";
static final int PLAY_COMMAND_ID = 1;
Expand Down
133 changes: 123 additions & 10 deletions android/src/main/java/com/stellarscript/vlcvideo/VLCVideoView.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package com.stellarscript.vlcvideo;

import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.view.SurfaceView;

import com.facebook.react.bridge.LifecycleEventListener;
Expand All @@ -16,12 +22,44 @@
public final class VLCVideoView extends SurfaceView {

private static final String MEDIA_ERROR_MESSAGE = "VLC encountered an error with this media.";

private static final String CHANNEL_ID_RESOURCE_NAME = "react_native_vlc2_channel_id";
private static final String SMALL_ICON_RESOURCE_NAME = "react_native_vlc2_small_icon";
private static final String LARGE_ICON_RESOURCE_NAME = "react_native_vlc2_large_icon";
private static final String PLAY_ICON_RESOURCE_NAME = "react_native_vlc2_play_icon";
private static final String PAUSE_ICON_RESOURCE_NAME = "react_native_vlc2_pause_icon";
private static final String PLAY_INTENT_ACTION = "VLCVideo:Play";
private static final String PAUSE_INTENT_ACTION = "VLCVideo:Pause";

public static final int PLAYBACK_NOTIFICATION_ID = 11740;

private String mTitle;
private boolean mPlayInBackground;
private boolean mIsSeekRequested;
private final ThemedReactContext mThemedReactContext;
private final LibVLC mLibVLC;
private final VLCVideoCallbackManager mCallbackManager;
private final VLCVideoEventEmitter mEventEmitter;
private final MediaPlayer mMediaPlayer;
private final VLCVideoCallbackManager.IntentCallback mIntentCallback = new VLCVideoCallbackManager.IntentCallback() {

@Override
public boolean onNewIntent(final Intent intent) {
final String action = intent != null && intent.getAction() != null ? intent.getAction() : "";
switch (action) {
case PLAY_INTENT_ACTION:
VLCVideoView.this.attachVLCVoutViews();
mMediaPlayer.play();
return true;
case PAUSE_INTENT_ACTION:
VLCVideoView.this.attachVLCVoutViews();
mMediaPlayer.pause();
return true;
default:
return false;
}
}

};
private final LifecycleEventListener mLifecycleEventListener = new LifecycleEventListener() {

@Override
Expand All @@ -32,11 +70,10 @@ public void onHostResume() {
@Override
public void onHostPause() {
try {
if (!mMediaPlayer.isReleased()) {
if (!mMediaPlayer.isReleased() && !mPlayInBackground) {
mMediaPlayer.pause();
VLCVideoView.this.detachVLCVoutViews();
}
} catch (final Exception e) {
} catch (final Throwable e) {
e.printStackTrace();
}
}
Expand All @@ -55,13 +92,16 @@ public void onEvent(final MediaPlayer.Event mediaEvent) {
case MediaPlayer.Event.EndReached:
mEventEmitter.emitOnEndReached();
VLCVideoView.this.stop();
VLCVideoView.this.clearPlaybackNotification();
break;
case MediaPlayer.Event.EncounteredError:
mEventEmitter.emitOnError(MEDIA_ERROR_MESSAGE, true);
VLCVideoView.this.stop();
VLCVideoView.this.clearPlaybackNotification();
break;
case MediaPlayer.Event.Paused:
mEventEmitter.emitOnPaused();
VLCVideoView.this.updatePlaybackNotification();
break;
case MediaPlayer.Event.TimeChanged:
final double time = mMediaPlayer.getTime();
Expand All @@ -74,6 +114,7 @@ public void onEvent(final MediaPlayer.Event mediaEvent) {
case MediaPlayer.Event.Playing:
final double duration = mMediaPlayer.getLength();
mEventEmitter.emitOnPlaying(duration);
VLCVideoView.this.updatePlaybackNotification();
break;
case MediaPlayer.Event.Buffering:
final double buffering = mediaEvent.getBuffering();
Expand All @@ -84,11 +125,12 @@ public void onEvent(final MediaPlayer.Event mediaEvent) {

};

public VLCVideoView(final ThemedReactContext themedReactContext, final LibVLC libVLC) {
public VLCVideoView(final ThemedReactContext themedReactContext, final LibVLC libVLC, final VLCVideoCallbackManager callbackManager) {
super(themedReactContext);

mThemedReactContext = themedReactContext;
mLibVLC = libVLC;
mCallbackManager = callbackManager;
mEventEmitter = new VLCVideoEventEmitter(VLCVideoView.this, mThemedReactContext);
mMediaPlayer = new MediaPlayer(mLibVLC);

Expand All @@ -98,14 +140,24 @@ public VLCVideoView(final ThemedReactContext themedReactContext, final LibVLC li
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
VLCVideoView.this.attachVLCVoutViews();
if (mCallbackManager != null) {
mCallbackManager.addCallback(mIntentCallback);
}

mThemedReactContext.addLifecycleEventListener(mLifecycleEventListener);
mMediaPlayer.setEventListener(mMediaPlayerEventListener);
}

@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
detachVLCVoutViews();
VLCVideoView.this.clearPlaybackNotification();
VLCVideoView.this.detachVLCVoutViews();
if (mCallbackManager != null) {
mCallbackManager.removeCallback(mIntentCallback);
}

mThemedReactContext.removeLifecycleEventListener(mLifecycleEventListener);
mMediaPlayer.setEventListener(null);
try {
Expand All @@ -131,8 +183,12 @@ protected void onLayout(final boolean changed, final int left, final int top, fi
}
}

public void loadMedia(final String sourceUrl, final long startTime, final boolean autoplay, final boolean hwDecoderEnabled) {
if (sourceUrl.isEmpty()) {
public void setPlayInBackground(final boolean playInBackground) {
mPlayInBackground = playInBackground;
}

public void loadMedia(final String sourceUrl, final long startTime, final boolean autoplay, final boolean hwDecoderEnabled, final String title) {
if (sourceUrl == null || sourceUrl.isEmpty()) {
return;
}

Expand All @@ -145,6 +201,7 @@ public void loadMedia(final String sourceUrl, final long startTime, final boolea
}
}

VLCVideoView.this.stop();
final Media newMedia = new Media(mLibVLC, newSourceUri);
newMedia.setHWDecoderEnabled(hwDecoderEnabled, false);

Expand All @@ -154,12 +211,13 @@ public void loadMedia(final String sourceUrl, final long startTime, final boolea
newMedia.addOption(startTimeOption);
}

stop();
mTitle = title;
mMediaPlayer.setMedia(newMedia);

if (autoplay) {
mMediaPlayer.play();
}

VLCVideoView.this.updatePlaybackNotification();
}

public void play() {
Expand Down Expand Up @@ -209,4 +267,59 @@ private void detachVLCVoutViews() {
}
}

private void updatePlaybackNotification() {
try {
final String channelId = getResources().getString(getResources().getIdentifier(CHANNEL_ID_RESOURCE_NAME, "string", mThemedReactContext.getPackageName()));
final int smallIconResId = getResources().getIdentifier(SMALL_ICON_RESOURCE_NAME, "drawable", mThemedReactContext.getPackageName());
final int largeIconResId = getResources().getIdentifier(LARGE_ICON_RESOURCE_NAME, "drawable", mThemedReactContext.getPackageName());
final int playIconResId = getResources().getIdentifier(PLAY_ICON_RESOURCE_NAME, "drawable", mThemedReactContext.getPackageName());
final int pauseIconResId = getResources().getIdentifier(PAUSE_ICON_RESOURCE_NAME, "drawable", mThemedReactContext.getPackageName());
final Bitmap lergeIconBitmap = BitmapFactory.decodeResource(getResources(), largeIconResId);
final Intent playbackIntent = new Intent(mThemedReactContext, mThemedReactContext.getCurrentActivity().getClass());
final NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(mThemedReactContext, channelId)
.setContentTitle(mTitle != null ? mTitle : "")
.setSmallIcon(smallIconResId)
.setLargeIcon(lergeIconBitmap)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(false);
if (mMediaPlayer.isPlaying()) {
playbackIntent.setAction(PAUSE_INTENT_ACTION);
notificationBuilder.addAction(
new NotificationCompat.Action.Builder(
pauseIconResId,
"Pause",
PendingIntent.getActivity(
mThemedReactContext,
PLAYBACK_NOTIFICATION_ID,
playbackIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
).build()
);
} else {
playbackIntent.setAction(PLAY_INTENT_ACTION);
notificationBuilder.addAction(
new NotificationCompat.Action.Builder(
playIconResId,
"Play",
PendingIntent.getActivity(
mThemedReactContext,
PLAYBACK_NOTIFICATION_ID,
playbackIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
).build()
);
}

NotificationManagerCompat.from(mThemedReactContext).notify(PLAYBACK_NOTIFICATION_ID, notificationBuilder.build());
} catch (final Throwable e) {
e.printStackTrace();
}
}

private void clearPlaybackNotification() {
NotificationManagerCompat.from(mThemedReactContext).cancel(PLAYBACK_NOTIFICATION_ID);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ final class VLCVideoViewManager extends SimpleViewManager<VLCVideoView> {

private final View.OnKeyListener mOnKeyListener;
private final LibVLC mLibVLC;
private final VLCVideoCallbackManager mCallbackManager;

public VLCVideoViewManager(final View.OnKeyListener onKeyListener, final LibVLC libVLC) {
public VLCVideoViewManager(final View.OnKeyListener onKeyListener, final LibVLC libVLC, final VLCVideoCallbackManager callbackManager) {
mOnKeyListener = onKeyListener;
mLibVLC = libVLC;
mCallbackManager = callbackManager;
}

@Override
Expand Down Expand Up @@ -77,7 +79,7 @@ public Map<String, Object> getExportedViewConstants() {

@Override
protected VLCVideoView createViewInstance(final ThemedReactContext themedReactContext) {
return new VLCVideoView(themedReactContext, mLibVLC);
return new VLCVideoView(themedReactContext, mLibVLC, mCallbackManager);
}

@Override
Expand Down Expand Up @@ -139,7 +141,16 @@ public void loadMedia(final VLCVideoView videoView, final ReadableMap media) {
hwDecoderEnabled = VLCVideoProps.MEDIA_HW_DECODER_ENABLED_DEFAULT_VALUE;
}

videoView.loadMedia(sourceUrl, startTime, autoplay, hwDecoderEnabled);
final String title;
if (media.hasKey(VLCVideoProps.MEDIA_TITLE_PROP) &&
!media.isNull(VLCVideoProps.MEDIA_TITLE_PROP) &&
media.getType(VLCVideoProps.MEDIA_TITLE_PROP) == ReadableType.String) {
title = media.getString(VLCVideoProps.MEDIA_TITLE_PROP);
} else {
title = VLCVideoProps.MEDIA_TITLE_DEFAULT_VALUE;
}

videoView.loadMedia(sourceUrl, startTime, autoplay, hwDecoderEnabled, title);
}

@ReactProp(name = VLCVideoProps.KEY_CONTROL_ENABLED_PROP, defaultBoolean = VLCVideoProps.KEY_CONTROL_ENABLED_DEFAULT_VALUE)
Expand All @@ -155,4 +166,9 @@ public void setKeyControlEnabled(final VLCVideoView videoView, final boolean key
}
}

@ReactProp(name = VLCVideoProps.PLAY_IN_BACKGROUND_PROP, defaultBoolean = VLCVideoProps.PLAY_IN_BACKGROUND_DEFAULT_VALUE)
public void setPlayInBackground(final VLCVideoView videoView, final boolean playInBackground) {
videoView.setPlayInBackground(playInBackground);
}

}
Loading

0 comments on commit 5176693

Please sign in to comment.