Divyanshu Varma
Divyanshu Varma

Reputation: 132

Resume playback from the same position upon rotation in ExoPlayer

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

Answers (1)

Masoud Dadashi
Masoud Dadashi

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 = truewith 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

Related Questions