Reputation: 132
I am working on the Baking App of Udacity Nanodegree. I have this code of an ExoPlayer fragment. I want to resume playback from the same position where it was if the device is rotated. I know that the activity will not be recreated because I added android:configChanges="orientation|keyboardHidden"
to my manifest. Now I'm not sure as to how the fragment would react to changes in orientation. Currently the video plays from the beginning upon rotation, and keeps on playing even if I hit the back key.
How do I: 1.Play the video from the same position 2.Stop the video from playing if I hit the back key?
package com.example.android.bakingapp;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* A simple {@link Fragment} subclass.
* Activities that contain this fragment must implement the
* {@link ExoPlayerFragment.OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {@link ExoPlayerFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class ExoPlayerFragment extends Fragment {
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
long currentPosition;
Bundle bundle;
SimpleExoPlayer player;
private String mParam1;
private String mParam2;
private PlayerView mPlayerView;
private OnFragmentInteractionListener mListener;
public ExoPlayerFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment ExoPlayerFragment.
*/
public static ExoPlayerFragment newInstance(String param1, String param2) {
ExoPlayerFragment fragment = new ExoPlayerFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
if (savedInstanceState != null) {
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
DefaultTrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector);
player.seekTo(savedInstanceState.getLong("currentPosition"));
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
currentPosition = player.getCurrentPosition();
outState.putLong("currentPosition", currentPosition);
Log.d("currentPosition", "onSaveInstanceState: " + currentPosition);
}
@Override
public void onPause() {
super.onPause();
player.stop();
if (Util.SDK_INT <= 23) {
player.release();
player = null;
}
}
@Override
public void onStop() {
super.onStop();
if (Util.SDK_INT > 23) {
player.release();
player = null;
}
}
@Override
public void onStart() {
super.onStart();
if (Util.SDK_INT > 23) {
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
DefaultTrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector);
}
}
@Override
public void onResume() {
super.onResume();
if ((Util.SDK_INT <= 23 || player == null)) {
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
DefaultTrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_exo_player, container, false);
mPlayerView = view.findViewById(R.id.video_view);
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
DefaultTrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector);
mPlayerView.setPlayer(player);
DataSource.Factory mediaDataSourceFactory = new DefaultDataSourceFactory(getContext(),
Util.getUserAgent(getContext(),
"BakingApp"),
bandwidthMeter);
String id = getArguments().getString("id");
String url = "", videoURL = "";
String shortDescriptionParameter = getArguments().getString("item");
try {
JSONArray jsonArray = new JSONArray(RecipeJson.jsonData);
JSONObject jsonObject = jsonArray.getJSONObject(Integer.parseInt(id));
JSONArray jsonArray1 = jsonObject.getJSONArray("steps");
int index1;
for (index1 = 0; index1 < jsonArray1.length(); index1++) {
JSONObject jsonObject1 = jsonArray1.getJSONObject(index1);
if (jsonObject1.getString("shortDescription").equals(shortDescriptionParameter)) {
videoURL = jsonObject1.getString("videoURL");
if (videoURL.equals("")) {
mPlayerView.setVisibility(View.GONE);
Toast.makeText(getContext(), "NO VIDEO", Toast.LENGTH_SHORT).show();
} else
url = videoURL;
break;
}
}
} catch (JSONException e) {
e.printStackTrace();
}
if (!url.equals("")) {
MediaSource mediaSource = new ExtractorMediaSource.Factory(mediaDataSourceFactory).createMediaSource(Uri.parse(url));
player.prepare(mediaSource);
player.setPlayWhenReady(true);
}
onButtonPressed(shortDescriptionParameter);
return view;
}
public void onButtonPressed(String shortDescription) {
if (mListener != null) {
mListener.onFragmentInteraction(shortDescription);
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnChangeStepFragmentInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
void onFragmentInteraction(String shortDescription);
}
}
Upvotes: 1
Views: 2188
Reputation: 1074
It is in fact very easy.
Approach 1:
Create your Instance of ExoPlayer
in onCreate
of your fragment and let the fragment to retain its instance. This way you will keep the player alive while device orientation changes. In onCreateView
attach the view to your player and everything is set.
Approach 2:
create your ExoPlayer in on onCreateView. For this to work properly, you will need to save the currentWindowIndex
and position
of player in onSaveInstanceState
of your fragment. Then you will have to change your code to player.seekTo(currentWindowIndex, position)
right before setting playWhenReady = true
with currentWindowIndex and position restored from the savedInstanceState Bundle. You should note that in this case there will be a discontinuity in the playback since player gets recreated. (so it is not recommended)
Upvotes: 2