I'm working on the Android TV app which uses VideoView to play the video from the url. Each time the video is paused and then resumed - it takes some time to pre-download it first (few seconds during which progress bar appears). The goal is to buffer the video during playback so that when I pause it and then resume - it resumes from pre-buffered state immediately without delay. I've also tried videoView.resume() method, but it doesn't help either. Is there a way to do that or am I doing something wrong?
Here is the code which plays/resumes the video:
public void onFragmentPlayPause(final Video video,
final VideoQuality quality,
final int position,
final Boolean play) {
stopSeeking();
videoView.setVideoPath(video.videoUrl(quality));
if (position == 0 || playbackState == LeanbackPlaybackState.IDLE) {
setupCallbacks();
playbackState = LeanbackPlaybackState.IDLE;
}
if (play && playbackState != LeanbackPlaybackState.PLAYING) {
progressBar.setVisibility(View.VISIBLE);
playbackState = LeanbackPlaybackState.PLAYING;
if (position > 0) {
videoView.seekTo(position);
videoView.start();
}
playbackFragment.startProgressObservation(progressFlowable());
} else {
playbackState = LeanbackPlaybackState.PAUSED;
videoView.pause();
playbackFragment.stopProgressObservation();
}
updatePlaybackState(position);
updateMetadata(video);
}
Unfortunately, I've didn't find the way to achieve this with VideoView, only using ExoPlayer, thanks to #pskink for the right direction. The API was returning mp4 videos, so I've used ExtractorMediaSource. The complete demo example with support of multiple formats can be found on ExoPlayer GitHub page.
Here is the final code I've ended up with using ExoPlayer:
private void playPause(final Video video, final int position, final Boolean play) {
if (position == 0 || playbackState == LeanbackPlaybackState.IDLE) {
setupCallbacks();
playbackState = LeanbackPlaybackState.IDLE;
}
if (play && playbackState != LeanbackPlaybackState.PLAYING) {
progressBar.setVisibility(View.VISIBLE);
playbackState = LeanbackPlaybackState.PLAYING;
player.start();
playbackFragment.startProgressObservation(progressFlowable());
} else {
playbackState = LeanbackPlaybackState.PAUSED;
player.pause();
playbackFragment.stopProgressObservation();
}
updatePlaybackState(position);
updateMetadata(video);
}
And Player implementation:
public class Player implements MediaController.MediaPlayerControl, ExoPlayer.EventListener {
private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
private final SimpleExoPlayer exoPlayer;
private final Context context;
private OnErrorListener onErrorListener;
private OnPreparedListener onPreparedListener;
private OnCompletionListener onCompletionListener;
private String url;
public interface OnErrorListener {
void onError(final Exception e);
}
public interface OnPreparedListener {
void onPrepared();
}
public interface OnCompletionListener {
void onComplete();
}
public Player(final Context context) {
this.context = context;
final #SimpleExoPlayer.ExtensionRendererMode int extensionRendererMode =
SimpleExoPlayer.EXTENSION_RENDERER_MODE_OFF;
final TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
this.exoPlayer = ExoPlayerFactory.newSimpleInstance(
context,
new DefaultTrackSelector(videoTrackSelectionFactory),
new DefaultLoadControl(),
null,
extensionRendererMode
);
this.exoPlayer.addListener(this);
}
#Override
public boolean canPause() {
return true;
}
#Override
public boolean canSeekBackward() {
return true;
}
#Override
public boolean canSeekForward() {
return true;
}
#Override
public int getAudioSessionId() {
throw new UnsupportedOperationException();
}
#Override
public int getBufferPercentage() {
return exoPlayer.getBufferedPercentage();
}
#Override
public int getCurrentPosition() {
return exoPlayer.getDuration() == com.google.android.exoplayer2.C.TIME_UNSET ? 0
: (int) exoPlayer.getCurrentPosition();
}
#Override
public int getDuration() {
return exoPlayer.getDuration() == com.google.android.exoplayer2.C.TIME_UNSET ? 0
: (int) exoPlayer.getDuration();
}
#Override
public boolean isPlaying() {
return exoPlayer.getPlayWhenReady();
}
#Override
public void start() {
exoPlayer.setPlayWhenReady(true);
}
#Override
public void pause() {
exoPlayer.setPlayWhenReady(false);
}
public void stop() {
exoPlayer.seekTo(0);
pause();
}
public void setOnErrorListener(final OnErrorListener onErrorListener) {
this.onErrorListener = onErrorListener;
}
public void setOnPreparedListener(final OnPreparedListener onPreparedListener) {
this.onPreparedListener = onPreparedListener;
}
public void setOnCompletionListener(final OnCompletionListener onCompletionListener) {
this.onCompletionListener = onCompletionListener;
}
public void setVolume(final float volume) {
exoPlayer.setVolume(volume);
}
public void release() {
exoPlayer.release();
}
public void updateUrl(final String url) {
this.url = url;
exoPlayer.prepare(buildMediaSource(Uri.parse(url)));
}
public SimpleExoPlayer exoPlayer() {
return exoPlayer;
}
#Override
public void seekTo(final int timeMillis) {
exoPlayer.seekTo(timeMillis);
}
#Override
public void onTimelineChanged(final Timeline timeline, final Object manifest) {
}
#Override
public void onTracksChanged(final TrackGroupArray trackGroups, final TrackSelectionArray trackSelections) {
}
#Override
public void onLoadingChanged(final boolean isLoading) {
}
#Override
public void onPlayerStateChanged(final boolean playWhenReady, final int playbackState) {
if (playbackState == ExoPlayer.STATE_READY) {
onPreparedListener.onPrepared();
}
if (playbackState == ExoPlayer.STATE_ENDED) {
onCompletionListener.onComplete();
}
}
#Override
public void onPlayerError(final ExoPlaybackException error) {
onErrorListener.onError(error);
}
#Override
public void onPositionDiscontinuity() {
}
public String url() {
return url;
}
private MediaSource buildMediaSource(final Uri uri) {
return new ExtractorMediaSource(uri, buildDataSourceFactory(true), new DefaultExtractorsFactory(),
null, null);
}
private DataSource.Factory buildDataSourceFactory(final DefaultBandwidthMeter bandwidthMeter) {
return new DefaultDataSourceFactory(context, bandwidthMeter,
buildHttpDataSourceFactory(bandwidthMeter));
}
private HttpDataSource.Factory buildHttpDataSourceFactory(final DefaultBandwidthMeter bandwidthMeter) {
return new DefaultHttpDataSourceFactory(Util.getUserAgent(context, Application.TAG), bandwidthMeter);
}
private DataSource.Factory buildDataSourceFactory(final boolean useBandwidthMeter) {
return buildDataSourceFactory(useBandwidthMeter ? BANDWIDTH_METER : null);
}
}
Related
I am Beginner android. Can anyone help me in brief about this problem. I am getting two links dynamically
url
alternate
I want to play second media(alternate) source if I get an error on the first one (url).
I am searching this for a week, a good answer will be appreciated. if you unable to solve please upvote it to get into more eyes.. Thanks in Advance.
Here is my full code
ImageButton playPause,lockScreen,unLock;
SeekBar seekBar;
TextView start,end;
boolean isPlaying = false;
boolean isLock = false;
RelativeLayout controller;
FrameLayout root;
boolean isControllerShowing = true;
private Handler mHandler;
SimpleExoPlayerView simpleExoPlayerView;
private SimpleExoPlayer player;
SessionManagement management;
String url, alternate;
Uri mp4VideoUri;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_live_tv);
checkConnection();
Sensey.getInstance().init(this);
Sensey.getInstance().startOrientationDetection(15,this);
management = new SessionManagement(this);
HashMap<String,String> hashMap = management.getUserDetails();
url = hashMap.get(SessionManagement.LIVE_URL);
alternate = hashMap.get(SessionManagement.ALTERNATE_URL);
playPause = findViewById(R.id.btnPlayPause);
lockScreen = findViewById(R.id.btnLock);
unLock = findViewById(R.id.btnOpenLock);
seekBar = findViewById(R.id.seeker);
end = findViewById(R.id.duration);
start = findViewById(R.id.position);
controller = findViewById(R.id.layout_controller);
root = findViewById(R.id.activity_live);
try {
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
LoadControl loadControl = new DefaultLoadControl();
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);
simpleExoPlayerView = findViewById(R.id.video_view);
simpleExoPlayerView.setUseController(false);
simpleExoPlayerView.requestFocus();
simpleExoPlayerView.setPlayer(player);
mp4VideoUri = Uri.parse(url); //Radnom 540p indian channel
mHandler = new Handler();
DefaultBandwidthMeter bandwidthMeterA = new DefaultBandwidthMeter();
DefaultDataSourceFactory dataSourceFactory = new
DefaultDataSourceFactory(this, Util.getUserAgent(this, "Madha TV"), bandwidthMeterA);
DefaultExtractorsFactory();
dataSourceFactory, extractorsFactory, null, null);
MediaSource videoSource = new HlsMediaSource(mp4VideoUri, dataSourceFactory, 1, null, null);
final LoopingMediaSource loopingSource = new LoopingMediaSource(videoSource);
player.prepare(loopingSource);
player.addListener(new ExoPlayer.EventListener() {
#Override
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
}
#Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
}
#Override
public void onLoadingChanged(boolean isLoading) {
}
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
}
#Override
public void onRepeatModeChanged(int repeatMode) {
}
#Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
}
#Override
public void onPlayerError(ExoPlaybackException error) {
switch (error.type) {
case ExoPlaybackException.TYPE_SOURCE:
Log.e("Error", "TYPE_SOURCE: " + error.getSourceException().getMessage());
Intent intent1 = new Intent(LiveTVActivity.this, MainActivity.class);
startActivity(intent1);
finish();
Toast.makeText(LiveTVActivity.this, error.getSourceException().getMessage(), Toast.LENGTH_SHORT).show();
break;
case ExoPlaybackException.TYPE_RENDERER:
break;
case ExoPlaybackException.TYPE_UNEXPECTED:
Log.e("Error", "TYPE_UNEXPECTED: " + error.getUnexpectedException().getMessage());
break;
}
player.stop();
player.prepare(loopingSource);
player.setPlayWhenReady(true);
}
#Override
public void onPositionDiscontinuity(int reason) {
}
#Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
}
#Override
public void onSeekProcessed() {
}
});
player.setPlayWhenReady(true); //run file/link when ready to play.
player.setVideoDebugListener(this); //for listening to resolution change and outputing the resolution
if (player.getPlayWhenReady())
{
playPause.setImageResource(R.drawable.cvp_action_pause);
isPlaying = true;
}
else
{
playPause.setImageResource(R.drawable.cvp_action_play);
isPlaying = false;
}
root.setOnClickListener(this);
playPause.setOnClickListener(this);
lockScreen.setOnClickListener(this);
unLock.setOnClickListener(this);
}
catch (Exception e)
{
Log.e("Error",e.toString());
}
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
}
updateMetadata();
} else {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
updateMetadata();
}
}
#Override
public void onTopSideUp() {
updateMetadata();
setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
#Override
public void onBottomSideUp() {
}
#Override
public void onRightSideUp() {
updateMetadata();
setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
#Override
public void onLeftSideUp() {
updateMetadata();
setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
}
#SuppressWarnings("ConstantConditions")
private void updateMetadata() {
Point displaySize;
displaySize = Utils.getDisplaySize(this);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(displaySize.x,displaySize.y);
lp.gravity = Gravity.CENTER;
simpleExoPlayerView.setLayoutParams(lp);
simpleExoPlayerView.invalidate();
}
private void checkConnection()
{
boolean isConnected = ConnectivityReceiver.isConnected();
showSnack(isConnected);
}
// Showing the status in Snackbar
private void showSnack(boolean isConnected) {
if (!isConnected) {
Config.noConnection(LiveTVActivity.this);
}
}
#Override
public void onClick(View view) {
if (view.getId() == R.id.btnPlayPause) {
setPlayPause();
}else if (view.getId() == R.id.btnLock) {
if (!isLock)
{
unLock.setVisibility(View.VISIBLE);
isLock = true;
controller.setVisibility(View.GONE);
root.setClickable(false);
}
}else if (view.getId() == R.id.btnOpenLock)
{
if (isLock)
{
root.setClickable(true);
isLock = false;
unLock.setVisibility(View.GONE);
controller.setVisibility(View.VISIBLE);
}
} else if (view.getId() == R.id.activity_live)
{
showController();
}
}
public void showController()
{
if (!isControllerShowing)
{
isControllerShowing = true;
controller.setVisibility(View.VISIBLE);
}
else
{
isControllerShowing = false;
controller.setVisibility(View.GONE);
}
}
private void setPlayPause() {
if (isPlaying)
{
player.setPlayWhenReady(false);
isPlaying = false;
playPause.setImageResource(R.drawable.cvp_action_play);
}
else
{
player.setPlayWhenReady(true);
isPlaying = true;
playPause.setImageResource(R.drawable.cvp_action_pause);
}
}
#Override
public void onNetworkConnectionChanged(boolean isConnected) {
showSnack(isConnected);
}
#Override
public void onVideoEnabled(DecoderCounters counters) {
}
#Override
public void onVideoDecoderInitialized(String decoderName, long initializedTimestampMs, long initializationDurationMs) {
}
#Override
public void onVideoInputFormatChanged(Format format) {
}
#Override
public void onDroppedFrames(int count, long elapsedMs) {
}
#Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
}
#Override
public void onRenderedFirstFrame(Surface surface) {
}
#Override
public void onVideoDisabled(DecoderCounters counters) {
}
Try this code..
public class VideoPlayerActivity extends AppCompatActivity implements ExoPlayer.EventListener {
private SimpleExoPlayer mSimpleExoPlayer;
private SimpleExoPlayerView mSimpleExoPlayerView;
private Handler mMainHandler;
private AdaptiveTrackSelection.Factory mAdaptiveTrackSelectionFactory;
private TrackSelector mTrackSelector;
private LoadControl mLoadControl;
private DefaultBandwidthMeter mBandwidthMeter;
private DataSource.Factory mDataSourceFactory;
private SimpleCache mSimpleCache;
private DataSource.Factory mFactory;
private MediaSource mVideoSource;
private LoopingMediaSource mLoopingMediaSource;
private ProgressBar mProgressBar;
private String videoUrl="http://sample.vodobox.net/skate_phantom_flex_4k/skate_phantom_flex_4k.m3u8";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video);
mSimpleExoPlayerView = (SimpleExoPlayerView) findViewById(R.id.videoPlayer);
mProgressBar = (ProgressBar) findViewById(R.id.amPrgbrLoading);
}
/**
* this method play audio and video with hls streaming.
*/
private void playMedia() {
mMainHandler = new Handler();
mBandwidthMeter = new DefaultBandwidthMeter();
mAdaptiveTrackSelectionFactory = new AdaptiveTrackSelection.Factory(mBandwidthMeter);
mTrackSelector = new DefaultTrackSelector(mAdaptiveTrackSelectionFactory);
mLoadControl = new DefaultLoadControl();
mSimpleExoPlayer = ExoPlayerFactory.newSimpleInstance(this, mTrackSelector, mLoadControl);
mSimpleExoPlayerView.setPlayer(mSimpleExoPlayer);
mSimpleExoPlayerView.setControllerVisibilityListener(new PlaybackControlView.VisibilityListener() {
#Override
public void onVisibilityChange(int visibility) {
mSimpleExoPlayerView.showController();
}
});
mDataSourceFactory = new DefaultDataSourceFactory(this,Util.getUserAgent(this, "com.exoplayerdemo"), mBandwidthMeter);
mSimpleCache = new SimpleCache(this.getCacheDir(), new LeastRecentlyUsedCacheEvictor(1024 * 1024 * 10));
mFactory = new CacheDataSourceFactory(mSimpleCache, mDataSourceFactory,0);
mVideoSource = new HlsMediaSource(Uri.parse(videoUrl),
mFactory, mMainHandler, new AdaptiveMediaSourceEventListener() {
#Override
public void onLoadStarted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs) {
//Toast.makeText(VideoPlayerActivity.this, "Load Started", Toast.LENGTH_SHORT).show();
}
#Override
public void onLoadCompleted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) {
//Toast.makeText(VideoPlayerActivity.this, "Load Completed", Toast.LENGTH_SHORT).show();
}
#Override
public void onLoadCanceled(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) {
}
#Override
public void onLoadError(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded, IOException error, boolean wasCanceled) {
videoUrl=""; // define second url
}
#Override
public void onUpstreamDiscarded(int trackType, long mediaStartTimeMs, long mediaEndTimeMs) {
// Toast.makeText(VideoPlayerActivity.this, "Up stream", Toast.LENGTH_SHORT).show();
}
#Override
public void onDownstreamFormatChanged(int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaTimeMs) {
// Toast.makeText(VideoPlayerActivity.this, "Down Stream", Toast.LENGTH_SHORT).show();
}
});
mLoopingMediaSource = new LoopingMediaSource(mVideoSource);
mSimpleExoPlayer.prepare(mLoopingMediaSource);
mSimpleExoPlayer.setPlayWhenReady(true);
mSimpleExoPlayer.addListener(new ExoPlayer.EventListener() {
#Override
public void onTimelineChanged(Timeline timeline, Object manifest) {
}
#Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
}
#Override
public void onLoadingChanged(boolean isLoading) {
}
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == ExoPlayer.STATE_BUFFERING) {
mProgressBar.setVisibility(View.VISIBLE);
} else {
mProgressBar.setVisibility(View.GONE);
}
}
#Override
public void onPlayerError(ExoPlaybackException error) {
}
#Override
public void onPositionDiscontinuity() {
}
});
}
#Override
protected void onResume() {
super.onResume();
playMedia();
}
#Override
protected void onStop() {
super.onStop();
stopMedia();
}
#Override
protected void onPause() {
super.onPause();
stopMedia();
}
private void stopMedia() {
mSimpleExoPlayer.stop();
mSimpleExoPlayer.release();
}
#Override
public void onTimelineChanged(Timeline timeline, Object manifest) {
}
#Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
}
#Override
public void onLoadingChanged(boolean isLoading) {
}
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
}
#Override
public void onPlayerError(ExoPlaybackException error) {
}
#Override
public void onPositionDiscontinuity() {
}
}
I am also getting the same issue. you have to do some change in your code
1. implement exo player listen to the activity so the common lister will use every instant of your player
Then you have to add all the player initialize code in a method and call this on error state of exo player
initializePlayer();
private void initializePlayer(String video_url) {
try {
videoUri = Uri.parse(VIDEOS_URL + contentMasterModel.getImage_or_video());
simpleExoPlayerView.requestFocus();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector);
simpleExoPlayerView.setPlayer(player);
//player.setPlayWhenReady(shouldAutoPlay);
player.addListener(this);
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
MediaSource mediaSource = new ExtractorMediaSource(videoUri,
mediaDataSourceFactory, extractorsFactory, null, null);
player.prepare(mediaSource);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
// Call some material design APIs here
// player.setPlayWhenReady(true);
}
inErrorState = false;
} catch (Exception e) {
e.printStackTrace();
Log.e("Please try again", "download again");
}
}
OnError
private void errorinitializePlayer() {
videoUri = Uri.parse(VIDEOS_URL);
simpleExoPlayerView.requestFocus();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector);
simpleExoPlayerView.setPlayer(player);
//player.setPlayWhenReady(shouldAutoPlay);
player.addListener(this);
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
MediaSource mediaSource = new ExtractorMediaSource(videoUri,
mediaDataSourceFactory, extractorsFactory, null, null);
player.prepare(mediaSource);
inErrorState = false;}
And implements EventListener, PlaybackControlView.VisibilityListener in activity
I need some help, I want to make a recycle view containing audio in each item, how to play the audio when press play button and set progress to seekbar and at the same time pause other sound and convert the play button to play mode in all row except the playing one be in pause mode
such as or chats in facebook messenger
This is my code:
MyAdapter.class
private List<String> positions = new ArrayList<>();
public myAdapter(Activity activity, ArrayList<Tweet> list) {
this.activity = activity;
this.list = list;
mPlayer = new MediaPlayer();
}
public void onBindViewHolder(final viewHolder holder, final int position) {
if (!positions.contains(String.valueOf(position)))
positions.add(String.valueOf(position));
}
}
viewHolder.class
public class viewHolder extends RecyclerView.ViewHolder implements
SeekBar.OnSeekBarChangeListener, View.OnClickListener {
LinearLayout parentPanel;
int viewType;
private RelativeLayout pauseLayout, playLayout;
private ImageView pauseIcon, playIcon;
private SeekBar seekBar;
private TextView periodTime;
AudioCallbacks mAudioCallbacks;
private int last_position;
viewHolder(final View itemView, int viewType) {
super(itemView);
this.viewType = viewType;
playLayout = (RelativeLayout) itemView.findViewById(R.id.play_layout);
pauseLayout = (RelativeLayout) itemView.findViewById(R.id.pause_layout);
playIcon = (ImageView) itemView.findViewById(R.id.play_icon);
pauseIcon = (ImageView) itemView.findViewById(R.id.pause_icon);
seekBar = (SeekBar) itemView.findViewById(R.id.seekBar);
periodTime = (TextView) itemView.findViewById(R.id.period_time);
seekBar.setOnSeekBarChangeListener(this);
playLayout.setOnClickListener(this);
pauseLayout.setOnClickListener(this);
mAudioCallbacks = new AudioCallbacks() {
#Override
public void onUpdate(int percentage) {
seekBar.setProgress(percentage);
if (percentage == 100)
mAudioCallbacks.onStop();
}
#Override
public void onPause() {
Log.i("on pause audio", " pause");
}
#Override
public void onStop() {
Log.i("on stop audio", " stop");
stopPlayingAudio();
}
};
}
void stopPlayingAudio() {
if (mPlayer != null) {
if (mPlayer.isPlaying()) {
updateAudioProgressBar();
mPlayer.stop();
mPlayer.reset();
seekBar.setProgress(0);
playLayout.setVisibility(View.VISIBLE);
pauseLayout.setVisibility(View.GONE);
}
}
}
void pausePlayingAudio() {
if (mPlayer != null) {
if (mPlayer.isPlaying()) {
mPlayer.pause();
updateAudioProgressBar();
mAudioCallbacks.onPause();
}
}
}
void playingAudio(Tweet message) {
updateAudioProgressBar();
if (mPlayer != null) {
try {
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setDataSource(message.getAudioUrl());
mPlayer.prepare();
mPlayer.start();
periodTime.setVisibility(View.VISIBLE);
periodTime.setText(String.valueOf(message.getAudioDuration()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
void updateAudioProgressBar() {
durationHandler.postDelayed(mUpdateTimeTask, 100);
}
private Runnable mUpdateTimeTask = new Runnable() {
public void run() {
try {
if (mPlayer.isPlaying()) {
long totalDuration = mPlayer.getDuration();
long currentDuration = mPlayer.getCurrentPosition();
int progress = Utils.getProgressPercentage(currentDuration, totalDuration);
mAudioCallbacks.onUpdate(progress);
periodTime.setText(Utils.getFileTime(currentDuration));
durationHandler.postDelayed(this, 100);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
durationHandler.removeCallbacks(mUpdateTimeTask);
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
int totalDuration = mPlayer.getDuration();
int currentPosition = Utils.progressToTimer(seekBar.getProgress(), totalDuration);
mPlayer.seekTo(currentPosition);
updateAudioProgressBar();
}
#Override
public void onClick(View view) {
Log.e("getAdapterPosition()L", last_position + " /");
for (String pos : positions) {
if (!pos.equals(String.valueOf(getAdapterPosition())))
notifyItemChanged(Integer.parseInt(pos));
}
last_position = getAdapterPosition();
Log.e("getAdapterPosition()F", last_position + " /");
Tweet message = list.get(getAdapterPosition());
switch (view.getId()) {
case R.id.pause_layout:
playLayout.setVisibility(View.VISIBLE);
pauseLayout.setVisibility(View.GONE);
pausePlayingAudio();
break;
case R.id.play_layout:
playLayout.setVisibility(View.GONE);
pauseLayout.setVisibility(View.VISIBLE);
mAudioCallbacks.onStop();
playingAudio(message);
break;
}
}
}
public interface AudioCallbacks {
void onUpdate(int percentage);
void onPause();
void onStop();
}
the problem of this code is :
some time the image not reversed of other object when play specific item .
when scrolled the pause button appear in multiple row.
when play one then play another, stop the first and run the new one but when return to the previous not playing.
when leave the activity or press back press button the sound not stopped.
Can someone helped me, please?
Controlling and updating progress of MediaPlayer from RecyclerView cells is tricky. Your solution does not correctly update the view states in onBindViewHolder call, which is the root cause of all the issues you are facing. Few other notes one should keep in mind are as follows:
update cell view state correctly, which also include add/remove progress updater
try to avoid anonymous allocations of xxxListeners, Runnables in onBindViewHolder, i.e. ViewHolder can be used to implement xxxListener interfaces etc.
remove the seek bar updater, when media player completes the playback of audio
respect activity life-cycle and release/stop media player if it is not required to play the audio
Below source shows possible implementation of Adapter
private class AudioItemAdapter extends RecyclerView.Adapter<AudioItemAdapter.AudioItemsViewHolder> {
private MediaPlayer mediaPlayer;
private List<AudioItem> audioItems;
private int currentPlayingPosition;
private SeekBarUpdater seekBarUpdater;
private AudioItemsViewHolder playingHolder;
AudioItemAdapter(List<AudioItem> audioItems) {
this.audioItems = audioItems;
this.currentPlayingPosition = -1;
seekBarUpdater = new SeekBarUpdater();
}
#Override
public AudioItemsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new AudioItemsViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false));
}
#Override
public void onBindViewHolder(AudioItemsViewHolder holder, int position) {
if (position == currentPlayingPosition) {
playingHolder = holder;
updatePlayingView();
} else {
updateNonPlayingView(holder);
}
}
#Override
public void onViewRecycled(AudioItemsViewHolder holder) {
super.onViewRecycled(holder);
if (currentPlayingPosition == holder.getAdapterPosition()) {
updateNonPlayingView(playingHolder);
playingHolder = null;
}
}
private void updateNonPlayingView(AudioItemsViewHolder holder) {
holder.sbProgress.removeCallbacks(seekBarUpdater);
holder.sbProgress.setEnabled(false);
holder.sbProgress.setProgress(0);
holder.ivPlayPause.setImageResource(R.drawable.ic_play_arrow);
}
private void updatePlayingView() {
playingHolder.sbProgress.setMax(mediaPlayer.getDuration());
playingHolder.sbProgress.setProgress(mediaPlayer.getCurrentPosition());
playingHolder.sbProgress.setEnabled(true);
if (mediaPlayer.isPlaying()) {
playingHolder.sbProgress.postDelayed(seekBarUpdater, 100);
playingHolder.ivPlayPause.setImageResource(R.drawable.ic_pause);
} else {
playingHolder.sbProgress.removeCallbacks(seekBarUpdater);
playingHolder.ivPlayPause.setImageResource(R.drawable.ic_play_arrow);
}
}
void stopPlayer() {
if (null != mediaPlayer) {
releaseMediaPlayer();
}
}
private class SeekBarUpdater implements Runnable {
#Override
public void run() {
if (null != playingHolder) {
playingHolder.sbProgress.setProgress(mediaPlayer.getCurrentPosition());
playingHolder.sbProgress.postDelayed(this, 100);
}
}
}
#Override
public int getItemCount() {
return audioItems.size();
}
class AudioItemsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, SeekBar.OnSeekBarChangeListener {
SeekBar sbProgress;
ImageView ivPlayPause;
AudioItemsViewHolder(View itemView) {
super(itemView);
ivPlayPause = (ImageView) itemView.findViewById(R.id.ivPlayPause);
ivPlayPause.setOnClickListener(this);
sbProgress = (SeekBar) itemView.findViewById(R.id.sbProgress);
sbProgress.setOnSeekBarChangeListener(this);
}
#Override
public void onClick(View v) {
if (getAdapterPosition() == currentPlayingPosition) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
} else {
mediaPlayer.start();
}
} else {
currentPlayingPosition = getAdapterPosition();
if (mediaPlayer != null) {
if (null != playingHolder) {
updateNonPlayingView(playingHolder);
}
mediaPlayer.release();
}
playingHolder = this;
startMediaPlayer(audioItems.get(currentPlayingPosition).audioResId);
}
updatePlayingView();
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
mediaPlayer.seekTo(progress);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
}
private void startMediaPlayer(int audioResId) {
mediaPlayer = MediaPlayer.create(getApplicationContext(), audioResId);
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
releaseMediaPlayer();
}
});
mediaPlayer.start();
}
private void releaseMediaPlayer() {
if (null != playingHolder) {
updateNonPlayingView(playingHolder);
}
mediaPlayer.release();
mediaPlayer = null;
currentPlayingPosition = -1;
}
}
You can find complete working solution here - GitHub
public class MyAdapter2 extends RecyclerView.Adapter<MyAdapter2.AudioItemsViewHolder> {
static MediaPlayer mediaPlayer;
Activity activity;
private final ArrayList<GroupItems> audioItems;//change it() to your items
private int currentPlayingPosition;
private final SeekBarUpdater seekBarUpdater;
private AudioItemsViewHolder playingHolder;
public MyAdapter2(Activity activity, ArrayList<GroupItems> items_pro) {
this.audioItems = items_pro;
this.currentPlayingPosition = -1;
seekBarUpdater = new SeekBarUpdater();
this.activity = activity;
}
#Override
public AudioItemsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//put YourItemsLayout;
return new AudioItemsViewHolder(LayoutInflater.from(parent.getContext()).inflate(YourItemsLayout, parent, false));
}
#Override
public void onBindViewHolder(AudioItemsViewHolder holder, int position) {
if (position == currentPlayingPosition) {
playingHolder = holder;
updatePlayingView();
} else {
updateNonPlayingView(holder);
}
}
private void updateNonPlayingView(AudioItemsViewHolder holder) {
holder.sbProgress.removeCallbacks(seekBarUpdater);
holder.sbProgress.setEnabled(false);
holder.sbProgress.setProgress(0);
holder.ivPlayPause.setImageResource(R.drawable.ic_baseline_play_arrow_24);
}
private void updatePlayingView() {
playingHolder.sbProgress.setMax(mediaPlayer.getDuration());
playingHolder.sbProgress.setProgress(mediaPlayer.getCurrentPosition());
playingHolder.sbProgress.setEnabled(true);
if (mediaPlayer.isPlaying()) {
playingHolder.sbProgress.postDelayed(seekBarUpdater, 100);
playingHolder.ivPlayPause.setImageResource(R.drawable.ic_pause);
} else {
playingHolder.sbProgress.removeCallbacks(seekBarUpdater);
playingHolder.ivPlayPause.setImageResource(R.drawable.ic_baseline_play_arrow_24);
}
}
private class SeekBarUpdater implements Runnable {
#Override
public void run() {
if (null != playingHolder && null != mediaPlayer) {
playingHolder.sbProgress.setProgress(mediaPlayer.getCurrentPosition());
playingHolder.sbProgress.postDelayed(this, 100);
}
}
}
#Override
public int getItemCount() {
return audioItems.size();
}
class AudioItemsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, SeekBar.OnSeekBarChangeListener {
SeekBar sbProgress;
ImageView ivPlayPause;
AudioItemsViewHolder(View itemView) {
super(itemView);
ivPlayPause = itemView.findViewById(R.id.sound_btn);
ivPlayPause.setOnClickListener(this);
sbProgress = itemView.findViewById(R.id.seekBar);
sbProgress.setOnSeekBarChangeListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.seekBar:
break;
case R.id.sound_btn: {
if (getAdapterPosition() == currentPlayingPosition) {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
} else {
if (mediaPlayer != null)
mediaPlayer.start();
}
} else {
currentPlayingPosition = getAdapterPosition();
if (mediaPlayer != null) {
if (null != playingHolder) {
updateNonPlayingView(playingHolder);
}
mediaPlayer.release();
}
playingHolder = this;
PlaySound(YOUR AUDIO FILE);//put your audio file
}
if (mediaPlayer != null)
updatePlayingView();
}
break;
}
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
mediaPlayer.seekTo(progress);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
}
private void PlaySound(File filesound) {
mediaPlayer = MediaPlayer.create(activity, Uri.parse(String.valueOf(filesound)));
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
releaseMediaPlayer();
}
});
mediaPlayer.start();
}
private void releaseMediaPlayer() {
if (null != playingHolder) {
updateNonPlayingView(playingHolder);
}
if (outputFile.exists())
outputFile.delete();
mediaPlayer.release();
mediaPlayer = null;
currentPlayingPosition = -1;
}
}
i wanna create android component, and in this component i have video player, that using MediaPlayer with SurfaceTexture. When i try that compoent in sample app, it work fine. But when i tried to press Home Button (Circle Button in android Device) then open again the app from history off running app (press button task/menu then click the apps), the Media Player Not Called normally. But if i click app icon again media player work normally. anyone know what happen ?
I try to logging when i resume it from background app, and it say
04-09 17:42:13.160 330-22013/? E/MediaPlayerService: setVideoSurfaceTexture failed: -19
04-09 17:44:49.944 453-453/com.videofiltertest E/MediaPlayer: pause called in state 0
04-09 17:44:49.945 453-453/com.videofiltertest E/MediaPlayer: error (-38, 0)
thanks.
This my native component code.
public class VideoPlayer extends TextureView implements TextureView.SurfaceTextureListener, LifecycleEventListener, MediaPlayer.OnPreparedListener{
private static final String TAG = VideoThumbnail.class.getSimpleName();
public MediaPlayer mMediaPlayer;
public VideoTextureRenderer mRenderer;
private int surfaceWidth;
private int surfaceHeight;
private ThemedReactContext mContext;
private String videoPath;
private String fragmentShaderCode = null;
private boolean isCrop = false;
private boolean isAutoPlay = false;
private boolean isLooping = false;
private boolean mediaPlayerReady = false;
private boolean isTextureAvailable = false;
private SurfaceTexture surfaceTexture;
private SurfaceTexture surface = null;
private int xpos;
private int seekto = 0;
private boolean isPaused = false;
private boolean isCreated = false;
private boolean mediaPlayerPrepared = false;
public VideoPlayer(ThemedReactContext context) {
super(context);
mContext = context;
mContext.addLifecycleEventListener(this);
this.setSurfaceTextureListener(this);
}
public VideoPlayer(ThemedReactContext context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mContext.addLifecycleEventListener(this);
this.setSurfaceTextureListener(this);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
surfaceHeight = View.MeasureSpec.getSize(heightMeasureSpec);
surfaceWidth = View.MeasureSpec.getSize(widthMeasureSpec);
this.setMeasuredDimension(surfaceWidth, surfaceWidth);
}
private void getSurface(){
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (surface != null) {
try {
mMediaPlayer.setDataSource(videoPath);
mMediaPlayer.prepare();
isCreated = true;
} catch (IOException e) {
Log.e("DEBUG", e.toString());
throw new RuntimeException("Could not open input video!");
}
} else {
surface = mRenderer.getVideoTexture();
getSurface();
}
}
}, 1000);
}
private void setup(){
if(mMediaPlayer == null){
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnPreparedListener(this);
}
if(mRenderer == null){
mRenderer = new VideoTextureRenderer(mContext, surfaceTexture, surfaceWidth, surfaceWidth );
}
getSurface();
}
public void setSource(String src){
videoPath = src;
}
public void setFilter(int filterCode){
switch (filterCode){
...
}
if(mediaPlayerReady){
mMediaPlayer.start();
mRenderer.setShader(fragmentShaderCode);
}
}
public void setCrop(boolean isCrop){
this.isCrop = isCrop;
if(mediaPlayerReady){
if(!mMediaPlayer.isPlaying()){
mMediaPlayer.start();
}
mRenderer.setCrop(this.isCrop);
}
}
public void togglePlayPause(){
if(mediaPlayerReady){
if(!mMediaPlayer.isPlaying()){
mMediaPlayer.start();
}else{
mMediaPlayer.pause();
}
}
}
public boolean isPlaying(){
if(mediaPlayerReady){
return mMediaPlayer.isPlaying();
}
return false;
}
public void setPaused(){
if(mediaPlayerReady){
if(mMediaPlayer.isPlaying()){
mMediaPlayer.pause();
}
}
}
public void setStop(){
if(mediaPlayerReady){
mMediaPlayer.stop();
}
}
public void setStart(){
if(mediaPlayerReady){
if(!mMediaPlayer.isPlaying()){
mMediaPlayer.start();
}
}
}
public void seekTo(int time){
if(mediaPlayerReady){
mMediaPlayer.seekTo(time);
}else{
seekto = time;
}
}
public boolean isPlayerReady(){
return mediaPlayerReady;
}
public int getCurrentPos(){
if(mediaPlayerReady){
return mMediaPlayer.getCurrentPosition();
}
return 0;
}
public int getDuration(){
return mMediaPlayer.getDuration();
}
public void setAutoPlay(boolean isAutoPlay){
this.isAutoPlay = isAutoPlay;
}
public void setLooping(boolean isLooping){
this.isLooping = isLooping;
}
#Override
public void onHostResume() {
if(isPaused){
resume();
isPaused = false;
}
}
#Override
public void onHostPause() {
pause();
}
#Override
public void onHostDestroy() {
cleanup();
}
public void resume(){
Log.e("DEBUG","IN RESUME");
}
public void pause(){
Log.e("DEBUG","IN PAUSE ");
if (mMediaPlayer != null){
Log.e("DEBUG","!NULL MPLAYER");
mMediaPlayer.pause();
mMediaPlayer.setDisplay(null);
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
}
if (mRenderer != null){
Log.e("DEBUG","!NULL MRENDER");
mRenderer.onPause();
mRenderer = null;
}
surfaceTexture = null;
mediaPlayerReady = false;
isPaused = true;
}
public void cleanup() {
Log.e("DEBUG", "CLEAN ");
if(mMediaPlayer != null){
mMediaPlayer.pause();
mMediaPlayer.release();
}
if(mRenderer != null){
mRenderer.onPause();
}
}
#Override
public void onSurfaceTextureAvailable(final SurfaceTexture surface, int width, int height) {
Log.e("DEBUG", "surface avalilable");
isTextureAvailable = true;
surfaceTexture = surface;
surfaceWidth = width;
surfaceHeight = height;
setup();
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
Log.e("DEBUG", "MASUK ONPREPARED");
final Thread thread = new Thread(new Runnable() {
#Override
public void run() {
mRenderer.setMediaPlayer(mMediaPlayer);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.setSurface(new Surface(surface));
mRenderer.setDimension(mMediaPlayer.getVideoWidth(), mMediaPlayer.getVideoHeight());
mRenderer.setShader(fragmentShaderCode);
mRenderer.setCrop(isCrop);
if(!mMediaPlayer.isLooping()){
mMediaPlayer.setLooping(isLooping);
}
if(isAutoPlay){
mMediaPlayer.start();
}else{
mMediaPlayer.seekTo(seekto);
}
mediaPlayerReady = true;
}
});
thread.run();
}
}
This the UI Manager code
public class RNVideoPlayerManager extends SimpleViewManager<VideoPlayer>{
private static final String REACT_CLASS = "RNVideoEditPlayer";
private static final String PROPS_SRC = "src";
private static final String PROPS_CROP = "isCrop";
private static final String PROPS_SEEK = "seekTo";
private static final String PROPS_AUTOPLAY = "isAutoPlay";
private static final String PROPS_LOOPING = "isLoop";
private static final String PROPS_FILTER = "filter";
#Override
public String getName(){
return REACT_CLASS;
}
#Override
public VideoPlayer createViewInstance(ThemedReactContext themedReactContext){
return new VideoPlayer(themedReactContext);
}
#Override
public void onDropViewInstance(VideoPlayer videoPlayer) {
super.onDropViewInstance(videoPlayer);
videoPlayer.cleanup();
}
#ReactProp(name = PROPS_SRC)
public void setSource(final VideoPlayer videoPlayer, final String src){
videoPlayer.setSource(src);
}
#ReactProp(name = PROPS_CROP, defaultBoolean = false)
public void setCrop(final VideoPlayer videoPlayer, final boolean isCrop){
videoPlayer.setCrop(isCrop);
}
#ReactProp(name = PROPS_SEEK, defaultInt = -1)
public void seekTo(final VideoPlayer videoPlayer, final int seekTime){
if(seekTime > 0){
videoPlayer.seekTo(seekTime);
}else{
return;
}
}
#ReactProp(name = PROPS_AUTOPLAY, defaultBoolean = false)
public void setAutoPlay(final VideoPlayer videoPlayer, final boolean isAutoPlay){
videoPlayer.setAutoPlay(isAutoPlay);
}
#ReactProp(name = PROPS_LOOPING, defaultBoolean = false)
public void setLoop(final VideoPlayer videoPlayer, final boolean isLoop){
videoPlayer.setLooping(isLoop);
}
#ReactProp(name = PROPS_FILTER, defaultInt = 0)
public void setFilter(final VideoPlayer videoPlayer, final int filterId){
videoPlayer.setFilter(filterId);
}
}
And this my javascript component code
import React, { Component, PropTypes } from 'react'
import {
AppRegistry,
requireNativeComponent,
View
} from 'react-native'
var iface = {
name: 'VideoPlayer',
propTypes: {
src : PropTypes.string,
isCrop : PropTypes.bool,
seekTo : PropTypes.number,
isAutoPlay : PropTypes.bool,
isLoop : PropTypes.bool,
filter : PropTypes.number,
...View.propTypes,
}
}
const VideoPlayer = requireNativeComponent('RNVideoEditPlayer', iface)
export class VideoEditPlayer extends Component{
static propTypes = {
src: PropTypes.string.isRequired,
isCrop : PropTypes.bool,
seekTo : PropTypes.number,
isAutoPlay : PropTypes.bool,
isLoop : PropTypes.bool,
filter : PropTypes.number,
style : PropTypes.object,
...View.propTypes,
}
static defaultProps = {
filter : 0,
isCrop : false,
isLoop : false,
isAutoPlay : false,
}
constructor(props){
super(props)
}
render(){
const{
src,
isCrop,
isLoop,
isAutoPlay,
seekTo,
filter,
style,
...props
} = this.props
return(
<VideoPlayer
style={style}
src={src}
isCrop={isCrop}
isAutoPlay={isAutoPlay}
isLoop={isLoop}
filter={filter}
seekTo={seekTo}
{...props}
/>
);
}
}
I have made an android Service to play music, when i close my application then it keeps on playing song for few minutes and then automatically stops without even calling destroy method. I am not able to understand why is it happening.
Below is the code of my Service class.
public class MusicService extends Service implements MediaPlayer.OnSeekCompleteListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener,
MediaPlayer.OnInfoListener, MediaPlayer.OnErrorListener, MediaPlayer.OnBufferingUpdateListener {
private MediaPlayer player;
private int songPosition;
private IBinder iBinder = new LocalBinder();
private PhoneStateListener phoneStateListener;
private TelephonyManager telephonyManager;
private boolean isPaused = false;
private ArrayList<Song> songsList;
private boolean isRepeat = false;
private boolean isShuffle = false;
private Intent broadcastIntent;
public static final String BROADCAST_INTENT = "music.akumar.com.musicplayer.seekbarintent";
private Handler handler = new Handler();
public class LocalBinder extends Binder {
public MusicService getService() {
return MusicService.this;
}
}
#Override
public void onCreate() {
broadcastIntent = new Intent(BROADCAST_INTENT);
player = new MediaPlayer();
player.setOnBufferingUpdateListener(this);
player.setOnCompletionListener(this);
player.setOnSeekCompleteListener(this);
player.setOnPreparedListener(this);
player.setOnInfoListener(this);
player.setOnErrorListener(this);
player.setLooping(true);
player.reset();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
telephonyManager = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
phoneStateListener = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch(state) {
case TelephonyManager.CALL_STATE_OFFHOOK:
case TelephonyManager.CALL_STATE_RINGING:
if (player.isPlaying()) {
pauseMedia();
isPaused = true;
}
break;
case TelephonyManager.CALL_STATE_IDLE:
if (player != null) {
if (isPaused) {
playMedia();
isPaused = false;
}
}
break;
}
}
};
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
return START_STICKY;
}
public void playMedia() {
if (player != null && !player.isPlaying()) {
player.start();
}
}
public void pauseMedia() {
if (player != null && player.isPlaying()) {
player.pause();
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (player.isPlaying()) {
player.stop();
}
player.release();
}
#Override
public IBinder onBind(Intent intent) {
return iBinder;
}
public void playSong(Song song) {
player.reset();
try {
player.setDataSource(getApplicationContext(), Uri.parse(song.getPath()));
player.prepare();
playMedia();
setHandler();
} catch (IOException e) {
e.printStackTrace();
}
}
private void setHandler() {
handler.removeCallbacks(sendUpdatesToUI);
handler.postDelayed(sendUpdatesToUI, 500);
}
private Runnable sendUpdatesToUI = new Runnable() {
#Override
public void run() {
logMediaPosition();
handler.postDelayed(this, 500);
}
};
private void logMediaPosition() {
}
public void playNext() {
}
public void playPrev() {
}
public boolean isPlaying() {
return player.isPlaying();
}
public int getSongPosition() {
return songPosition;
}
public void setSongPosition(int songPosition) {
this.songPosition = songPosition;
}
public ArrayList<Song> getSongsList() {
return songsList;
}
public void setSongsList(ArrayList<Song> songsList) {
this.songsList = songsList;
}
public boolean isRepeat() {
return isRepeat;
}
public void setRepeat(boolean isRepeat) {
this.isRepeat = isRepeat;
}
public boolean isShuffle() {
return isShuffle;
}
public void setShuffle(boolean isShuffle) {
this.isShuffle = isShuffle;
}
public void addSongToList(Song song) {
songsList.add(song);
}
public Song getCurrentSong() {
return songsList.get(songPosition);
}
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
}
#Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i2) {
return false;
}
#Override
public boolean onInfo(MediaPlayer mediaPlayer, int i, int i2) {
return false;
}
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
}
#Override
public void onSeekComplete(MediaPlayer mediaPlayer) {
}
}
And below the code of my activity class.
public class MusicPlayer extends FragmentActivity implements View.OnClickListener, SeekBar.OnSeekBarChangeListener {
private boolean mBound = false;
private MusicService musicService;
private ArrayList<Song> songsList = new ArrayList<Song>();
private boolean isRepeat = false;
private boolean isShuffle = false;
private int position;
private ImageButton btnPlay;
private ImageButton btnPlayNext;
private ImageButton btnPlayPrev;
private ImageButton btnRepeat;
private ImageButton btnShuffle;
private ImageButton btnFavorite;
private TextView currentSongView;
private SeekBar seekBar;
private TextView songCurrentDurationLabel;
private TextView songTotalDurationLabel;
private ViewPager viewPager;
private BroadcastReceiver broadcastReceiver;
private boolean mBroadcastIsRegistered;
ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mBound = true;
MusicService.LocalBinder localBinder = (MusicService.LocalBinder)iBinder;
musicService = localBinder.getService();
musicService.setSongsList(songsList);
musicService.setSongPosition(0);
musicService.playSong(0);
btnPlay.setImageResource(R.drawable.btn_pause);
registerReceiver(broadcastReceiver, new IntentFilter(MusicService.BROADCAST_INTENT));
mBroadcastIsRegistered = true;
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mBound = false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_music_player);
songsList = prepareListOfSongsAndAlbumMap();
currentSongView = (TextView)findViewById(R.id.currentSongView);
btnPlay = (ImageButton)findViewById(R.id.btnPlay);
btnPlayNext = (ImageButton)findViewById(R.id.btnNext);
btnPlayPrev = (ImageButton)findViewById(R.id.btnPrevious);
btnRepeat = (ImageButton)findViewById(R.id.btnRepeat);
btnShuffle = (ImageButton)findViewById(R.id.btnShuffle);
btnFavorite = (ImageButton)findViewById(R.id.btnFavourite);
seekBar = (SeekBar)findViewById(R.id.SeekBar01);
songCurrentDurationLabel = (TextView)findViewById(R.id.songCurrentDurationLabel);
songTotalDurationLabel = (TextView)findViewById(R.id.songTotalDurationLabel);
viewPager = (ViewPager)findViewById(R.id.viewPagerMusicPlayer);
position = 0;
btnPlay.setOnClickListener(this);
btnPlayNext.setOnClickListener(this);
btnPlayPrev.setOnClickListener(this);
btnRepeat.setOnClickListener(this);
btnShuffle.setOnClickListener(this);
btnFavorite.setOnClickListener(this);
seekBar.setOnSeekBarChangeListener(this);
DataTransferBetweenActivity data = new DataTransferBetweenActivity(songsList, position);
MusicPlayerTabAdapter musicPlayerTabAdapter = new MusicPlayerTabAdapter(getSupportFragmentManager(), data);
viewPager.setAdapter(musicPlayerTabAdapter);
broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
updateUI(intent);
}
};
}
private void updateUI(Intent serviceIntent) {
}
private ArrayList<Song> prepareListOfSongsAndAlbumMap() {
Uri musicUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection;
projection = null;
String sortOrder = null;
String selectionMimeType = MediaStore.Audio.Media.IS_MUSIC + " !=0";
Cursor musicCursor = getContentResolver().query(musicUri, projection, selectionMimeType, null, sortOrder);
int count = musicCursor.getCount();
if (musicCursor.moveToFirst()) {
do {
String path = musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.DATA));
String title = musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
String album = musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));
String artist = musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
int artistId = musicCursor.getInt(musicCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST_ID));
int albumId = musicCursor.getInt(musicCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
int trackId = musicCursor.getInt(musicCursor.getColumnIndex(MediaStore.Audio.Media.TRACK));
long duration = musicCursor.getInt(musicCursor.getColumnIndex(MediaStore.Audio.Media.DURATION));
Uri sArtworkUri = Uri
.parse("content://media/external/audio/albumart");
Uri albumArtUri = ContentUris.withAppendedId(sArtworkUri, albumId);
Song song = new Song(path, title, album, artist, albumId, trackId, duration, albumArtUri.toString(), artistId);
songsList.add(song);
} while (musicCursor.moveToNext());
Collections.sort(songsList, new Comparator<Song>() {
#Override
public int compare(Song song, Song song2) {
return song.getTitle().toUpperCase().compareTo(song2.getTitle().toUpperCase());
}
});
musicCursor.close();
}
return songsList;
}
#Override
protected void onStart() {
super.onStart();
Intent serviceIntent = new Intent(this, MusicService.class);
startService(serviceIntent);
bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE);
}
#Override
protected void onDestroy() {
super.onDestroy();
if (mBound) {
unbindService(serviceConnection);
mBound = false;
}
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btnPlay:
break;
case R.id.btnNext:
break;
case R.id.btnPrevious:
break;
case R.id.btnRepeat:
break;
case R.id.btnShuffle:
break;
}
}
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
#Override
protected void onPause() {
if (mBroadcastIsRegistered) {
unregisterReceiver(broadcastReceiver);
mBroadcastIsRegistered = false;
}
super.onPause();
}
#Override
protected void onResume() {
if (!mBroadcastIsRegistered) {
registerReceiver(broadcastReceiver, new IntentFilter(MusicService.BROADCAST_INTENT));
mBroadcastIsRegistered = true;
}
super.onResume();
}
public void playSong(int position) {
musicService.playSong(position);
musicService.setSongPosition(position);
}
}
I am starting my service with startService() method and i am not calling stopService() or stopSelf() anywhere, but i am not able to figure out why it stops playing after sometime.
I think you should call startForeground() from your service.
Here is a sample project of a music player. I might be helpful
FakePlayer
I'm working on a music player for android and i'm stuck at this problem.
By now i can play a song with musicservice and send it to background, i also display a notification with current playing song.
What i need is to re-open the main activity from the song notification and continue playing the song, it actually starts the desired activity but the music service is re-created and it stops the current playing song.
Here is my code.
MusicService.java
public class MusicService extends Service implements
MediaPlayer.OnPreparedListener,
MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener {
private final IBinder musicBind = new MusicBinder();
//media player
private MediaPlayer player;
//song list
private ArrayList<SongModel> songs;
//current position
private int songPosition;
public MusicService() {
}
public void onCreate() {
//create the service
super.onCreate();
//initialize position
songPosition = 0;
//create player
player = new MediaPlayer();
initMusicPlayer();
}
public void initMusicPlayer() {
//set player properties
player.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setOnPreparedListener(this);
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
}
public void setList(ArrayList<SongModel> theSongs) {
songs = theSongs;
}
public class MusicBinder extends Binder {
public MusicService getService() {
return MusicService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return musicBind;
}
#Override
public boolean onUnbind(Intent intent) {
player.stop();
player.release();
return false;
}
public int getPosition() {
return player.getCurrentPosition();
}
public int getCurrenListPosition() {
return songPosition;
}
public int getDuration() {
return player.getDuration();
}
public boolean isPlaying() {
return player.isPlaying();
}
public void pausePlayer() {
player.pause();
}
public void stopPlayer() {
player.stop();
}
public void seekToPosition(int posn) {
player.seekTo(posn);
}
public void start() {
player.start();
}
public void playSong() {
try {
//play a song
player.reset();
SongModel playSong = songs.get(songPosition);
String trackUrl = playSong.getFileUrl();
player.setDataSource(trackUrl);
player.prepareAsync();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onCompletion(MediaPlayer mp) {
if (mp.getCurrentPosition() == 0) {
mp.reset();
}
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mp.reset();
return false;
}
#Override
public void onPrepared(MediaPlayer mp) {
//start playback
mp.start();
SongModel playingSong = songs.get(songPosition);
Intent intent = new Intent(this, NavDrawerMainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder builder = new Notification.Builder(this);
builder.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.ic_action_playing)
.setTicker(playingSong.getTitle())
.setOngoing(true)
.setContentTitle(playingSong.getTitle())
.setContentText(playingSong.getArtistName());
Notification notification = builder.build();
startForeground((int) playingSong.getSongId(), notification);
}
#Override
public void onDestroy() {
stopForeground(true);
}
public void setSong(int songIndex) {
songPosition = songIndex;
}
}
DiscoverSongsFragment.java
public class DiscoverSongsFragment extends Fragment
implements MediaController.MediaPlayerControl {
JazzyGridView songsContainer;
SongsHelper songsHelper;
SongsAdapter songsAdapter;
ArrayList<SongModel> songsArrayList;
ConnectionState connectionState;
Context mContext;
private static View rootView;
SongModel currentSong;
SeekBar nowPlayingSeekBar;
final Handler handler = new Handler();
// this value contains the song duration in milliseconds.
// Look at getDuration() method in MediaPlayer class
int mediaFileLengthInMilliseconds;
View nowPlayingLayout;
boolean nowPlayingLayoutVisible;
TextView nowPlayingTitle;
TextView nowPlayingArtist;
ImageButton nowPlayingCover;
ImageButton nowPlayingStop;
private MusicService musicService;
private Intent playIntent;
private boolean musicBound = false;
private boolean playbackPaused = false;
private int mCurrentTransitionEffect = JazzyHelper.SLIDE_IN;
public static DiscoverSongsFragment newInstance() {
return new DiscoverSongsFragment();
}
public DiscoverSongsFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_discover_songs, container, false);
mContext = rootView.getContext();
setupViews(rootView);
return rootView;
}
#Override
public void onStart() {
super.onStart();
if (playIntent == null) {
playIntent = new Intent(mContext, MusicService.class);
mContext.bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
mContext.startService(playIntent);
}
}
#Override
public void onResume() {
super.onResume();
if (isPlaying()) {
showNowPlayingLayout();
primarySeekBarProgressUpdater();
}
}
#Override
public void onDestroy() {
mContext.stopService(playIntent);
musicService = null;
super.onDestroy();
}
private void hideNowPlayingLayout() {
nowPlayingLayoutVisible = false;
nowPlayingLayout.setVisibility(View.GONE);
Animation animationFadeIn = AnimationUtils.loadAnimation(mContext, R.anim.fade_out);
nowPlayingLayout.startAnimation(animationFadeIn);
}
private void showNowPlayingLayout() {
nowPlayingLayoutVisible = true;
nowPlayingLayout.setVisibility(View.VISIBLE);
Animation animationFadeIn = AnimationUtils.loadAnimation(mContext, R.anim.fade_in);
nowPlayingLayout.startAnimation(animationFadeIn);
}
private void setupViews(View rootView) {
songsHelper = new SongsHelper();
songsArrayList = new ArrayList<SongModel>();
connectionState = new ConnectionState(mContext);
songsAdapter = new SongsAdapter(mContext, songsArrayList);
nowPlayingLayout = rootView.findViewById(R.id.nowPlayingLayout);
nowPlayingLayout.setVisibility(View.GONE);
nowPlayingLayoutVisible = false;
songsContainer = (JazzyGridView) rootView.findViewById(R.id.songsContainerView);
songsContainer.setTransitionEffect(mCurrentTransitionEffect);
songsContainer.setAdapter(songsAdapter);
songsContainer.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
musicService.setSong(position);
musicService.playSong();
if (playbackPaused) {
playbackPaused = false;
}
currentSong = songsArrayList.get(position);
// gets the song length in milliseconds from URL
mediaFileLengthInMilliseconds = getDuration();
if (currentSong != null) {
nowPlayingTitle.setText(currentSong.getTitle());
nowPlayingArtist.setText(currentSong.getArtistName());
nowPlayingCover.setImageBitmap(currentSong.getCoverArt());
}
primarySeekBarProgressUpdater();
if (!nowPlayingLayoutVisible) {
showNowPlayingLayout();
}
}
});
nowPlayingSeekBar = (SeekBar) rootView.findViewById(R.id.nowPlayingSeekbar);
nowPlayingSeekBar.setMax(99);
nowPlayingTitle = (TextView) rootView.findViewById(R.id.nowPlayingTitle);
nowPlayingArtist = (TextView) rootView.findViewById(R.id.nowPlayingArtist);
nowPlayingStop = (ImageButton) rootView.findViewById(R.id.nowPlayingStop);
nowPlayingStop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (isPlaying()) {
currentSong = null;
playbackPaused = false;
musicService.stopPlayer();
mediaFileLengthInMilliseconds = 0;
nowPlayingSeekBar.setProgress(0);
hideNowPlayingLayout();
}
}
});
nowPlayingCover = (ImageButton) rootView.findViewById(R.id.nowPlayingCover);
nowPlayingCover.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
Intent intent = new Intent(mContext, SongDetailsActivity.class);
intent.putExtra("Title", currentSong.getTitle());
intent.putExtra("Artist", currentSong.getArtistName());
intent.putExtra("Album", currentSong.getAlbumName());
intent.putExtra("Genre", currentSong.getGenre());
intent.putExtra("CoverUrl", currentSong.getCoverArtUrl());
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
});
getSongs();
hideNowPlayingLayout();
}
private void getSongs() {
if (!connectionState.isConnectedToInternet()) {
}
songsAdapter.clear();
String songsUrl = Constants.getAPI_SONGS_URL();
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(songsUrl, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray jsonArray) {
if (jsonArray != null) {
for (int i = 0; i < jsonArray.length(); i++) {
try {
JSONObject jsonObject = jsonArray.getJSONObject(i);
SongModel song = songsHelper.getSongFromJson(jsonObject);
songsAdapter.add(song);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
}
});
AppController.getInstance().addToRequestQueue(jsonArrayRequest);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
}
#Override
public void onDetach() {
super.onDetach();
}
/**
* Method which updates the SeekBar primary progress by current song playing position
*/
private void primarySeekBarProgressUpdater() {
nowPlayingSeekBar.setProgress((int) (((float) getCurrentPosition() / getDuration()) * 100));
//if (isPlaying()) {
Runnable runnable = new Runnable() {
public void run() {
primarySeekBarProgressUpdater();
}
};
handler.postDelayed(runnable, 1000);
//}
}
#Override
public void start() {
musicService.start();
}
#Override
public void pause() {
playbackPaused = true;
musicService.pausePlayer();
}
#Override
public int getDuration() {
if (musicService != null && musicBound && musicService.isPlaying()) {
return musicService.getDuration();
}
return 0;
}
#Override
public int getCurrentPosition() {
if (musicService != null && musicBound && musicService.isPlaying()) {
return musicService.getPosition();
}
return 0;
}
public int getCurrentListPosition() {
if (musicService != null && musicBound && musicService.isPlaying()) {
return musicService.getCurrenListPosition();
}
return 0;
}
#Override
public void seekTo(int pos) {
musicService.seekToPosition(pos);
}
#Override
public boolean isPlaying() {
if (musicService != null && musicBound && musicService.isPlaying()) {
return musicService.isPlaying();
}
return false;
}
#Override
public int getBufferPercentage() {
return 0;
}
#Override
public boolean canPause() {
return true;
}
#Override
public boolean canSeekBackward() {
return true;
}
#Override
public boolean canSeekForward() {
return true;
}
#Override
public int getAudioSessionId() {
return 0;
}
//connect to the service
private ServiceConnection musicConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
//get service
musicService = binder.getService();
//pass list
musicService.setList(songsArrayList);
musicBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
musicBound = false;
}
};
}
(The fragment also re-creates itself when navigating through drawer menu items)
I hope somebody can help me achieve this. I dont know how to maintane the state when re-starting the MainActivity (by the way, im using navdrawer to hold fragments)
Include the currently playing song in your notification intent. Update the intent as the song changes. Include the flag to clear top and the flag to update current in the notification intent. :( sorry IDK if I have the flags right for your situation but you'll have a place to do more research.
In your service where you create the notification intent.
// link the notifications to the recorder activity
Intent resultIntent = new Intent(context, KmlReader.class);
resultIntent
.setAction(ServiceLocationRecorder.INTENT_COM_GOSYLVESTER_BESTRIDES_LOCATION_RECORDER);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent resultPendingIntent = PendingIntent
.getActivity(context, 0, resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
return mBuilder.build();
}
Then in main onCreate check the bundle for the name of the currently playing song and display it. Notice how I check for the existence of a bundle key before using it.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
String intentaction = intent.getAction();
// First Run checks
if (savedInstanceState == null) {
// first run init
...
} else {
// get the saved_Instance state
// always get the default when key doesn't exist
// default is null for this example
currentCameraPosition = savedInstanceState
.containsKey(SAVED_INSTANCE_CAMERA_POSITION) ? (CameraPosition) savedInstanceState
.getParcelable(SAVED_INSTANCE_CAMERA_POSITION) : null;
...