Tag

exoplayer

Browsing

In this article, I will tell you how to play youtube video in ExoPlayer in Android. In previous tutorials, We were learned video streaming from the server using ExoPlayer. If you wish to read the previous article Go Here. I assuming you have basic understanding of ExoPlayer.

How to play Youtube video inside the ExoPlayer in Android. I will demonstrate step by step

1. Let’s create a new project in Android Studio.

Add dependency in Project build.gradle
allprojects {
    repositories {
        google()
        jcenter()
        /**
         * Add dependency in Project build.gradle
         */
        maven { url "https://jitpack.io" }
    }
}
Add dependency in app build.gradle
   // For youtube Url Extractor
    implementation 'com.github.HaarigerHarald:android-youtubeExtractor:v1.7.0'
    // ExoPlayer
    implementation 'com.google.android.exoplayer:exoplayer:2.7.3'

2. Prepare ExoPlayerManager Singleton

In my previous tutorials wrote ExoPlayer instance inside each activity (not a good practice). In case if we need more than one ExoPlayerView in the app, we have to write code in each activity, as per programming standard it’s not a good practice.

Therefore, I’m going to write ExoPlayerManager utility class. In other words, I creating a singleton class for managing ExoPlayer instance.

2.1 Create a CallBacks for observing ExoPlayer state
package com.androidwave.youtubeexoplayer;

public interface CallBacks {

    void callbackObserver(Object obj);

    public interface playerCallBack {
        void onItemClickOnItem(Integer albumId);

        void onPlayingEnd();
    }
}
3. Furthermore, create a singleton class with ExoPlayerManager.java name

As per singleton design pattern constructor should be private

 /**
     * declare some usable variable
     */
    private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
    private static final String TAG = "ExoPlayerManager";
    private static ExoPlayerManager mInstance = null;
    PlayerView mPlayerView;
    DefaultDataSourceFactory dataSourceFactory;
    String uriString = "";
    ArrayList<String> mPlayList = null;
    Integer playlistIndex = 0;
    CallBacks.playerCallBack listner;
    private SimpleExoPlayer mPlayer;

 /**
     * Return ExoPlayerManager instance
     * @param mContext
     * @return
     */
    public static ExoPlayerManager getSharedInstance(Context mContext) {
        if (mInstance == null) {
            mInstance = new ExoPlayerManager(mContext);
        }
        return mInstance;
    }
 /**
     * private constructor
     * @param mContext
     */
    private ExoPlayerManager(Context mContext) {

        TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
        TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
        mPlayer = ExoPlayerFactory.newSimpleInstance(mContext, trackSelector);

        mPlayerView = new PlayerView(mContext);
        mPlayerView.setUseController(true);
        mPlayerView.requestFocus();
        mPlayerView.setPlayer(mPlayer);

        Uri mp4VideoUri = Uri.parse(uriString);

        dataSourceFactory = new DefaultDataSourceFactory(mContext, Util.getUserAgent(mContext, "androidwave"), BANDWIDTH_METER);

        final MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
                .createMediaSource(mp4VideoUri);

        mPlayer.prepare(videoSource);
        mPlayer.addListener(new Player.EventListener() {
            @Override
            public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
                Log.i(TAG, "onTimelineChanged: ");
            }

            @Override
            public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
                Log.i(TAG, "onTracksChanged: ");
            }

            @Override
            public void onLoadingChanged(boolean isLoading) {
                Log.i(TAG, "onLoadingChanged: ");
            }

            @Override
            public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
                Log.i(TAG, "onPlayerStateChanged: ");
                if (playbackState == 4 && mPlayList != null && playlistIndex + 1 < mPlayList.size()) {
                    Log.e(TAG, "Song Changed...");

                    playlistIndex++;
                    listner.onItemClickOnItem(playlistIndex);
                    playStream(mPlayList.get(playlistIndex));
                } else if (playbackState == 4 && mPlayList != null && playlistIndex + 1 == mPlayList.size()) {
                    mPlayer.setPlayWhenReady(false);
                }
                if (playbackState == 4 && listner != null) {
                    listner.onPlayingEnd();
                }
            }

            @Override
            public void onRepeatModeChanged(int repeatMode) {
                Log.i(TAG, "onRepeatModeChanged: ");
            }

            @Override
            public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
                Log.i(TAG, "onShuffleModeEnabledChanged: ");
            }

            @Override
            public void onPlayerError(ExoPlaybackException error) {
                Log.i(TAG, "onPlayerError: ");
            }

            @Override
            public void onPositionDiscontinuity(int reason) {
                Log.i(TAG, "onPositionDiscontinuity: ");
            }

            @Override
            public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
                Log.i(TAG, "onPlaybackParametersChanged: ");
            }

            @Override
            public void onSeekProcessed() {
                Log.i(TAG, "onSeekProcessed: ");
            }
        });
    }

After define some method complete code will seem like below class

package com.androidwave.youtubeexoplayer;

import android.content.Context;
import android.net.Uri;
import android.util.Log;

import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
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.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;

public class ExoPlayerManager {

    /**
     * declare some usable variable
     */
    private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
    private static final String TAG = "ExoPlayerManager";
    private static ExoPlayerManager mInstance = null;
    PlayerView mPlayerView;
    DefaultDataSourceFactory dataSourceFactory;
    String uriString = "";
    ArrayList<String> mPlayList = null;
    Integer playlistIndex = 0;
    CallBacks.playerCallBack listner;
    private SimpleExoPlayer mPlayer;

    /**
     * private constructor
     * @param mContext
     */
    private ExoPlayerManager(Context mContext) {

        TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
        TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
        mPlayer = ExoPlayerFactory.newSimpleInstance(mContext, trackSelector);

        mPlayerView = new PlayerView(mContext);
        mPlayerView.setUseController(true);
        mPlayerView.requestFocus();
        mPlayerView.setPlayer(mPlayer);

        Uri mp4VideoUri = Uri.parse(uriString);

        dataSourceFactory = new DefaultDataSourceFactory(mContext, Util.getUserAgent(mContext, "androidwave"), BANDWIDTH_METER);

        final MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
                .createMediaSource(mp4VideoUri);

        mPlayer.prepare(videoSource);
        mPlayer.addListener(new Player.EventListener() {
            @Override
            public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
                Log.i(TAG, "onTimelineChanged: ");
            }

            @Override
            public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
                Log.i(TAG, "onTracksChanged: ");
            }

            @Override
            public void onLoadingChanged(boolean isLoading) {
                Log.i(TAG, "onLoadingChanged: ");
            }

            @Override
            public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
                Log.i(TAG, "onPlayerStateChanged: ");
                if (playbackState == 4 && mPlayList != null && playlistIndex + 1 < mPlayList.size()) {
                    Log.e(TAG, "Song Changed...");

                    playlistIndex++;
                    listner.onItemClickOnItem(playlistIndex);
                    playStream(mPlayList.get(playlistIndex));
                } else if (playbackState == 4 && mPlayList != null && playlistIndex + 1 == mPlayList.size()) {
                    mPlayer.setPlayWhenReady(false);
                }
                if (playbackState == 4 && listner != null) {
                    listner.onPlayingEnd();
                }
            }

            @Override
            public void onRepeatModeChanged(int repeatMode) {
                Log.i(TAG, "onRepeatModeChanged: ");
            }

            @Override
            public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
                Log.i(TAG, "onShuffleModeEnabledChanged: ");
            }

            @Override
            public void onPlayerError(ExoPlaybackException error) {
                Log.i(TAG, "onPlayerError: ");
            }

            @Override
            public void onPositionDiscontinuity(int reason) {
                Log.i(TAG, "onPositionDiscontinuity: ");
            }

            @Override
            public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
                Log.i(TAG, "onPlaybackParametersChanged: ");
            }

            @Override
            public void onSeekProcessed() {
                Log.i(TAG, "onSeekProcessed: ");
            }
        });
    }

    /**
     * Return ExoPlayerManager instance
     * @param mContext
     * @return
     */
    public static ExoPlayerManager getSharedInstance(Context mContext) {
        if (mInstance == null) {
            mInstance = new ExoPlayerManager(mContext);
        }
        return mInstance;
    }


    public void setPlayerListener(CallBacks.playerCallBack mPlayerCallBack) {
        listner = mPlayerCallBack;
    }

    public PlayerView getPlayerView() {
        return mPlayerView;
    }

    public void playStream(String urlToPlay) {
        uriString = urlToPlay;
        Uri mp4VideoUri = Uri.parse(uriString);
        MediaSource videoSource;
        String filenameArray[] = urlToPlay.split("\\.");
        if (uriString.toUpperCase().contains("M3U8")) {
            videoSource = new HlsMediaSource(mp4VideoUri, dataSourceFactory, null, null);
        } else {
            mp4VideoUri = Uri.parse(urlToPlay);
            videoSource = new ExtractorMediaSource(mp4VideoUri, dataSourceFactory, new DefaultExtractorsFactory(),
                    null, null);
        }


        // Prepare the player with the source.
        mPlayer.prepare(videoSource);
        mPlayer.setPlayWhenReady(true);

    }

    public void setPlayerVolume(float vol) {
        mPlayer.setVolume(vol);
    }

    public void setUriString(String uri) {
        uriString = uri;
    }

    public void setPlaylist(ArrayList<String> uriArrayList, Integer index, CallBacks.playerCallBack callBack) {
        mPlayList = uriArrayList;
        playlistIndex = index;
        listner = callBack;
        playStream(mPlayList.get(playlistIndex));
    }


    public void playerPlaySwitch() {
        if (uriString != "") {
            mPlayer.setPlayWhenReady(!mPlayer.getPlayWhenReady());
        }
    }

    public void stopPlayer(boolean state) {
        mPlayer.setPlayWhenReady(!state);
    }

    public void destroyPlayer() {
        mPlayer.stop();
    }

    public Boolean isPlayerPlaying() {
        return mPlayer.getPlayWhenReady();
    }

    public ArrayList<String> readURLs(String url) {
        if (url == null) return null;
        ArrayList<String> allURls = new ArrayList<String>();
        try {

            URL urls = new URL(url);
            BufferedReader in = new BufferedReader(new InputStreamReader(urls
                    .openStream()));
            String str;
            while ((str = in.readLine()) != null) {
                allURls.add(str);
            }
            in.close();
            return allURls;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}

4. How to use ExoPlayerManager inside App?

Now we prepared ExoPlayer Manager, You are thinking about how will I use this manager in our app. don’t worry I’m here will explain with an example.

4.1 Let’s create a activity.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.exoplayer2.ui.PlayerView
        android:id="@+id/mPlayerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#A6000000"
        app:controller_layout_id="@layout/exo_playback_control_view"
        app:player_layout_id="@layout/exo_simple_player_view"
        app:repeat_toggle_modes="none"
        app:show_timeout="45000"
        app:resize_mode="fixed_height"
        app:surface_type="texture_view" />

</android.support.constraint.ConstraintLayout>
4.2Create a exo_playback_control_view.xml for ExoPlayer playback controller
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="44dp"
    android:layout_gravity="bottom"
    android:background="#A6000000"
    android:layoutDirection="ltr"
    android:orientation="vertical">
    <! –    android:background="#11000000"-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin">

        <ImageButton
            android:id="@id/exo_play"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent"
            app:srcCompat="@android:drawable/ic_media_play" />

        <ImageButton
            android:id="@id/exo_pause"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent"
            app:srcCompat="@android:drawable/ic_media_pause" />

        <TextView
            android:id="@id/exo_position"
            android:layout_width="45dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/activity_horizontal_margin"
            android:gravity="center"
            android:includeFontPadding="false"
            android:letterSpacing="-0.02"
            android:textColor="#ffffff"
            android:textSize="12sp"
            tools:text="23:09" />


        <com.google.android.exoplayer2.ui.DefaultTimeBar
            android:id="@id/exo_progress"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="8dp"
            app:bar_height="8dp"
            app:buffered_color="#4Dd8d8d8"
            app:played_color="@color/white"
            app:scrubber_color="@color/white"
            app:unplayed_color="#4Dd8d8d8" />

    </LinearLayout>


</LinearLayout>

5. Let’s extract video url from Youtube YouTubeExtractor

   private void extractYoutubeUrl() {
        @SuppressLint("StaticFieldLeak") YouTubeExtractor mExtractor = new YouTubeExtractor(this) {
            @Override
            protected void onExtractionComplete(SparseArray<YtFile> sparseArray, VideoMeta videoMeta) {
                if (sparseArray != null) {
                    playVideo(sparseArray.get(17).getUrl());
                }
            }
        };
        mExtractor.extract(mYoutubeLink, true, true);
    }

6. After that, set url with ExoPlayer for streaming

  private void playVideo(String downloadUrl) {
        PlayerView mPlayerView = findViewById(R.id.mPlayerView);
        mPlayerView.setPlayer(ExoPlayerManager.getSharedInstance(MainActivity.this).getPlayerView().getPlayer());
        ExoPlayerManager.getSharedInstance(MainActivity.this).playStream(downloadUrl);
    }

7. Finally, MainActivity.java code is

package com.androidwave.youtubeexoplayer;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.SparseArray;

import com.google.android.exoplayer2.ui.PlayerView;

import at.huber.youtubeExtractor.VideoMeta;
import at.huber.youtubeExtractor.YouTubeExtractor;
import at.huber.youtubeExtractor.YtFile;

public class MainActivity extends AppCompatActivity {
    // Replace video id with your video Id
    private String YOUTUBE_VIDEO_ID = "uZnWUZW1hQo";
    private String BASE_URL = "https://www.youtube.com";
    private String mYoutubeLink = BASE_URL + "/watch?v=" + YOUTUBE_VIDEO_ID;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        extractYoutubeUrl();
    }

    private void extractYoutubeUrl() {
        @SuppressLint("StaticFieldLeak") YouTubeExtractor mExtractor = new YouTubeExtractor(this) {
            @Override
            protected void onExtractionComplete(SparseArray<YtFile> sparseArray, VideoMeta videoMeta) {
                if (sparseArray != null) {
                    playVideo(sparseArray.get(17).getUrl());
                }
            }
        };
        mExtractor.extract(mYoutubeLink, true, true);
    }

    private void playVideo(String downloadUrl) {
        PlayerView mPlayerView = findViewById(R.id.mPlayerView);
        mPlayerView.setPlayer(ExoPlayerManager.getSharedInstance(MainActivity.this).getPlayerView().getPlayer());
        ExoPlayerManager.getSharedInstance(MainActivity.this).playStream(downloadUrl);
    }

}

Conclusion

Now you are done, your project is ready to run, that way you can easily play youtube video in ExoPlayer

Download Project- Play Youtube Video in ExoPlayer

We demonstrate video streaming from the server using the ExoPlayer. In fact, youtube player also used ExoPlayer for streaming video. This ExoPlayer android example I will explain how to use ExoPlayer in own android application.

ExoPlayer is an open-source project. This is not a part of Android SDK. In Android, ExoPlayer is application level media player. It’s standard audio and video components are built on Android’s MediaCodec API, which was released in Android.

ExoPlayer is easy to use, maintainable and fully customize.

ExoPlayer Android Tutorial Sample App

1. Create a new project.

Go to File Menu and Create a New Project, fill information like project and package name after that click Next and select target SDK and Finish

2. After that add below dependency in build.gradle.

    // add exo player dependency here
    implementation 'com.google.android.exoplayer:exoplayer:2.7.3'
    implementation 'org.jsoup:jsoup:1.10.3'

    // code generator for view
    implementation "com.jakewharton:butterknife:8.8.1"
    annotationProcessor "com.jakewharton:butterknife-compiler:8.8.1"

In the gradle.xml file, We have 2 major dependencies. One for ExoPlayer and second as know about ButterKnife already. Basically, the butterknife is injected of view in the java file.

3. Open activity_main.xml and add two Button like below code.

While creating project Android Studio is provide default template like EmptyActivity. So MainActivity.java and activity_main.xml is automatically created. So open activity_main.xml and add to button

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_design"
    tools:context=".MainActivity"
    >

  <Button
      android:id="@+id/buttonPlayUrlVideo"
      android:layout_width="200dp"
      android:layout_height="wrap_content"
      android:layout_marginStart="8dp"
      android:layout_marginTop="16dp"
      android:layout_marginEnd="8dp"
      android:layout_marginBottom="8dp"
      android:background="@color/colorAccent"
      android:text="@string/title_play_url"
      android:textColor="@color/white"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintHorizontal_bias="0.5"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/buttonPlayDefaultVideo"
      />

  <Button
      android:id="@+id/buttonPlayDefaultVideo"
      android:layout_width="200dp"
      android:layout_height="wrap_content"
      android:layout_marginStart="8dp"
      android:layout_marginTop="8dp"
      android:layout_marginEnd="8dp"
      android:background="@color/colorAccent"
      android:text="@string/title_play_default_url"
      android:textColor="@color/white"
      app:layout_constraintBottom_toTopOf="@+id/buttonPlayUrlVideo"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintHorizontal_bias="0.5"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintVertical_chainStyle="packed"
      />
</android.support.constraint.ConstraintLayout>

As per design, we have created two buttons one for play default video. The second user can put own URL in dialog prompt.

4.1 Now connect this layout with MainActivity.java

Open the main activity, we generate view inject code by using ButterKnief injection and set on click listeners.

  @BindView(R.id.buttonPlayUrlVideo)
  Button buttonPlayUrlVideo;
  @BindView(R.id.buttonPlayDefaultVideo)
  Button buttonPlayDefaultVideo;

  @OnClick({ R.id.buttonPlayUrlVideo, R.id.buttonPlayDefaultVideo })
  public void onViewClicked(View view) {
    switch (view.getId()) {
      case R.id.buttonPlayUrlVideo:
        showDialogPrompt();
        break;
      case R.id.buttonPlayDefaultVideo:
        Intent mIntent =
            ExoPlayerActivity.getStartIntent(this, VideoPlayerConfig.DEFAULT_VIDEO_URL);
        startActivity(mIntent);
        break;
    }
  }
4.2. Let’s bind butter knife in onCreate Activity
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    getSupportActionBar().hide();
    ButterKnife.bind(this);
  }

4.3. -As per above, We are using two buttons. So If you want to play a default video then we call intent with default URL of ExoPlayer activity.
like Below

  // End Activity Here
  Intent mIntent = ExoPlayerActivity.getStartIntent(this, VideoPlayerConfig.DEFAULT_VIDEO_URL);

  startActivity(mIntent);


4.3. – Button for playing its own video by entering video URL. So taking user URL we have created alert dialog with edit text.

Just create a layout file for dialog view. In res, folder create file named dialog_prompts.xml and add few components

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="@dimen/default_item_padding"
    >

  <TextView
      android:id="@+id/textView1"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginStart="8dp"
      android:layout_marginTop="8dp"
      android:layout_marginEnd="8dp"
      android:text="Type Your Url"
      android:textAppearance="?android:attr/textAppearanceLarge"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      />

  <EditText
      android:id="@+id/editTextDialogUrlInput"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginStart="8dp"
      android:layout_marginTop="8dp"
      android:layout_marginEnd="8dp"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/textView1"
      >

    <requestFocus />

  </EditText>

</android.support.constraint.ConstraintLayout>

Open again MainActivity.java and put this code for creating dialog

  private void showDialogPrompt() {
    // get dialog_prompts.xml view
    LayoutInflater li = LayoutInflater.from(this);
    View promptsView = li.inflate(R.layout.dialog_prompts, null);
    AlertDialog.Builder mBuilder = new AlertDialog.Builder( this);
    // set dialog_prompts.xml to dialog
    mBuilder.setView(promptsView);
    final EditText userInputURL = (EditText) promptsView
        .findViewById(R.id.editTextDialogUrlInput);
    // set dialog message here
    mBuilder.setCancelable(false)
        .setPositiveButton("OK",
            new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog, int id) {
                boolean isURL = Patterns.WEB_URL.matcher(userInputURL.getText().toString().trim()).matches();
                if (isURL) {
                  Intent mIntent = ExoPlayerActivity.getStartIntent(MainActivity.this, userInputURL.getText().toString().trim());
                  startActivity(mIntent);
                } else {
                  Toast.makeText(MainActivity.this, getString(R.string.error_message_url_not_valid), Toast.LENGTH_SHORT).show();
                }
              }
            })
        .setNegativeButton("Cancel",
            new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
              }
            }).create().show();
  }

5. Now MainActivity.java full code seems like below

package com.androidwave.exoplayer;

import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

  public class MainActivity extends AppCompatActivity {

    @BindView(R.id.buttonPlayUrlVideo)
    Button buttonPlayUrlVideo;
    @BindView(R.id.buttonPlayDefaultVideo)
    Button buttonPlayDefaultVideo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      getSupportActionBar().hide();
      ButterKnife.bind(this);
    }

    @OnClick({ R.id.buttonPlayUrlVideo, R.id.buttonPlayDefaultVideo })
    public void onViewClicked(View view) {
      switch (view.getId()) {
        case R.id.buttonPlayUrlVideo:
          showDialogPrompt();
          break;
        case R.id.buttonPlayDefaultVideo:
          Intent mIntent =
              ExoPlayerActivity.getStartIntent(this, VideoPlayerConfig.DEFAULT_VIDEO_URL);
          startActivity(mIntent);
          break;
      }
    }

    private void showDialogPrompt() {
      // get dialog_prompts.xml view
      LayoutInflater li = LayoutInflater.from(this);
      View promptsView = li.inflate(R.layout.dialog_prompts, null);
      AlertDialog.Builder mBuilder = new AlertDialog.Builder(this);
      // set dialog_prompts.xml to dialog
      mBuilder.setView(promptsView);
      final EditText userInputURL = (EditText) promptsView
          .findViewById(R.id.editTextDialogUrlInput);
      // set dialog message here
      mBuilder.setCancelable(false)
          .setPositiveButton("OK",
              new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                  boolean isURL =
                      Patterns.WEB_URL.matcher(userInputURL.getText().toString().trim()).matches();
                  if (isURL) {
                    Intent mIntent = ExoPlayerActivity.getStartIntent(MainActivity.this,
                        userInputURL.getText().toString().trim());
                    startActivity(mIntent);
                  } else {
                    Toast.makeText(MainActivity.this,
                        getString(R.string.error_message_url_not_valid), Toast.LENGTH_SHORT).show();
                  }
                }
              })
          .setNegativeButton("Cancel",
              new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                  dialog.cancel();
                }
              }).create().show();
    }
  }

6. Configure ExoPlayer

Before going to Exo Player activity we define followings configuration of ExoPlayer.
MIN_BUFFER_DURATION – Minimum Video you want to buffer while Playing. for now, I have set 3 second
MAX_BUFFER_DURATION – You can set Max Video you want to buffer during PlayBack.
MIN_PLAYBACK_START_BUFFER – Set Min Video you want to buffer before start Playing it
MIN_PLAYBACK_RESUME_BUFFER – Set Min video You want to buffer when the user resumes video
DEFAULT_VIDEO_URL – Set Default video url for demo

package com.androidwave.exoplayer;

  public class VideoPlayerConfig {
    //Minimum Video you want to buffer while Playing
    public static final int MIN_BUFFER_DURATION = 3000;
    //Max Video you want to buffer during PlayBack
    public static final int MAX_BUFFER_DURATION = 5000;
    //Min Video you want to buffer before start Playing it
    public static final int MIN_PLAYBACK_START_BUFFER = 1500;
    //Min video You want to buffer when user resumes video
    public static final int MIN_PLAYBACK_RESUME_BUFFER = 5000;

    public static final String DEFAULT_VIDEO_URL =
        "https://androidwave.com/media/androidwave-video-exo-player.mp4";
  }

7. Create a layout file name with activity_exo_player.xml

Create layout file inside res-> layout-> activity_exo_player.xml. and some components Progressbar(buffering indicator) and ExoPlayerView

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ExoPlayerActivity"
    >

  <com.google.android.exoplayer2.ui.PlayerView
      android:id="@+id/videoFullScreenPlayer"
      android:layout_width="0dp"
      android:layout_height="0dp"
      android:background="#A6000000"
      app:controller_layout_id="@layout/exo_playback_control_view"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintHorizontal_bias="1.0"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintVertical_bias="1.0"
      app:player_layout_id="@layout/exo_simple_player_view"
      app:repeat_toggle_modes="none"
      app:show_timeout="45000"
      app:surface_type="texture_view"
      />

  <ProgressBar
      android:id="@+id/spinnerVideoDetails"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_centerInParent="true"
      android:layout_marginStart="8dp"
      android:layout_marginTop="8dp"
      android:layout_marginEnd="8dp"
      android:layout_marginBottom="8dp"
      android:indeterminate="true"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      />

  <ImageView
      android:id="@+id/imageViewExit"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginStart="16dp"
      android:layout_marginLeft="16dp"
      android:layout_marginTop="16dp"
      android:padding="@dimen/default_item_padding"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      app:srcCompat="@drawable/ic_cross_circular_button_outline"
      />
</android.support.constraint.ConstraintLayout>

8. Same as Create ExoPlayerActivity.java

Go to create an Activity with ExoPlayerActivity.java and set content layout is activity_exo_player.xml. Furthermore generates ButteKnief Injection by select set ContentView and right click and click generate. Now one popup appeared select view and set ids and click ok.

  @BindView(R.id.videoFullScreenPlayer)
  PlayerView videoFullScreenPlayer;
  @BindView(R.id.spinnerVideoDetails)
  ProgressBar spinnerVideoDetails;
  @BindView(R.id.imageViewExit)
  ImageView imageViewExit;
8.1. Define some variables and do some fullscreen configuration
  private static final String TAG = "ExoPlayerActivity";
  private static final String KEY_VIDEO_URI = "video_uri";
  String videoUri;
  SimpleExoPlayer player;
  Handler mHandler;
  Runnable mRunnable;
8.2. We are using vector icon so enable
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
8.3. Now initialise video player configuration
  private void initializePlayer() {
    if (player == null) {
      // 1. Create a default TrackSelector
      LoadControl loadControl = new DefaultLoadControl(
          new DefaultAllocator(true, 16),
          VideoPlayerConfig.MIN_BUFFER_DURATION,
          VideoPlayerConfig.MAX_BUFFER_DURATION,
          VideoPlayerConfig.MIN_PLAYBACK_START_BUFFER,
          VideoPlayerConfig.MIN_PLAYBACK_RESUME_BUFFER, -1, true);

      BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
      TrackSelection.Factory videoTrackSelectionFactory =
          new AdaptiveTrackSelection.Factory(bandwidthMeter);
      TrackSelector trackSelector =
          new DefaultTrackSelector(videoTrackSelectionFactory);
      // 2. Create the player
      player = ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(this), trackSelector,
          loadControl);
      videoFullScreenPlayer.setPlayer(player);
    }
  }
8.3. After inilizaing player create a media source using below methods
  private void buildMediaSource(Uri mUri) {
    // Measures bandwidth during playback. Can be null if not required.
    DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
    // Produces DataSource instances through which media data is loaded.
    DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this,
        Util.getUserAgent(this, getString(R.string.app_name)), bandwidthMeter);
    // This is the MediaSource representing the media to be played.
    MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
        .createMediaSource(mUri);
    // Prepare the player with the source.
    player.prepare(videoSource);
    player.setPlayWhenReady(true);
    player.addListener(this);
  }
8.4. Implement Player.EventListener and override unimplemented methods

Just implement Player.EventListener you have to implement override methods. onPlayerStateChanged() methods will return all player state.

  @Override
  public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
    switch (playbackState) {

      case Player.STATE_BUFFERING:
        spinnerVideoDetails.setVisibility(View.VISIBLE);
        break;
      case Player.STATE_ENDED:
        // Activate the force enable
        break;
      case Player.STATE_IDLE:

        break;
      case Player.STATE_READY:
        spinnerVideoDetails.setVisibility(View.GONE);

        break;
      default:
        // status = PlaybackStatus.IDLE;
        break;
    }
  }

9. Finally, The full activity (ExoPlayerActivity.java) code looks like this

Mainly we are doing below things in this activity

  • Initialize Player with configuration .
  • Prepare source files and settings on the player.
  • Add Player Event listener for listening to different player state
  • Manage player state with activity life cycler eg. pausePlayer(), resumePlayer(), releasePlayer()
package com.androidwave.exoplayer;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.ProgressBar;

import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.LoadControl;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
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.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

  public class ExoPlayerActivity extends AppCompatActivity implements Player.EventListener {
    private static final String TAG = "ExoPlayerActivity";

    private static final String KEY_VIDEO_URI = "video_uri";

    @BindView(R.id.videoFullScreenPlayer)
    PlayerView videoFullScreenPlayer;
    @BindView(R.id.spinnerVideoDetails)
    ProgressBar spinnerVideoDetails;
    @BindView(R.id.imageViewExit)
    ImageView imageViewExit;

    String videoUri;
    SimpleExoPlayer player;
    Handler mHandler;
    Runnable mRunnable;

    public static Intent getStartIntent(Context context, String videoUri) {
      Intent intent = new Intent(context, ExoPlayerActivity.class);
      intent.putExtra(KEY_VIDEO_URI, videoUri);
      return intent;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      requestWindowFeature(Window.FEATURE_NO_TITLE);
      this.getWindow()
          .setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
              WindowManager.LayoutParams.FLAG_FULLSCREEN);
      setContentView(R.layout.activity_exo_player);
      ButterKnife.bind(this);
      AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
      getSupportActionBar().hide();

      if (getIntent().hasExtra(KEY_VIDEO_URI)) {
        videoUri = getIntent().getStringExtra(KEY_VIDEO_URI);
      }

      setUp();
    }

    private void setUp() {
      initializePlayer();
      if (videoUri == null) {
        return;
      }
      buildMediaSource(Uri.parse(videoUri));
    }

    @OnClick(R.id.imageViewExit)
    public void onViewClicked() {
      finish();
    }

    private void initializePlayer() {
      if (player == null) {
        // 1. Create a default TrackSelector
        LoadControl loadControl = new DefaultLoadControl(
            new DefaultAllocator(true, 16),
            VideoPlayerConfig.MIN_BUFFER_DURATION,
            VideoPlayerConfig.MAX_BUFFER_DURATION,
            VideoPlayerConfig.MIN_PLAYBACK_START_BUFFER,
            VideoPlayerConfig.MIN_PLAYBACK_RESUME_BUFFER, -1, true);

        BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
        TrackSelection.Factory videoTrackSelectionFactory =
            new AdaptiveTrackSelection.Factory(bandwidthMeter);
        TrackSelector trackSelector =
            new DefaultTrackSelector(videoTrackSelectionFactory);
        // 2. Create the player
        player =
            ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(this), trackSelector,
                loadControl);
        videoFullScreenPlayer.setPlayer(player);
      }
    }

    private void buildMediaSource(Uri mUri) {
      // Measures bandwidth during playback. Can be null if not required.
      DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
      // Produces DataSource instances through which media data is loaded.
      DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this,
          Util.getUserAgent(this, getString(R.string.app_name)), bandwidthMeter);
      // This is the MediaSource representing the media to be played.
      MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
          .createMediaSource(mUri);
      // Prepare the player with the source.
      player.prepare(videoSource);
      player.setPlayWhenReady(true);
      player.addListener(this);
    }

    private void releasePlayer() {
      if (player != null) {
        player.release();
        player = null;
      }
    }

    private void pausePlayer() {
      if (player != null) {
        player.setPlayWhenReady(false);
        player.getPlaybackState();
      }
    }

    private void resumePlayer() {
      if (player != null) {
        player.setPlayWhenReady(true);
        player.getPlaybackState();
      }
    }

    @Override
    protected void onPause() {
      super.onPause();
      pausePlayer();
      if (mRunnable != null) {
        mHandler.removeCallbacks(mRunnable);
      }
    }

    @Override
    protected void onRestart() {
      super.onRestart();
      resumePlayer();
    }

    @Override
    protected void onDestroy() {
      super.onDestroy();
      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) {
      switch (playbackState) {

        case Player.STATE_BUFFERING:
          spinnerVideoDetails.setVisibility(View.VISIBLE);
          break;
        case Player.STATE_ENDED:
          // Activate the force enable
          break;
        case Player.STATE_IDLE:

          break;
        case Player.STATE_READY:
          spinnerVideoDetails.setVisibility(View.GONE);

          break;
        default:
          // status = PlaybackStatus.IDLE;
          break;
      }
    }

    @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() {

    }
  }

Conclusion

Wow! Great job! You just finished the ExoPlayer android example. You learned about integrating ExoPlayer in your application.

Download Sample Project- Video Streaming ExoPlayer in Android

If you have any queries, feel free to connect us.

In the previous tutorial, we demonstrate video streaming over the Internet using ExoPlayer. Somehow you missed previous articles you must read this ExoPlayer in Android for better understanding. In another article we saw how to play audio, video from SD card (local) as well, So you get an idea about ExoPlayer core functionality, DASH and UI library module.

I hope you read my previous tutorials(I can assume you have basic knowledge about ExoPlayer). In this tutorial, I will describe how to implement video playback in the RecyclerView Android. Just like some popular application eg. Magisto, Facebook, Twitter, Instagram.

This article based on ExoPlayer. In case some things missing in this article download full source and a working sample app is there.

Before starting code, Think about the problem statement. Basically, The following major challenges for us to implementing ExoPlayer in RecyclerView.

  • The first problem is managing ExoPlayer playback in RecyclerView
  • Second is we need to know which view on the scrolled list is currently active. Other words you can say which list item is visible 100%. So we can attach the player with a particular view.

Step to implement ExoPlayer in RecyclerView

  • Create Project and set up dependencies
  • Write a custom RecyclerView that are capable of manage video playback.
  • Write an adapter for holding list item
    • Create a model class with getter setter
    • Prepare an XML layout for media list item
    • Write ViewHolder class for representing media list item
    • Complete the RecyclerView Adapter
  • Do followings operation in MainActivity
    • In MainActivity layout, file adds custom ExoRecyclerView that create earlier.
    • Prepare demo content
    • Set the adapter to RecyclerView
    • Run the project

1. Create Project and set up dependencies

All conceptual part is done, let’s move to AndroidStudio create a new project with the default template. Follows few steps your project will be created, now we need to add some dependencies in app/gradle file.

Following dependencies are in this android app tutorial. I hope you already know the uses of use one.

  • RecyclerView – For using RecyclerView
  • ExoPlayer – Using ExoPlayer
  • Glide – For loading media thumbnails
  • Constraint Layout – Performace is much better to other layouts
1.1 Open the app/build.gradle file and add all above dependencies
  // RecyclerView
  implementation 'com.android.support:recyclerview-v7:28.0.0'

  // ExoPlayer
  implementation 'com.google.android.exoplayer:exoplayer:2.7.3'
  // Image loading
  implementation 'com.github.bumptech.glide:glide:4.9.0'
  annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
1.2 Enable vector drawable by adding below line inside defaultConfig bracket
  vectorDrawables.useSupportLibrary = true

2. Write a custom RecyclerView that is capable manage video playback.

Doing that we have to create a new subclass of RecyclerView named is ExoPlayerRecyclerView. Mean create a new class ExoPlayerRecyclerView.java which extends RecyclerView. In this class, we have to manage two things. First, manage ExoPlayer playback. Second is to find that list item that visibility 100%. So that we will add a player on that particular view.

2.1 Problem – how to manage ExoPlayer playback?
  • Create a class ExoPlayerRecyclerView.java which extend RecyclerView 
  • Get start position and end position of view from LayoutManager after that calculate currently visible view position.
  • Here now on target position, we will add ExoPlayerView and set ExoPlayer.
  //play the video in the row
  public void playVideo(boolean isEndOfList) {

    int targetPosition;

    if (!isEndOfList) {
      int startPosition = ((LinearLayoutManager) Objects.requireNonNull(
          getLayoutManager())).findFirstVisibleItemPosition();
      int endPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();

      // if there is more than 2 list-items on the screen, set the difference to be 1
      if (endPosition - startPosition > 1) {
        endPosition = startPosition + 1;
      }

      // something is wrong. return.
      if (startPosition < 0 || endPosition < 0) {
        return;
      }

      // if there is more than 1 list-item on the screen
      if (startPosition != endPosition) {
        int startPositionVideoHeight = getVisibleVideoSurfaceHeight(startPosition);
        int endPositionVideoHeight = getVisibleVideoSurfaceHeight(endPosition);

        targetPosition =
            startPositionVideoHeight > endPositionVideoHeight ? startPosition : endPosition;
      } else {
        targetPosition = startPosition;
      }
    } else {
      targetPosition = mediaObjects.size() - 1;
    }

    Log.d(TAG, "playVideo: target position: " + targetPosition);

    // video is already playing so return
    if (targetPosition == playPosition) {
      return;
    }

    // set the position of the list-item that is to be played
    playPosition = targetPosition;
    if (videoSurfaceView == null) {
      return;
    }

    // remove any old surface views from previously playing videos
    videoSurfaceView.setVisibility(INVISIBLE);
    removeVideoView(videoSurfaceView);

    int currentPosition =
        targetPosition - ((LinearLayoutManager) Objects.requireNonNull(
            getLayoutManager())).findFirstVisibleItemPosition();

    View child = getChildAt(currentPosition);
    if (child == null) {
      return;
    }

    PlayerViewHolder holder = (PlayerViewHolder) child.getTag();
    if (holder == null) {
      playPosition = -1;
      return;
    }
    mediaCoverImage = holder.mediaCoverImage;
    progressBar = holder.progressBar;
    volumeControl = holder.volumeControl;
    viewHolderParent = holder.itemView;
    requestManager = holder.requestManager;
    mediaContainer = holder.mediaContainer;

    videoSurfaceView.setPlayer(videoPlayer);
    viewHolderParent.setOnClickListener(videoViewClickListener);

    DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(
        context, Util.getUserAgent(context, AppName));
    String mediaUrl = mediaObjects.get(targetPosition).getUrl();
    if (mediaUrl != null) {
      MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
          .createMediaSource(Uri.parse(mediaUrl));
      videoPlayer.prepare(videoSource);
      videoPlayer.setPlayWhenReady(true);
    }
  }

  andwidthMeter =new

  DefaultBandwidthMeter();
  // Produces DataSource instances through which media data is loaded.

  DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(appContext,
      Util.getUserAgent(appContext, "android_wave_list"), defaultBandwidthMeter);
  // This is the MediaSource representing the media to be played.
  String uriString = videoInfoList.get(targetPosition).getUrl();
        if(uriString !=null)

  {
    MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
        .createMediaSource(Uri.parse(uriString));
    // Prepare the player with the source.
    player.prepare(videoSource);
    player.setPlayWhenReady(true);
  }
}
2.2 Second Problem – Get visible video surface scrolled list.
 /**
   * Returns the visible region of the video surface on the screen.
   * if some is cut off, it will return less than the @videoSurfaceDefaultHeight
   */
  private int getVisibleVideoSurfaceHeight(int playPosition) {
    int at = playPosition - ((LinearLayoutManager) Objects.requireNonNull(
        getLayoutManager())).findFirstVisibleItemPosition();
    Log.d(TAG, "getVisibleVideoSurfaceHeight: at: " + at);

    View child = getChildAt(at);
    if (child == null) {
      return 0;
    }

    int[] location = new int[2];
    child.getLocationInWindow(location);

    if (location[1] < 0) {
      return location[1] + videoSurfaceDefaultHeight;
    } else {
      return screenDefaultHeight - location[1];
    }
  }

I have solved both problems, The complete code of ExoPlayerRecyclerView is

package com.androidwave.exoplayer.ui;

import android.content.Context;
import android.graphics.Point;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import com.androidwave.exoplayer.R;
import com.androidwave.exoplayer.adapter.PlayerViewHolder;
import com.androidwave.exoplayer.model.MediaObject;
import com.bumptech.glide.RequestManager;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
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.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
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 java.util.ArrayList;
import java.util.Objects;

/**
 * Created on : May 24, 2019
 * Author     : AndroidWave
 */
public class ExoPlayerRecyclerView extends RecyclerView {

  private static final String TAG = "ExoPlayerRecyclerView";
  private static final String AppName = "Android ExoPlayer";
  /**
   * PlayerViewHolder UI component
   * Watch PlayerViewHolder class
   */
  private ImageView mediaCoverImage, volumeControl;
  private ProgressBar progressBar;
  private View viewHolderParent;
  private FrameLayout mediaContainer;
  private PlayerView videoSurfaceView;
  private SimpleExoPlayer videoPlayer;
  /**
   * variable declaration
   */
  // Media List
  private ArrayList<MediaObject> mediaObjects = new ArrayList<>();
  private int videoSurfaceDefaultHeight = 0;
  private int screenDefaultHeight = 0;
  private Context context;
  private int playPosition = -1;
  private boolean isVideoViewAdded;
  private RequestManager requestManager;
  // controlling volume state
  private VolumeState volumeState;
  private OnClickListener videoViewClickListener = new OnClickListener() {
    @Override
    public void onClick(View v) {
      toggleVolume();
    }
  };

  public ExoPlayerRecyclerView(@NonNull Context context) {
    super(context);
    init(context);
  }

  public ExoPlayerRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    init(context);
  }

  private void init(Context context) {
    this.context = context.getApplicationContext();
    Display display = ((WindowManager) Objects.requireNonNull(
        getContext().getSystemService(Context.WINDOW_SERVICE))).getDefaultDisplay();
    Point point = new Point();
    display.getSize(point);

    videoSurfaceDefaultHeight = point.x;
    screenDefaultHeight = point.y;

    videoSurfaceView = new PlayerView(this.context);
    videoSurfaceView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM);

    BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
    TrackSelection.Factory videoTrackSelectionFactory =
        new AdaptiveTrackSelection.Factory(bandwidthMeter);
    TrackSelector trackSelector =
        new DefaultTrackSelector(videoTrackSelectionFactory);

    //Create the player using ExoPlayerFactory
    videoPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector);
    // Disable Player Control
    videoSurfaceView.setUseController(false);
    // Bind the player to the view.
    videoSurfaceView.setPlayer(videoPlayer);
    // Turn on Volume
    setVolumeControl(VolumeState.ON);

    addOnScrollListener(new RecyclerView.OnScrollListener() {
      @Override
      public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);

        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
          if (mediaCoverImage != null) {
            // show the old thumbnail
            mediaCoverImage.setVisibility(VISIBLE);
          }

          // There's a special case when the end of the list has been reached.
          // Need to handle that with this bit of logic
          if (!recyclerView.canScrollVertically(1)) {
            playVideo(true);
          } else {
            playVideo(false);
          }
        }
      }

      @Override
      public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
      }
    });

    addOnChildAttachStateChangeListener(new OnChildAttachStateChangeListener() {
      @Override
      public void onChildViewAttachedToWindow(@NonNull View view) {

      }

      @Override
      public void onChildViewDetachedFromWindow(@NonNull View view) {
        if (viewHolderParent != null && viewHolderParent.equals(view)) {
          resetVideoView();
        }
      }
    });

    videoPlayer.addListener(new Player.EventListener() {
      @Override
      public void onTimelineChanged(Timeline timeline, @Nullable 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) {
        switch (playbackState) {

          case Player.STATE_BUFFERING:
            Log.e(TAG, "onPlayerStateChanged: Buffering video.");
            if (progressBar != null) {
              progressBar.setVisibility(VISIBLE);
            }

            break;
          case Player.STATE_ENDED:
            Log.d(TAG, "onPlayerStateChanged: Video ended.");
            videoPlayer.seekTo(0);
            break;
          case Player.STATE_IDLE:

            break;
          case Player.STATE_READY:
            Log.e(TAG, "onPlayerStateChanged: Ready to play.");
            if (progressBar != null) {
              progressBar.setVisibility(GONE);
            }
            if (!isVideoViewAdded) {
              addVideoView();
            }
            break;
          default:
            break;
        }
      }

      @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 void playVideo(boolean isEndOfList) {

    int targetPosition;

    if (!isEndOfList) {
      int startPosition = ((LinearLayoutManager) Objects.requireNonNull(
          getLayoutManager())).findFirstVisibleItemPosition();
      int endPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();

      // if there is more than 2 list-items on the screen, set the difference to be 1
      if (endPosition - startPosition > 1) {
        endPosition = startPosition + 1;
      }

      // something is wrong. return.
      if (startPosition < 0 || endPosition < 0) {
        return;
      }

      // if there is more than 1 list-item on the screen
      if (startPosition != endPosition) {
        int startPositionVideoHeight = getVisibleVideoSurfaceHeight(startPosition);
        int endPositionVideoHeight = getVisibleVideoSurfaceHeight(endPosition);

        targetPosition =
            startPositionVideoHeight > endPositionVideoHeight ? startPosition : endPosition;
      } else {
        targetPosition = startPosition;
      }
    } else {
      targetPosition = mediaObjects.size() - 1;
    }

    Log.d(TAG, "playVideo: target position: " + targetPosition);

    // video is already playing so return
    if (targetPosition == playPosition) {
      return;
    }

    // set the position of the list-item that is to be played
    playPosition = targetPosition;
    if (videoSurfaceView == null) {
      return;
    }

    // remove any old surface views from previously playing videos
    videoSurfaceView.setVisibility(INVISIBLE);
    removeVideoView(videoSurfaceView);

    int currentPosition =
        targetPosition - ((LinearLayoutManager) Objects.requireNonNull(
            getLayoutManager())).findFirstVisibleItemPosition();

    View child = getChildAt(currentPosition);
    if (child == null) {
      return;
    }

    PlayerViewHolder holder = (PlayerViewHolder) child.getTag();
    if (holder == null) {
      playPosition = -1;
      return;
    }
    mediaCoverImage = holder.mediaCoverImage;
    progressBar = holder.progressBar;
    volumeControl = holder.volumeControl;
    viewHolderParent = holder.itemView;
    requestManager = holder.requestManager;
    mediaContainer = holder.mediaContainer;

    videoSurfaceView.setPlayer(videoPlayer);
    viewHolderParent.setOnClickListener(videoViewClickListener);

    DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(
        context, Util.getUserAgent(context, AppName));
    String mediaUrl = mediaObjects.get(targetPosition).getUrl();
    if (mediaUrl != null) {
      MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
          .createMediaSource(Uri.parse(mediaUrl));
      videoPlayer.prepare(videoSource);
      videoPlayer.setPlayWhenReady(true);
    }
  }

  /**
   * Returns the visible region of the video surface on the screen.
   * if some is cut off, it will return less than the @videoSurfaceDefaultHeight
   */
  private int getVisibleVideoSurfaceHeight(int playPosition) {
    int at = playPosition - ((LinearLayoutManager) Objects.requireNonNull(
        getLayoutManager())).findFirstVisibleItemPosition();
    Log.d(TAG, "getVisibleVideoSurfaceHeight: at: " + at);

    View child = getChildAt(at);
    if (child == null) {
      return 0;
    }

    int[] location = new int[2];
    child.getLocationInWindow(location);

    if (location[1] < 0) {
      return location[1] + videoSurfaceDefaultHeight;
    } else {
      return screenDefaultHeight - location[1];
    }
  }

  // Remove the old player
  private void removeVideoView(PlayerView videoView) {
    ViewGroup parent = (ViewGroup) videoView.getParent();
    if (parent == null) {
      return;
    }

    int index = parent.indexOfChild(videoView);
    if (index >= 0) {
      parent.removeViewAt(index);
      isVideoViewAdded = false;
      viewHolderParent.setOnClickListener(null);
    }
  }

  private void addVideoView() {
    mediaContainer.addView(videoSurfaceView);
    isVideoViewAdded = true;
    videoSurfaceView.requestFocus();
    videoSurfaceView.setVisibility(VISIBLE);
    videoSurfaceView.setAlpha(1);
    mediaCoverImage.setVisibility(GONE);
  }

  private void resetVideoView() {
    if (isVideoViewAdded) {
      removeVideoView(videoSurfaceView);
      playPosition = -1;
      videoSurfaceView.setVisibility(INVISIBLE);
      mediaCoverImage.setVisibility(VISIBLE);
    }
  }

  public void releasePlayer() {

    if (videoPlayer != null) {
      videoPlayer.release();
      videoPlayer = null;
    }

    viewHolderParent = null;
  }

  public void onPausePlayer() {
    if (videoPlayer != null) {
      videoPlayer.stop(true);
    }
  }

  private void toggleVolume() {
    if (videoPlayer != null) {
      if (volumeState == VolumeState.OFF) {
        Log.d(TAG, "togglePlaybackState: enabling volume.");
        setVolumeControl(VolumeState.ON);
      } else if (volumeState == VolumeState.ON) {
        Log.d(TAG, "togglePlaybackState: disabling volume.");
        setVolumeControl(VolumeState.OFF);
      }
    }
  }

  private void setVolumeControl(VolumeState state) {
    volumeState = state;
    if (state == VolumeState.OFF) {
      videoPlayer.setVolume(0f);
      animateVolumeControl();
    } else if (state == VolumeState.ON) {
      videoPlayer.setVolume(1f);
      animateVolumeControl();
    }
  }

  private void animateVolumeControl() {
    if (volumeControl != null) {
      volumeControl.bringToFront();
      if (volumeState == VolumeState.OFF) {
        requestManager.load(R.drawable.ic_volume_off)
            .into(volumeControl);
      } else if (volumeState == VolumeState.ON) {
        requestManager.load(R.drawable.ic_volume_on)
            .into(volumeControl);
      }
      volumeControl.animate().cancel();

      volumeControl.setAlpha(1f);

      volumeControl.animate()
          .alpha(0f)
          .setDuration(600).setStartDelay(1000);
    }
  }

  public void setMediaObjects(ArrayList<MediaObject> mediaObjects) {
    this.mediaObjects = mediaObjects;
  }

  /**
   * Volume ENUM
   */
  private enum VolumeState {
    ON, OFF
  }
}

Now ExoPlayerRecyclerView.java component is ready to use. Now I will explain how to use this component.

3. Write an adapter for holding list item

Before moving ahead we need to write some helper class for RecyclerView adapter,

3.1 Create a model class with getter setter

Go to the src folder and create the model folder for separating all model classes. Inside this folder create a POJO class named MediaObject.java. That has getter and setter of title and cover image URL and media URL

package com.androidwave.exoplayer.model;

/**
 * Created by Morris on 03,June,2019
 */
public class MediaObject {
  private int uId;
  private String title;
  private String mediaUrl;
  private String mediaCoverImgUrl;
  private String userHandle;

  public String getUserHandle() {
    return userHandle;
  }

  public void setUserHandle(String mUserHandle) {
    this.userHandle = mUserHandle;
  }

  public int getId() {
    return uId;
  }

  public void setId(int uId) {
    this.uId = uId;
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String mTitle) {
    this.title = mTitle;
  }

  public String getUrl() {
    return mediaUrl;
  }

  public void setUrl(String mUrl) {
    this.mediaUrl = mUrl;
  }

  public String getCoverUrl() {
    return mediaCoverImgUrl;
  }

  public void setCoverUrl(String mCoverUrl) {
    this.mediaCoverImgUrl = mCoverUrl;
  }
}
3.2 Prepare an XML layout for media list item

Create a layout file inside the res => layout folder named is layout_media_list_item.xml, which holds the view of the list item. So add some view component in this layout file.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    android:background="@color/white"
    >

  <TextView
      android:id="@+id/tvTitle"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginTop="8dp"
      android:letterSpacing="-0.02"
      android:lineSpacingExtra="5sp"
      android:textColor="@color/navy"
      android:textSize="18sp"
      android:textStyle="bold"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      tools:text="Was india better then south africa in current serise?"
      />

  <ImageView
      android:id="@+id/imageView"
      android:layout_width="25dp"
      android:layout_height="25dp"
      android:layout_marginBottom="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginTop="8dp"
      app:layout_constraintBottom_toTopOf="@+id/mediaContainer"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/tvTitle"
      app:srcCompat="@drawable/ic_user"
      />

  <TextView
      android:id="@+id/tvUserHandle"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginStart="8dp"
      android:letterSpacing="-0.02"
      android:textColor="@color/windows_blue"
      android:textSize="10sp"
      app:layout_constraintBottom_toBottomOf="@+id/imageView"
      app:layout_constraintStart_toEndOf="@+id/imageView"
      app:layout_constraintTop_toTopOf="@+id/imageView"
      tools:text="by @morris12"
      />

  <FrameLayout
      android:id="@+id/mediaContainer"
      android:layout_width="match_parent"
      android:layout_height="300dp"
      android:layout_marginBottom="8dp"
      android:layout_marginEnd="8dp"
      android:layout_marginStart="8dp"
      android:adjustViewBounds="true"
      android:background="@android:color/black"
      android:gravity="center"
      android:scaleType="center"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/imageView"
      >

    <ImageView
        android:id="@+id/ivMediaCoverImage"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"
        android:scaleType="centerCrop"
        android:src="@drawable/cover"
        />


    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:visibility="gone"
        style="?android:attr/progressBarStyle"
        />
    <ImageView
        android:id="@+id/ivVolumeControl"
        android:layout_width="25dp"
        android:layout_height="25dp"
        android:layout_gravity="end|bottom"
        android:layout_marginBottom="15dp"
        android:layout_marginEnd="15dp"
        android:alpha="0"
        android:animateLayoutChanges="true"
        android:scaleType="centerCrop"
        android:src="@drawable/ic_volume_on"
        />
  </FrameLayout>

</android.support.constraint.ConstraintLayout>
3.4 Write ViewHolder class for representing media list item

Create a new class inside the adapter package named is PlayerViewHolder. This class will extends RecyclerView.ViewHolder. Initialize the all list item inside this view.

package com.androidwave.exoplayer.adapter;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.androidwave.exoplayer.R;
import com.androidwave.exoplayer.model.MediaObject;
import com.bumptech.glide.RequestManager;

/**
 * Created by Morris on 03,June,2019
 */
public class PlayerViewHolder extends RecyclerView.ViewHolder {

  /**
   * below view have public modifier because
   * we have access PlayerViewHolder inside the ExoPlayerRecyclerView
   */
  public FrameLayout mediaContainer;
  public ImageView mediaCoverImage, volumeControl;
  public ProgressBar progressBar;
  public RequestManager requestManager;
  private TextView title, userHandle;
  private View parent;

  public PlayerViewHolder(@NonNull View itemView) {
    super(itemView);
    parent = itemView;
    mediaContainer = itemView.findViewById(R.id.mediaContainer);
    mediaCoverImage = itemView.findViewById(R.id.ivMediaCoverImage);
    title = itemView.findViewById(R.id.tvTitle);
    userHandle = itemView.findViewById(R.id.tvUserHandle);
    progressBar = itemView.findViewById(R.id.progressBar);
    volumeControl = itemView.findViewById(R.id.ivVolumeControl);
  }

  void onBind(MediaObject mediaObject, RequestManager requestManager) {
    this.requestManager = requestManager;
    parent.setTag(this);
    title.setText(mediaObject.getTitle());
    userHandle.setText(mediaObject.getUserHandle());
    this.requestManager
        .load(mediaObject.getCoverUrl())
        .into(mediaCoverImage);
  }
}
3.5 Complete the RecyclerView Adapter

Doing that create an adapter class named is MediaRecyclerAdapter and paste below code

package com.androidwave.exoplayer.adapter;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import com.androidwave.exoplayer.R;
import com.androidwave.exoplayer.model.MediaObject;
import com.bumptech.glide.RequestManager;
import java.util.ArrayList;

/**
 * Created by Morris on 03,June,2019
 */
public class MediaRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

  private ArrayList<MediaObject> mediaObjects;
  private RequestManager requestManager;

  public MediaRecyclerAdapter(ArrayList<MediaObject> mediaObjects,
      RequestManager requestManager) {
    this.mediaObjects = mediaObjects;
    this.requestManager = requestManager;
  }

  @NonNull
  @Override
  public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
    return new PlayerViewHolder(
        LayoutInflater.from(viewGroup.getContext())
            .inflate(R.layout.layout_media_list_item, viewGroup, false));
  }

  @Override
  public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
    ((PlayerViewHolder) viewHolder).onBind(mediaObjects.get(i), requestManager);
  }

  @Override
  public int getItemCount() {
    return mediaObjects.size();
  }
}

4. Do followings operation in MainActivity

All part is done, Now come to activity part.

4.1 Lets add ExoPlayerRecyclerView inside the XML file

If you selected some default template while creating a project, MainActivity.java and activity_main.xml class will automatically be created. Open the activity_main and add below code.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    >

  <com.androidwave.exoplayer.ui.ExoPlayerRecyclerView
      android:id="@+id/exoPlayerRecyclerView"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:background="#dedede"
      android:dividerHeight="8dp"
      />

</android.support.constraint.ConstraintLayout>
4.2 Prepare demo content

For displaying data on RecyclerView We have to prepare a demo content.

 private void prepareVideoList() {
    VideoInfo videoInfo = new VideoInfo();
    videoInfo.setId(1);
    videoInfo.setUserHandle("@h.pandya");
    videoInfo.setTitle("Do you think the concept of marriage will no longer exist in the future?");
    videoInfo.setCoverUrl(
        "https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-1.png");
    videoInfo.setUrl("https://androidwave.com/media/androidwave-video-1.mp4");

    VideoInfo videoInfo2 = new VideoInfo();
    videoInfo2.setId(2);
    videoInfo2.setUserHandle("@hardik.patel");
    videoInfo2.setTitle(
        "If my future husband doesn't cook food as good as my mother should I scold him?");
    videoInfo2.setCoverUrl(
        "https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-2.png");
    videoInfo2.setUrl("https://androidwave.com/media/androidwave-video-2.mp4");

    VideoInfo videoInfo3 = new VideoInfo();
    videoInfo3.setId(3);
    videoInfo3.setUserHandle("@arun.gandhi");
    videoInfo3.setTitle("Give your opinion about the Ayodhya temple controversy.");
    videoInfo3.setCoverUrl(
        "https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-3.png");
    videoInfo3.setUrl("https://androidwave.com/media/androidwave-video-3.mp4");

    VideoInfo videoInfo4 = new VideoInfo();
    videoInfo4.setId(4);
    videoInfo4.setUserHandle("@sachin.patel");
    videoInfo4.setTitle("When did kama founders find sex offensive to Indian traditions");
    videoInfo4.setCoverUrl(
        "https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-4.png");
    videoInfo4.setUrl("https://androidwave.com/media/androidwave-video-6.mp4");

    VideoInfo videoInfo5 = new VideoInfo();
    videoInfo5.setId(5);
    videoInfo5.setUserHandle("@monika.sharma");
    videoInfo5.setTitle("When did you last cry in front of someone?");
    videoInfo5.setCoverUrl(
        "https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-5.png");
    videoInfo5.setUrl("https://androidwave.com/media/androidwave-video-5.mp4");

    videoInfoList.add(videoInfo);
    videoInfoList.add(videoInfo2);
    videoInfoList.add(videoInfo3);
    videoInfoList.add(videoInfo4);
    videoInfoList.add(videoInfo5);
    videoInfoList.add(videoInfo);
    videoInfoList.add(videoInfo2);
    videoInfoList.add(videoInfo3);
    videoInfoList.add(videoInfo4);
    videoInfoList.add(videoInfo5);
  }

4.3 Finally MainActivity source code looks like

package com.androidwave.exoplayer;

import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import com.androidwave.exoplayer.adapter.MediaRecyclerAdapter;
import com.androidwave.exoplayer.model.MediaObject;
import com.androidwave.exoplayer.ui.ExoPlayerRecyclerView;
import com.androidwave.exoplayer.utils.DividerItemDecoration;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.request.RequestOptions;
import java.util.ArrayList;

import static android.widget.LinearLayout.VERTICAL;

/**
 * Created by Morris on 03,June,2019
 */
public class MainActivity extends AppCompatActivity {

  ExoPlayerRecyclerView mRecyclerView;

  private ArrayList<MediaObject> mediaObjectList = new ArrayList<>();
  private MediaRecyclerAdapter mAdapter;
  private boolean firstTime = true;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    initView();
    // Prepare demo content
    prepareVideoList();

    //set data object
    mRecyclerView.setMediaObjects(mediaObjectList);
    mAdapter = new MediaRecyclerAdapter(mediaObjectList, initGlide());

    //Set Adapter
    mRecyclerView.setAdapter(mAdapter);

    if (firstTime) {
      new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
          mRecyclerView.playVideo(false);
        }
      });
      firstTime = false;
    }
  }

  private void initView() {
    mRecyclerView = findViewById(R.id.exoPlayerRecyclerView);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this, VERTICAL, false));
    Drawable dividerDrawable = ContextCompat.getDrawable(this, R.drawable.divider_drawable);
    mRecyclerView.addItemDecoration(new DividerItemDecoration(dividerDrawable));
    mRecyclerView.setItemAnimator(new DefaultItemAnimator());
  }

  private RequestManager initGlide() {
    RequestOptions options = new RequestOptions();

    return Glide.with(this)
        .setDefaultRequestOptions(options);
  }

  @Override
  protected void onDestroy() {
    if (mRecyclerView != null) {
      mRecyclerView.releasePlayer();
    }
    super.onDestroy();
  }

  private void prepareVideoList() {
    MediaObject mediaObject = new MediaObject();
    mediaObject.setId(1);
    mediaObject.setUserHandle("@h.pandya");
    mediaObject.setTitle(
        "Do you think the concept of marriage will no longer exist in the future?");
    mediaObject.setCoverUrl(
        "https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-1.png");
    mediaObject.setUrl("https://androidwave.com/media/androidwave-video-1.mp4");

    MediaObject mediaObject2 = new MediaObject();
    mediaObject2.setId(2);
    mediaObject2.setUserHandle("@hardik.patel");
    mediaObject2.setTitle(
        "If my future husband doesn't cook food as good as my mother should I scold him?");
    mediaObject2.setCoverUrl(
        "https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-2.png");
    mediaObject2.setUrl("https://androidwave.com/media/androidwave-video-2.mp4");

    MediaObject mediaObject3 = new MediaObject();
    mediaObject3.setId(3);
    mediaObject3.setUserHandle("@arun.gandhi");
    mediaObject3.setTitle("Give your opinion about the Ayodhya temple controversy.");
    mediaObject3.setCoverUrl(
        "https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-3.png");
    mediaObject3.setUrl("https://androidwave.com/media/androidwave-video-3.mp4");

    MediaObject mediaObject4 = new MediaObject();
    mediaObject4.setId(4);
    mediaObject4.setUserHandle("@sachin.patel");
    mediaObject4.setTitle("When did kama founders find sex offensive to Indian traditions");
    mediaObject4.setCoverUrl(
        "https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-4.png");
    mediaObject4.setUrl("https://androidwave.com/media/androidwave-video-6.mp4");

    MediaObject mediaObject5 = new MediaObject();
    mediaObject5.setId(5);
    mediaObject5.setUserHandle("@monika.sharma");
    mediaObject5.setTitle("When did you last cry in front of someone?");
    mediaObject5.setCoverUrl(
        "https://androidwave.com/media/images/exo-player-in-recyclerview-in-android-5.png");
    mediaObject5.setUrl("https://androidwave.com/media/androidwave-video-5.mp4");

    mediaObjectList.add(mediaObject);
    mediaObjectList.add(mediaObject2);
    mediaObjectList.add(mediaObject3);
    mediaObjectList.add(mediaObject4);
    mediaObjectList.add(mediaObject5);
    mediaObjectList.add(mediaObject);
    mediaObjectList.add(mediaObject2);
    mediaObjectList.add(mediaObject3);
    mediaObjectList.add(mediaObject4);
    mediaObjectList.add(mediaObject5);
  }
}

Conclusion

You almost finished. That’s all for ExoPlayer in RecyclerViiew. I hope it’s helpful for you, then help me by sharing this post with all your friends who learning android app development. I

Download Sample Project- ExoPlayer in RecyclerView in Android

If you have any queries, feel free to ask them in the comment section below. Happy Coding 🙂

Read more on android architecture components tutorials