In my App, I have fragment in which I, alongside with other, play video in ExoPlayer. I set it up as simple as possible and the player itself works flawlessly, even with media session.
But the problem is that on device rotation, the video loads again in background (there seems to be two layers - one standard, functioning normally, loading video and playing it; and second layer in background. I can hear the sound of the video shortly after the activity recreates.
I am stopping and releasing the player and media session, but it didn't help.
MediaFragment.java
public class StepViewFragment extends Fragment implements Player.EventListener{
//vars
public StepViewFragment() {
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
//attach callbacks for buttons
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_step_view, container, false);
//Bind view ButterKnife
ButterKnife.bind(this, rootView);
//get Step from Bundle
Bundle bundle = this.getArguments();
if (bundle != null) {
step = bundle.getParcelable(AppConstants.STEP_BUNDLE_KEY);
recipe = bundle.getParcelable(AppConstants.RECIPE_BUNDLE_KEY);
}
mainHandler = new Handler();
//...
//setup ui
//...
if (step.videoURL.equals("")) {
mediaCard.setVisibility(View.GONE);
} else {
playVideo();
initMediaSession();
}
return rootView;
}
public void initMediaSession (){
mExoPlayer.addListener(this);
mMediaSession = new MediaSessionCompat(getContext(), TAG);
mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS);
mMediaSession.setMediaButtonReceiver(null);
mStateBuilder = new PlaybackStateCompat.Builder()
.setActions(
PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE
);
mMediaSession.setPlaybackState(mStateBuilder.build());
mMediaSession.setCallback(new MySessionCallback());
mMediaSession.setActive(true);
}
public void playVideo() {
if (mExoPlayer == null) {
DefaultTrackSelector trackSelector = new DefaultTrackSelector();
LoadControl loadControl = new DefaultLoadControl();
RenderersFactory renderersFactory = new DefaultRenderersFactory(getContext());
mExoPlayer = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector, loadControl);
mPlayerView.setPlayer(mExoPlayer);
String videoUrl = step.videoURL;
Uri mp4VideoUri = Uri.parse(videoUrl);
String userAgent = Util.getUserAgent(getContext(), "BakingApp");
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(getContext(), userAgent);
ExtractorMediaSource.Factory mediaSource = new ExtractorMediaSource.Factory(dataSourceFactory);
mExoPlayer.prepare(mediaSource.createMediaSource(mp4VideoUri));
mPlayerView.hideController();
mExoPlayer.setPlayWhenReady(true);
}
}
public void releasePlayer() {
mExoPlayer.stop();
mExoPlayer.release();
mExoPlayer = null;
mMediaSession.release();
mPlayerView = null;
}
#Override
public void onStop() {
if (mExoPlayer != null) {
releasePlayer();
}
super.onStop();
}
#Override
public void onDestroyView() {
super.onDestroyView();
if (mExoPlayer != null) {
releasePlayer();
}
}
#Override
public void onPause() {
super.onPause();
if (mExoPlayer != null) {
releasePlayer();
}
}
#Override
public void onDetach() {
super.onDetach();
if (mExoPlayer != null) {
releasePlayer();
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (mExoPlayer != null){
releasePlayer();
}
}
#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) {
if ((playbackState == Player.STATE_READY) && playWhenReady) {
mStateBuilder.setState(PlaybackStateCompat.STATE_PLAYING, mExoPlayer.getCurrentPosition(), 1f);
} else if ((playbackState == Player.STATE_READY)){
mStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED, mExoPlayer.getCurrentPosition(), 1f);
}
mMediaSession.setPlaybackState(mStateBuilder.build());
Log.d("HOVNOOOO", "Playback State Changed");
}
#Override
public void onRepeatModeChanged(int repeatMode) {
}
#Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
}
#Override
public void onPlayerError(ExoPlaybackException error) {
}
#Override
public void onPositionDiscontinuity(int reason) {
}
#Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
}
#Override
public void onSeekProcessed() {
}
private class MySessionCallback extends MediaSessionCompat.Callback {
#Override
public void onPlay() {
mExoPlayer.setPlayWhenReady(true);
}
#Override
public void onPause() {
mExoPlayer.setPlayWhenReady(false);
}
}
Solved...
I was using add() to create the Fragment, so on rotation it added new one.
Using replace instead solved the problem.
Related
Mediabrowsercompat not playing the music when app goes background. If I don't disconnect the mediabrowsercompat instance in activity onStop method then it is working. But that is not the solution as according to documentation we have to disconnect the service once app goes background.
This is my mediabroweserservicecomat class (I am using Exoplayer to play music):
private static final String PLAYBACK_CHANNEL_ID = "100";
private static final int PLAYBACK_NOTIFICATION_ID =101 ;
private MediaSessionCompat mediaSessionCompat;
private PlaybackStateCompat.Builder stateBuilder;
private SimpleExoPlayer exoPlayer;
private Uri oldUri;
private AudioAttributes audioAttributes;
private PlayerNotificationManager playerNotificationManager;
#Override
public void onCreate() {
super.onCreate();
Log.i("Test","Hi");
initPlayer();
initAttributes();
ComponentName mediaButtonReceiver = new ComponentName(getApplicationContext(), MediaButtonReceiver.class);
mediaSessionCompat = new MediaSessionCompat(getApplicationContext(), "Tag", mediaButtonReceiver, null);
mediaSessionCompat.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS|
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
stateBuilder = new PlaybackStateCompat.Builder();
stateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY|PlaybackStateCompat.ACTION_PLAY_PAUSE);
mediaSessionCompat.setPlaybackState(stateBuilder.build());
mediaSessionCompat.setCallback(mediaSessionCompatCallback);
setSessionToken(mediaSessionCompat.getSessionToken());
mediaSessionCompat.setActive(true);
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.setClass(this, MediaButtonReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, 0);
mediaSessionCompat.setMediaButtonReceiver(pendingIntent);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
MediaButtonReceiver.handleIntent(mediaSessionCompat,intent);
return super.onStartCommand(intent, flags, startId);
}
private MediaSessionCompat.Callback mediaSessionCompatCallback = new MediaSessionCompat.Callback() {
#Override
public void onPlayFromUri(Uri uri, Bundle extras) {
super.onPlayFromUri(uri, extras);
if (uri!=null){
MediaItem mediaSource = MediaItem.fromUri(uri);
if (uri!=oldUri){
play(mediaSource);
onPlay();
}else {
oldUri = uri;
}
}
}
#Override
public void onPlay() {
super.onPlay();
Log.i("onPlay","onPlay");
//startService(new Intent(MusicService.this,MusicForegroundService.class));
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(MusicService.this,
PLAYBACK_CHANNEL_ID, R.string.channel_name, PLAYBACK_NOTIFICATION_ID,
new PlayerNotificationManager.MediaDescriptionAdapter() {
#Override
public CharSequence getCurrentContentTitle(Player player) {
return "title";
}
#Nullable
#Override
public PendingIntent createCurrentContentIntent(Player player) {
Intent intent = new Intent(getBaseContext(), PlayerFullViewActivity.class);
return PendingIntent.getActivity(getBaseContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
#Nullable
#Override
public CharSequence getCurrentContentText(Player player) {
return "content";
}
#Nullable
#Override
public Bitmap getCurrentLargeIcon(Player player, PlayerNotificationManager.BitmapCallback callback) {
return null;
}
},
new PlayerNotificationManager.NotificationListener() {
#Override
public void onNotificationPosted(int notificationId, Notification notification, boolean ongoing) {
startForeground(notificationId,notification);
}
#Override
public void onNotificationCancelled(int notificationId, boolean dismissedByUser) {
stopSelf();
}
});
playerNotificationManager.setPlayer(exoPlayer);
}
#Override
public void onPause() {
super.onPause();
pause();
}
#Override
public void onStop() {
playerNotificationManager.setPlayer(null);
super.onStop();
stop();
}
};
#Override
public void onDestroy() {
super.onDestroy();
stop();
}
private void stop() {
exoPlayer.setPlayWhenReady(false);
exoPlayer.release();
exoPlayer =null;
updatePlayBackState(PlaybackStateCompat.STATE_NONE);
mediaSessionCompat.setActive(false);
mediaSessionCompat.release();
}
#SuppressLint("WrongConstant")
private void pause() {
if (exoPlayer!=null){
exoPlayer.setPlayWhenReady(false);
if (exoPlayer.getPlaybackState()==PlaybackStateCompat.STATE_PLAYING){
updatePlayBackState(PlaybackStateCompat.STATE_PAUSED);
}
}
}
private void play(MediaItem mediaSource){
if (exoPlayer==null) {
initPlayer();
}
if (audioAttributes==null) {
initAttributes();
}
exoPlayer.setAudioAttributes(audioAttributes,true);
exoPlayer.setMediaItem(mediaSource);
exoPlayer.prepare();
play();
}
private void initAttributes() {
audioAttributes = new AudioAttributes.Builder().setUsage(C.USAGE_MEDIA)
.setContentType(C.CONTENT_TYPE_MUSIC)
.build();
}
private void initPlayer() {
exoPlayer = new SimpleExoPlayer.Builder(this, new DefaultRenderersFactory(getBaseContext()),
new DefaultExtractorsFactory()).build();
}
private void play() {
exoPlayer.setPlayWhenReady(true);
updatePlayBackState(PlaybackStateCompat.STATE_PLAYING);
mediaSessionCompat.setActive(true);
}
private void updatePlayBackState(int statePlaying) {
mediaSessionCompat.setPlaybackState(new PlaybackStateCompat.Builder().
setState(statePlaying,0L,0).build());
}
#Nullable
#Override
public BrowserRoot onGetRoot(#NonNull String clientPackageName, int clientUid, #Nullable Bundle rootHints) {
return new BrowserRoot("",null);
}
#Override
public void onLoadChildren(#NonNull String parentId, #NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
result.sendResult(null);
}
This is my activity class:
ActivityPlayerFullViewBinding activityPlayerFullViewBinding;
private MediaBrowserCompat mediaBrowserCompat;
private MediaBrowserCompat.ConnectionCallback connectionCallback = new MediaBrowserCompat.ConnectionCallback(){
#Override
public void onConnected() {
super.onConnected();
MediaSessionCompat.Token sessionToken = mediaBrowserCompat.getSessionToken();
if (sessionToken!=null){
try {
MediaControllerCompat mediaControllerCompat = new
MediaControllerCompat(PlayerFullViewActivity.this, sessionToken);
MediaControllerCompat.setMediaController(PlayerFullViewActivity.this,mediaControllerCompat);
playPauseBuild();
Log.d("onConnected","ConnectionSuccess");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
#Override
public void onConnectionFailed() {
super.onConnectionFailed();
Log.d("onConnectionFaild","ConnectionFailed");
}
};
MediaControllerCompat.Callback controllerCallback =
new MediaControllerCompat.Callback() {
#Override
public void onMetadataChanged(MediaMetadataCompat metadata) {}
#Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityPlayerFullViewBinding = DataBindingUtil.setContentView(this,R.layout.activity_player_full_view);
ComponentName componentName = new ComponentName(this,MusicService.class);
mediaBrowserCompat = new MediaBrowserCompat(this,componentName,connectionCallback,null);
}
private void playPauseBuild() {
MediaControllerCompat mediaController = MediaControllerCompat.getMediaController(PlayerFullViewActivity.this);
activityPlayerFullViewBinding.playPauseBtn
.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int state = mediaController.getPlaybackState().getState();
if (state== PlaybackStateCompat.STATE_PAUSED||state==PlaybackStateCompat.STATE_STOPPED||
state==PlaybackStateCompat.STATE_NONE){
mediaController.getTransportControls().playFromUri(
Uri.parse("https://www.mboxdrive.com/Eminem-Sing-For-The-Moment9jamo.com_.mp3"),null);
activityPlayerFullViewBinding.playPauseBtn.setText("Pause");
}else if (state == PlaybackStateCompat.STATE_PLAYING ||
state == PlaybackStateCompat.STATE_BUFFERING ||
state == PlaybackStateCompat.STATE_CONNECTING){
mediaController.getTransportControls().pause();
activityPlayerFullViewBinding.playPauseBtn.setText("Play");
}
}
});
mediaController.registerCallback(controllerCallback);
}
#Override
protected void onStart() {
super.onStart();
mediaBrowserCompat.connect();
}
#Override
public void onResume() {
super.onResume();
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
#Override
protected void onStop() {
super.onStop();
Log.i("onStop","onStop");
MediaControllerCompat mediaController = MediaControllerCompat.getMediaController(this);
if (mediaController != null) {
mediaController.unregisterCallback(controllerCallback);
}
mediaBrowserCompat.disconnect();
}
After wasting 2 sleepless nights. I found a workaround. I don't know whether it's a correct way to handle this. But if you have ever faced this problem. You can try it:
#Override
protected void onStart() {
super.onStart();
if (!mediaBrowserCompat.isConnected())
mediaBrowserCompat.connect();
}
#Override
protected void onStop() {
super.onStop();
Log.i("onStop","onStop");
MediaControllerCompat mediaController = MediaControllerCompat.getMediaController(this);
if (mediaController != null) {
mediaController.unregisterCallback(controllerCallback);
}
// mediaBrowserCompat.disconnect();
}
#Override
protected void onDestroy() {
super.onDestroy();
mediaBrowserCompat.disconnect();
}
Below is My fragment that contains exoplayer which will inflate with draggable panel in Activity. My problem is while swiping the draggable panel left or right video is closing but audio is still playing in background. If I swipe off the draggable panel audio also needs to closed.Tried many sources couldn't found any solutions.Also In both fragment and Activity while swipping off the draggable panel onPause() method is not triggred. Also public void onClosedToLeft() method also not getting called.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dragpanelone, container, false);
try {
exoPlayerView = (SimpleExoPlayerView)v.findViewById(R.id.exoplayer);
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelector trackSelector = new DefaultTrackSelector(new AdaptiveTrackSelection.Factory(bandwidthMeter));
exoplayer = ExoPlayerFactory.newSimpleInstance(getActivity(), trackSelector);
Uri uri = Uri.parse(videoURL);
DefaultHttpDataSourceFactory dataSourceFactory = new DefaultHttpDataSourceFactory("exoplayer_video");
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
MediaSource mediaSource = new ExtractorMediaSource(uri, dataSourceFactory, extractorsFactory, null, null);
exoPlayerView.setPlayer(exoplayer);
exoplayer.prepare(mediaSource);
exoplayer.setPlayWhenReady(true);
} catch (Exception e){
e.printStackTrace();
}
return v;
}
#Override
public void onDestroy() {
super.onDestroy();
exoplayer.release();
}
#Override
public void onStop() {
super.onStop();
exoplayer.setPlayWhenReady(false);
}
public void pausePlayback() {
exoplayer.setPlayWhenReady(false); //pauses the playback if it's playing
}
private void releasePlayer() {
if (exoplayer != null) {
exoplayer.release();
exoplayer.setPlayWhenReady(false);
exoplayer = null;
}
}
public static void closePlayer()
{
exoplayer.clearVideoSurface();
}
}
This is my Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeDraggablepanel();
hookDraggablePanelListners();
}
public void initializeDraggablepanel(){
draggablepanel = (DraggablePanel)findViewById(R.id.draggable_panel);
draggablepanel.setFragmentManager(getSupportFragmentManager());
draggablepanel.setTopFragment(new DragpanelFragOne());
draggablepanel.setBottomFragment(new DragPanelTwo());
draggablepanel.setTopViewHeight(550);
draggablepanel.initializeView();
}
private void hookDraggablePanelListners(){
draggablepanel.setDraggableListener(new DraggableListener() {
#Override
public void onMaximized() {
}
#Override
public void onMinimized() {
}
#Override
public void onClosedToLeft() {
DragpanelFragOne.closePlayer();
Intent i = new Intent(MainActivity.this,HomeScreen.class);
startActivity(i);
}
#Override
public void onClosedToRight() {
DragpanelFragOne.closePlayer();
Intent i = new Intent(MainActivity.this,HomeScreen.class);
startActivity(i);
}
});
}
#Override
public void onPause() {
super.onPause();
DragpanelFragOne.closePlayer();
}
#Override
public void onStop() {
super.onStop();
draggablepanel.closeToLeft();
}
#Override
public void onBackPressed() {
draggablepanel.isClickToMinimizeEnabled();
Intent i = new Intent(MainActivity.this,HomeScreen.class);
startActivity(i);
}
}
Solution:
You need to call the method stop() on the exoplayer object whenever you are closing the player, like this:
if (exoPlayer != null) {
exoPlayer.setPlayWhenReady(false);
exoPlayer.stop();
exoPlayer.seekTo(0);
}
Hope this helps.
I am using a service to play music so when I first start the service from an activity and exit it plays properly in the background but when I start another music and exit the background playback stops any idea why this is happening?
public class AudioPlayerService extends Service implements Player.EventListener {
private final IBinder mBinder = new LocalBinder();
private SimpleExoPlayer player;
private Item item;
private PlayerNotificationManager playerNotificationManager;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
releasePlayer();
super.onDestroy();
}
private void releasePlayer() {
if (player != null) {
playerNotificationManager.setPlayer(null);
player.release();
player = null;
stopSelf();
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public SimpleExoPlayer getPlayerInstance() {
if (item != null && player == null && !TextUtils.isEmpty(item.getUrl())) {
startPlayer();
}
return player;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
releasePlayer();
Bundle b = intent.getBundleExtra(AppConstants.BUNDLE_KEY);
if (b != null) {
item = b.getParcelable(AppConstants.ITEM_KEY);
}
if (item != null && player == null && !TextUtils.isEmpty(item.getUrl())) {
startPlayer();
}
String action = intent.getAction();
if (player != null) {
if (!TextUtils.isEmpty(action) && action.equalsIgnoreCase(ACTION_PLAY)) {
player.setPlayWhenReady(true);
}
if (!TextUtils.isEmpty(action) && action.equalsIgnoreCase(ACTION_PAUSE)) {
player.setPlayWhenReady(false);
}
}
return START_STICKY;
}
private void startPlayer() {
final Context context = this;
Uri uri = Uri.parse(item.getUrl());
player = ExoPlayerFactory.newSimpleInstance(context, new DefaultTrackSelector());
DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(context,
Util.getUserAgent(context, getString(R.string.app_name)));
CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(
CommonUtils.getCache(context),
dataSourceFactory,
CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
MediaSource mediaSource = new ExtractorMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(uri);
player.prepare(mediaSource);
player.addListener(this);
player.setPlayWhenReady(true);
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(context, AppConstants.PLAYBACK_CHANNEL_ID,
R.string.playback_channel_name,
AppConstants.PLAYBACK_NOTIFICATION_ID,
new PlayerNotificationManager.MediaDescriptionAdapter() {
#Override
public String getCurrentContentTitle(Player player) {
return item.getTitle();
}
#Nullable
#Override
public PendingIntent createCurrentContentIntent(Player player) {
Intent intent = new Intent(context, PlayerActivity.class);
Bundle serviceBundle = new Bundle();
serviceBundle.putParcelable(AppConstants.ITEM_KEY, item);
intent.putExtra(AppConstants.BUNDLE_KEY, serviceBundle);
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
#Nullable
#Override
public String getCurrentContentText(Player player) {
return item.getSummary();
}
#Nullable
#Override
public Bitmap getCurrentLargeIcon(Player player, PlayerNotificationManager.BitmapCallback callback) {
return null;
}
}
);
playerNotificationManager.setNotificationListener(new PlayerNotificationManager.NotificationListener() {
#Override
public void onNotificationStarted(int notificationId, Notification notification) {
startForeground(notificationId, notification);
}
#Override
public void onNotificationCancelled(int notificationId) {
stopSelf();
}
});
playerNotificationManager.setPlayer(player);
}
private void updateWidget(boolean playWhenReady) {
Intent intent = new Intent(this, PlayerWidget.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
int[] ids = AppWidgetManager.getInstance(getApplication())
.getAppWidgetIds(new ComponentName(getApplication(), PlayerWidget.class));
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
Bundle serviceBundle = new Bundle();
serviceBundle.putParcelable(AppConstants.ITEM_KEY, item);
intent.putExtra(AppConstants.BUNDLE_KEY, serviceBundle);
intent.putExtra(PlayerWidget.WIDGET_PLAYING_EXTRA, playWhenReady);
sendBroadcast(intent);
}
#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) {
updateWidget(playWhenReady);
}
#Override
public void onRepeatModeChanged(int repeatMode) {
}
#Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
}
#Override
public void onPlayerError(ExoPlaybackException error) {
}
#Override
public void onPositionDiscontinuity(int reason) {
}
#Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
}
#Override
public void onSeekProcessed() {
}
public class LocalBinder extends Binder {
public AudioPlayerService getService() {
return AudioPlayerService.this;
}
}
}
This is my playerActivity
public class PlayerActivity extends BaseActivity {
#BindView(R.id.video_view)
PlayerView mPlayerView;
#BindView(R.id.tvTitle)
TextView mTvTitle;
#BindView(R.id.tvSummary)
TextView mTvSummary;
#BindView(R.id.ivThumbnail)
ImageView mIvThumb;
#BindView(R.id.adView)
AdView mAdView;
private String mTitle;
private String mSummary;
private String mImage;
private AudioPlayerService mService;
private Intent intent;
private String shareableLink;
private boolean mBound = false;
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
AudioPlayerService.LocalBinder binder = (AudioPlayerService.LocalBinder) iBinder;
mService = binder.getService();
mBound = true;
initializePlayer();
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mBound = false;
}
};
#SuppressLint("MissingSuperCall")
#Override
protected void onCreate(Bundle savedInstanceState) {
onCreate(savedInstanceState, R.layout.activity_player);
Bundle b = getIntent().getBundleExtra(AppConstants.BUNDLE_KEY);
if (b != null) {
Item item = b.getParcelable(AppConstants.ITEM_KEY);
shareableLink = b.getString(AppConstants.SHARE_KEY);
String mUrl = item.getUrl();
mImage = item.getImage();
mTitle = item.getTitle();
mSummary = item.getSummary();
intent = new Intent(this, AudioPlayerService.class);
Bundle serviceBundle = new Bundle();
serviceBundle.putParcelable(AppConstants.ITEM_KEY, item);
intent.putExtra(AppConstants.BUNDLE_KEY, serviceBundle);
Util.startForegroundService(this, intent);
mPlayerView.setUseController(true);
mPlayerView.showController();
mPlayerView.setControllerAutoShow(true);
mPlayerView.setControllerHideOnTouch(false);
}
AdRequest adRequest = new AdRequest.Builder().build();
mAdView.loadAd(adRequest);
}
private void initializePlayer() {
if (mBound) {
SimpleExoPlayer mPlayer = mService.getPlayerInstance();
mPlayerView.setPlayer(mPlayer);
}
}
#Override
public void onStart() {
super.onStart();
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
initializePlayer();
setUI();
}
private void setUI() {
mTvTitle.setText(mTitle);
mTvSummary.setText(Html.fromHtml(mSummary));
GlideApp.with(this)
.load(mImage)
.placeholder(R.drawable.about_background)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(mIvThumb);
}
#Override
protected void onStop() {
unbindService(mConnection);
mBound = false;
super.onStop();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.player_menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.share_podcast:
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_SUBJECT, mTitle);
shareIntent.putExtra(Intent.EXTRA_TEXT, mTitle + "\n\n" + shareableLink);
shareIntent.setType("text/plain");
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_text)));
return true;
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
public void onToolBarSetUp(Toolbar toolbar, ActionBar actionBar) {
TextView tvHeader = toolbar.findViewById(R.id.tvClassName);
tvHeader.setText(R.string.app_name);
}
}
The music should be playing even if I exit the activity which happens properly when I select the music for the first time but when I re-select another track while one is already playing background it changes the track but as soon as I exit the activity the service stops.
I am trying to set Exoplayer into Fragment, I passed video url in initializePlayer(String mediaUri)
video working well, but if I rotate the device, video restarted, I read several tutorials to solve this issue with no success to call seekTo()
sorry for my English
public RecipeStepsVideoPlayerFragment() {
// Required empty public constructor
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view= inflater.inflate(R.layout.fragment_recipe_steps_video_player, container, false);
ButterKnife.bind(this, view);
Bundle bundle=getArguments();
if(bundle!=null){
videoUrl=bundle.getString(VIDEO_URL_KEY);
}
initializePlayer(videoUrl);
stepDescription.setText(bundle.getString(STEP_DESCRIPTION_KEY));
return view;
}
#Override
public void onViewStateRestored(#Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
if (savedInstanceState != null) {
startAutoPlay = savedInstanceState.getBoolean(KEY_AUTO_PLAY);
startWindow = savedInstanceState.getInt(KEY_WINDOW);
startPosition = savedInstanceState.getLong(KEY_POSITION);
Toast.makeText(getContext(),startPosition.toString(), Toast.LENGTH_LONG).show();
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
releasePlayer();
outState.putBoolean(KEY_AUTO_PLAY, startAutoPlay);
outState.putInt(KEY_WINDOW, startWindow);
outState.putLong(KEY_POSITION, startPosition);
}
public void initializePlayer(String mediaUri) {
if (mExoPlayer == null) {
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
mExoPlayer = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector);
mPlayerView.setPlayer(mExoPlayer);
mediaDataSourceFactory = new DefaultDataSourceFactory(getContext(),
Util.getUserAgent(getContext(), "BakingX"),
(TransferListener<? super DataSource>) bandwidthMeter);
mediaSource = new ExtractorMediaSource
.Factory(mediaDataSourceFactory)
.createMediaSource(Uri.parse(mediaUri));
//my prolem I can not call seekTo
if (startPosition!=null) {
mExoPlayer.seekTo(startWindow,startPosition);
}
mExoPlayer.prepare(mediaSource);
mExoPlayer.setPlayWhenReady(playWhenReady);
}
}
private void releasePlayer() {
if (mExoPlayer!= null) {
startPosition = mExoPlayer.getCurrentPosition();
startWindow = mExoPlayer.getCurrentWindowIndex();
playWhenReady = mExoPlayer.getPlayWhenReady();
mExoPlayer.release();
mExoPlayer = null;
}
}
private void updateStartPosition() {
if (mExoPlayer != null) {
startAutoPlay = mExoPlayer.getPlayWhenReady();
startWindow = mExoPlayer.getCurrentWindowIndex();
startPosition = Math.max(0, mExoPlayer.getContentPosition());
}
}
#Override
public void onStart() {
super.onStart();
if (Util.SDK_INT > 23) {
initializePlayer(videoUrl);
}
}
#Override
public void onResume() {
super.onResume();
if (Util.SDK_INT <= 23 || mExoPlayer == null) {
initializePlayer(videoUrl);
}
}
#Override
public void onPause() {
super.onPause();
updateStartPosition();
if (Util.SDK_INT <= 23) {
releasePlayer();
}
}
#Override
public void onStop() {
super.onStop();
// releasePlayer();
if (Util.SDK_INT > 23) {
releasePlayer();
}
}
#Override
public void onDestroy() {
super.onDestroy();
releasePlayer();
}
}
my question exactly how I call seekTo() after mobile rotation
A couple of issues:
1. Remove playWhenReady and update everywhere it to startAutoPlay.
2. Move the code from onViewRestored to onCreateView.
3. Update the position after setting the media source.
public RecipeStepsVideoPlayerFragment() {
// Required empty public constructor
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_recipe_steps_video_player, container, false);
ButterKnife.bind(this, view);
Bundle bundle = getArguments();
if (bundle != null) {
videoUrl = bundle.getString(VIDEO_URL_KEY);
}
if (savedInstanceState != null) {
startAutoPlay = savedInstanceState.getBoolean(KEY_AUTO_PLAY);
startWindow = savedInstanceState.getInt(KEY_WINDOW);
startPosition = savedInstanceState.getLong(KEY_POSITION);
Toast.makeText(getContext(), startPosition.toString(), Toast.LENGTH_LONG).show();
}
initializePlayer(videoUrl);
stepDescription.setText(bundle.getString(STEP_DESCRIPTION_KEY));
return view;
}
#Override
public void onViewStateRestored(#Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
}
#Override
public void onSaveInstanceState(Bundle outState) {
releasePlayer();
outState.putBoolean(KEY_AUTO_PLAY, startAutoPlay);
outState.putInt(KEY_WINDOW, startWindow);
outState.putLong(KEY_POSITION, startPosition);
}
public void initializePlayer(String mediaUri) {
if (mExoPlayer == null) {
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
mExoPlayer = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector);
mPlayerView.setPlayer(mExoPlayer);
mediaDataSourceFactory = new DefaultDataSourceFactory(getContext(),
Util.getUserAgent(getContext(), "BakingX"),
(TransferListener<? super DataSource>) bandwidthMeter);
mediaSource = new ExtractorMediaSource
.Factory(mediaDataSourceFactory)
.createMediaSource(Uri.parse(mediaUri));
mExoPlayer.prepare(mediaSource);
if (startPosition != null) {
mExoPlayer.seekTo(startWindow, startPosition);
}
mExoPlayer.setPlayWhenReady(startAutoPlay);
}
}
private void releasePlayer() {
if (mExoPlayer != null) {
startPosition = mExoPlayer.getCurrentPosition();
startWindow = mExoPlayer.getCurrentWindowIndex();
startAutoPlay = mExoPlayer.getPlayWhenReady();
mExoPlayer.release();
mExoPlayer = null;
}
}
private void updateStartPosition() {
if (mExoPlayer != null) {
startAutoPlay = mExoPlayer.getPlayWhenReady();
startWindow = mExoPlayer.getCurrentWindowIndex();
startPosition = Math.max(0, mExoPlayer.getContentPosition());
}
}
#Override
public void onStart() {
super.onStart();
if (Util.SDK_INT > 23) {
initializePlayer(videoUrl);
}
}
#Override
public void onResume() {
super.onResume();
if (Util.SDK_INT <= 23 || mExoPlayer == null) {
initializePlayer(videoUrl);
}
}
#Override
public void onPause() {
super.onPause();
updateStartPosition();
if (Util.SDK_INT <= 23) {
releasePlayer();
}
}
#Override
public void onStop() {
super.onStop();
// releasePlayer();
if (Util.SDK_INT > 23) {
releasePlayer();
}
}
#Override
public void onDestroy() {
super.onDestroy();
releasePlayer();
}
I have created an ExoPlayer inside a fragment. If during the playback I press the Home button, and get back to the player, the player seems to be empty, the play/pause button does not work anymore. The video is gone. The video player view seems to be completely empty.
public class PlaybackFragment extends Fragment implements View.OnClickListener, EventListener, PlaybackControlView.VisibilityListener {
private static final String TAG = PlaybackFragment.class.getSimpleName();
Video mVideo; //Video object passed in for playback
SimpleExoPlayerView mExoPlayerView;
private TextView mVideoTitle;
private SimpleExoPlayer mExoPlayer;
private FrameLayout mFullScreenButton; //Holds the fullscreen icon
private Dialog mFullScreenDialog;
private ImageView mFullScreenIcon;
private boolean mExoPlayerFullScreen; //full-screen dialog
private static final String LOG_TAG = PlaybackFragment.class.getSimpleName();
/**
* Root ViewGroup holding the fragment
*/
private ViewGroup mPlayerViewContainer;
/**
* Context used across the class
*/
private Context mContext;
/**
* Activity to which the fragment is added
*/
private Activity mActivity;
/**
* Uri received from calling activity containing Uri to the video to be played
*/
private Uri mUri;
public PlaybackFragment() {
super();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreate: ");
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreateView: ");
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_playback, container, false);
mVideoTitle = root.findViewById(R.id.playback_video_title);
mExoPlayerView = root.findViewById(R.id.exoPlayerView);
mPlayerViewContainer = (ViewGroup) mExoPlayerView.getParent();
return root;
}
/**
* Sets the Uri to video to be played
* #param mediaUri
*/
public void playUri(Uri mediaUri) {
mUri = mediaUri;
}
#Override
public void onViewStateRestored(#Nullable Bundle savedInstanceState) {
Log.d(TAG, "onViewStateRestored: ");
super.onViewStateRestored(savedInstanceState);
}
private void preparePlayback() {
mExoPlayerView.setControllerVisibilityListener(this);
mExoPlayerView.requestFocus();
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
new DefaultTrackSelector(trackSelectionFactory);
mExoPlayer =
ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector);
mExoPlayerView.setPlayer(mExoPlayer);
DataSource.Factory dsFactory = new DefaultDataSourceFactory(mContext,
com.google.android.exoplayer2.util.Util.getUserAgent(mContext, mActivity.getApplicationInfo().processName), null);
// Produces Extractor instances for parsing the media data.
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
MediaSource videoSource = new ExtractorMediaSource(mUri,
dsFactory, extractorsFactory, null, null);
// Prepare the player with the source.
mExoPlayer.prepare(videoSource);
//start player
mExoPlayer.setPlayWhenReady(true);
mExoPlayer.addListener(this);
// initFullscreenDialog();
initFullscreenButton();
}
#Override
public void onVisibilityChange(int visibility) {
}
#Override
public void onTimelineChanged(Timeline timeline, Object manifest) {
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
Log.d(TAG, "onActivityCreated: ");
super.onActivityCreated(savedInstanceState);
mContext = mActivity = getActivity();
preparePlayback();
}
#Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart: ");
}
#Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
}
#Override
public void onLoadingChanged(boolean isLoading) {
}
#Override
public void onClick(View v) {
}
public void pause() {
mExoPlayer.stop();
}
//Interface to trigger playback-finish event in the parent activity
public interface OnPlayStateListener {
void onPlayFinish();
}
OnPlayStateListener mCallback;
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if(playbackState == Player.STATE_ENDED) {
quitFullScreen(); //Leave fullscreen after playback finished
mCallback.onPlayFinish();
Log.i(LOG_TAG,"onPlayFinish() triggered");
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mCallback = (OnPlayStateListener) context;
} catch (ClassCastException e){
throw new ClassCastException(context.toString() + " must implement OnPlayStateListener");
}
}
#Override
public void onRepeatModeChanged(int repeatMode) {
}
#Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
}
#Override
public void onPlayerError(ExoPlaybackException error) {
Toast.makeText(mContext,error.getLocalizedMessage(),Toast.LENGTH_SHORT).show();
}
#Override
public void onPositionDiscontinuity(int reason) {
}
#Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
}
#Override
public void onSeekProcessed() {
}
private void goFullScreen() {
Util.showFullScreen(mActivity, mExoPlayerView);
// ((ViewGroup) mExoPlayerView.getParent()).removeView(mExoPlayerView);
// mFullScreenDialog.addContentView(mExoPlayerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
mFullScreenIcon.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_fullscreen_exit));
mExoPlayerFullScreen = true;
// mFullScreenDialog.show();
}
private void quitFullScreen() {
Util.quitFullScreen(mActivity, mPlayerViewContainer, mExoPlayerView);
// ((ViewGroup) mExoPlayerView.getParent()).removeView(mExoPlayerView);
// FrameLayout frame = mActivity.findViewById(R.id.main_media_frame);
// frame.addView(mExoPlayerView);
mExoPlayerFullScreen = false;
// mFullScreenDialog.dismiss();
mFullScreenIcon.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_fullscreen_expand));
}
private void initFullscreenButton() {
PlaybackControlView controlView = mExoPlayerView.findViewById(R.id.exo_controller);
mFullScreenIcon = controlView.findViewById(R.id.exo_fullscreen_icon);
mFullScreenButton = controlView.findViewById(R.id.exo_fullscreen_button);
mFullScreenButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!mExoPlayerFullScreen) {
goFullScreen();
}
else {
quitFullScreen();
}
}
});
}
private void initFullscreenDialog() {
//The following uses anonymous class to create a custom dialog
mFullScreenDialog = new Dialog(mContext, android.R.style.Theme_DeviceDefault_NoActionBar_Fullscreen) {
#Override
public void onBackPressed() {
if(mExoPlayerFullScreen) {
quitFullScreen();
}
super.onBackPressed();
}
};
}
#Override
public void onDestroy() {
super.onDestroy();
if(mExoPlayer != null) mExoPlayer.release();
Log.d(TAG, "onDestroy: ");
}
/**
* Moves forward or backward by step milliseconds <br/>
* #param step specifies step size. It is a positive or negative integer for moving forward and
* backward, respectively.
*/
public void movePlayPos(int step) {
mExoPlayer.seekTo(mExoPlayer.getCurrentPosition() + step);
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
Log.d(TAG, "onSaveInstanceState: ");
super.onSaveInstanceState(outState);
outState.putLong("position", mExoPlayer.getCurrentPosition());
}
}
I have checked the fragment lifecycle in Logcat, but it seems that after I get back to the player activity, only the fragment's onStart() is called. So, there is no way to restore the state. Any ideas how I can save a video playback position upon pause and resume playback on the activity's onResume?
You can save the current position of exoplayer in onPause() and then seek to that position in onResume().
try this:
long position;
#Override
protected void onPause() {
super.onPause();
if(mExoPlayerView != null && mExoPlayerView.getPlayWhenReady()) {
position = mExoPlayerView.getCurrentPosition();
mExoPlayerView.setPlayWhenReady(false);
}
}
#Override
protected void onResume() {
super.onResume();
if(mExoPlayerView != null) {
mExoPlayerView.seekTo(position);
mExoPlayerView.setPlayWhenReady(true);
}
}