Author

admin

Browsing

DateTime is a common utility in Android. In these days most of the project required date and time formatting in Android. So we are building a plug and play solution. I hope each developer will keep this utility in own inbox for a Time Saving purpose. In this tutorials, we demonstrate different-different date formatting and parsing

SimpleDateFormat is a java class which extends the DateFormat java class. It is used for normalization, formatting and parsing date in a locale-sensitive manner.

SimpleDateFormat is used for below utility in Android

  • Formatting – It allows for formatting Date to Text in Android any given locale.
  • Parsing – You can parse any Text in Date object by using SimpleDateFormat

Some specific Letter is used for represent Date or Time Component in Android. You can read Google Official site.

Formatting

Here we discuss some date format my intention is to cover each combination of date format.

DATE_FORMAT_1 = hh:mm a
The output will be -: 10:37 am

DATE_FORMAT_2 = h:mm a
Output will be -: 10:37 am

DATE_FORMAT_3 = yyyy-MM-dd
The output will be -: 2018-12-05

DATE_FORMAT_4 = dd-MMMM-yyyy
The output will be -: 05-December-2018

DATE_FORMAT_5 = dd MMMM yyyy
The output will be -: 05 December 2018

DATE_FORMAT_6 = dd MMMM yyyy zzzz
The output will be -: 05 December 2018 UTC

DATE_FORMAT_7 = EEE, MMM d, ''yy
The output will be -: Wed, Dec 5, '18

DATE_FORMAT_8 = yyyy-MM-dd HH:mm:ss
The Output will be -: 2018-12-05 10:37:43

DATE_FORMAT_9 = h:mm a dd MMMM yyyy
The output will be -: 10:37 am 05 December 2018

DATE_FORMAT_10 = K:mm a, z
The output will be -: 10:37 am, UTC

DATE_FORMAT_11 = hh 'o''clock' a, zzzz
The output will be -: 10 o'clock am, UTC

DATE_FORMAT_12 = yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
The output will be -: 2018-12-05T10:37:43.937Z

DATE_FORMAT_13 = E, dd MMM yyyy HH:mm:ss z
The output will be -: Wed, 05 Dec 2018 10:37:43 UTC

DATE_FORMAT_14 = yyyy.MM.dd G 'at' HH:mm:ss z
The output will be -: 2018.12.05 AD at 10:37:43 UTC

DATE_FORMAT_15 = yyyyy.MMMMM.dd GGG hh:mm aaa
The output will be -: 02018.D.05 AD 10:37 am

DATE_FORMAT_16 = EEE, d MMM yyyy HH:mm:ss Z
The output will be -: Wed, 5 Dec 2018 10:37:43 +0000

DATE_FORMAT_17 = yyyy-MM-dd'T'HH:mm:ss.SSSZ
The output will be -: 2018-12-05T10:37:43.946+0000

DATE_FORMAT_18 = yyyy-MM-dd'T'HH:mm:ss.SSSXXX
The output will be -: 2018-12-05T10:37:43.949Z

DATE_FORMAT_19 = dd-MMM-yyyy
The output will be -: 05-Dec-2018

All above date format combination. Now comes utility class.

Get Current Date

    /**
     * Default date format 
     */
    public static final String DATE_FORMAT_2 = "dd-MMM-yyyy";
    
    public static String getCurrentDate() {
        SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_1);
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date today = Calendar.getInstance().getTime();
        return dateFormat.format(today);
    }

Get current Time


    /**
     * Default time format
     */
    public static final String DATE_FORMAT_1 = "hh:mm a";
    
    public static String getCurrentTime() {
        SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_1);
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date today = Calendar.getInstance().getTime();
        return dateFormat.format(today);
    }

Get Date and Time from Timestamp

  /**
     * 
     * @param time in milliseconds (Timestamp)
     * @param mDateFormat SimpleDateFormat
     * @return
     */
    public static String getDateTimeFromTimeStamp(Long time, String mDateFormat) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(mDateFormat);
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date dateTime = new Date(time);
        return dateFormat.format(dateTime);
    }

How to use it

        /**
         * Get current date example 1
         */
        String mCurrentDate = DateUtils.getCurrentDate();
        
        /**
         * Get current time example 2
         */
        String mCurrentTime = DateUtils.getCurrentTime();

       /**
        * Date Format example 3
        */
        public static final String DATE_FORMAT = "dd-MMM-yyyy";
        String mDateTime = DateUtils.getDateFromTimeStamp(System.currentTimeMillis(), DATE_FORMAT);

Date Parsing in Android

Let’s take few example of date parsing

Get Timestamp from DateTime in Android

 /**
     * Get Timestamp from date and time
     * @param mDateTime datetime String
     * @param mDateFormat Date Format 
     * @return
     * @throws ParseException
     */
    public static long getTimeStampFromDateTime(String mDateTime, String mDateFormat) throws ParseException {
        SimpleDateFormat dateFormat = new SimpleDateFormat(mDateFormat);
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date date = dateFormat.parse(mDateTime);
        return date.getTime();
    }  

Get DateTime from Date instase

   /**
     * Return  datetime String from date object  
     * @param mDateFormat format of date 
     * @param date date object that you want to parse
     * @return
     */
    public static String formatDateTimeFromDate(String mDateFormat, Date date) {
        if (date == null) {
            return null;
        }
        return DateFormat.format(mDateFormat, date).toString();
    }

Convert DateTime String to another DateTime String format

This is very useful and confusing utility in Android

   /**
     *  Convert one date format string  to another date format string in android
     * @param inputDateFormat Input SimpleDateFormat
     * @param outputDateFormat Output SimpleDateFormat
     * @param inputDate  input Date String
     * @return
     * @throws ParseException
     */
    public static String formatDateFromDateString(String inputDateFormat, String outputDateFormat, String inputDate) throws ParseException {
        Date mParsedDate;
        String mOutputDateString;
        SimpleDateFormat mInputDateFormat = new SimpleDateFormat(inputDateFormat, java.util.Locale.getDefault());
        SimpleDateFormat mOutputDateFormat = new SimpleDateFormat(outputDateFormat, java.util.Locale.getDefault());
        mParsedDate = mInputDateFormat.parse(inputDate);
        mOutputDateString = mOutputDateFormat.format(mParsedDate);
        return mOutputDateString;

    }

How to use

       /**
         * Date Format example 4
         */
        public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
        String mDateTimeString = "2018-12-05 10:37:43";
        try {
            long mTimestamp = DateUtils.getTimeStampFromDateTime(mDateTimeString, DATE_FORMAT);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        /**
         * Get current datetime example 5
         */
        public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
        String mDateTime = DateUtils.formatDateTimeFromDate(DATE_FORMAT, Calendar.getInstance().getTime());
   
   
       
      /**  Example 6
       * DateTime Input Format
       */
       public static final String DATE_INPUT_FORMAT = "yyyy-MM-dd HH:mm:ss";

      /**
       * DateTime Output Format
       */
      public static final String DATE_OUTPUT_FORMAT = "dd-MMM-yyyy";

       String mDateTimeString = "2018-12-05 10:37:43";

        String mDateTime = null;
        try {
            mDateTime = DateUtils.formatDateFromDateString(DATE_INPUT_FORMAT, DATE_OUTPUT_FORMAT, mDateTimeString);
        } catch (ParseException e) {
            e.printStackTrace();
        }

      The output is - 05-Dec-2018;
        

The complete Utitliy seems like below

package com.androidwave.datetimeutils.utils;


import android.text.format.DateFormat;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class DateUtils {

    private static final String TAG = "DateUtils";

    public static final String DATE_FORMAT_1 = "hh:mm a";
    public static final String DATE_FORMAT_2 = "h:mm a";
    public static final String DATE_FORMAT_3 = "yyyy-MM-dd";
    public static final String DATE_FORMAT_4 = "dd-MMMM-yyyy";
    public static final String DATE_FORMAT_5 = "dd MMMM yyyy";
    public static final String DATE_FORMAT_6 = "dd MMMM yyyy zzzz";
    public static final String DATE_FORMAT_7 = "EEE, MMM d, ''yy";
    public static final String DATE_FORMAT_8 = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_FORMAT_9 = "h:mm a dd MMMM yyyy";
    public static final String DATE_FORMAT_10 = "K:mm a, z";
    public static final String DATE_FORMAT_11 = "hh 'o''clock' a, zzzz";
    public static final String DATE_FORMAT_12 = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
    public static final String DATE_FORMAT_13 = "E, dd MMM yyyy HH:mm:ss z";
    public static final String DATE_FORMAT_14 = "yyyy.MM.dd G 'at' HH:mm:ss z";
    public static final String DATE_FORMAT_15 = "yyyyy.MMMMM.dd GGG hh:mm aaa";
    public static final String DATE_FORMAT_16 = "EEE, d MMM yyyy HH:mm:ss Z";
    public static final String DATE_FORMAT_17 = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
    public static final String DATE_FORMAT_18 = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";


    public static String getCurrentDate() {
        SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_1);
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date today = Calendar.getInstance().getTime();
        return dateFormat.format(today);
    }

    public static String getCurrentTime() {
        SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_1);
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date today = Calendar.getInstance().getTime();
        return dateFormat.format(today);
    }

    /**
     * @param time        in milliseconds (Timestamp)
     * @param mDateFormat SimpleDateFormat
     * @return
     */
    public static String getDateTimeFromTimeStamp(Long time, String mDateFormat) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(mDateFormat);
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date dateTime = new Date(time);
        return dateFormat.format(dateTime);
    }

    /**
     * Get Timestamp from date and time
     *
     * @param mDateTime   datetime String
     * @param mDateFormat Date Format
     * @return
     * @throws ParseException
     */
    public static long getTimeStampFromDateTime(String mDateTime, String mDateFormat) throws ParseException {
        SimpleDateFormat dateFormat = new SimpleDateFormat(mDateFormat);
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date date = dateFormat.parse(mDateTime);
        return date.getTime();
    }

    /**
     * Return  datetime String from date object
     *
     * @param mDateFormat format of date
     * @param date        date object that you want to parse
     * @return
     */
    public static String formatDateTimeFromDate(String mDateFormat, Date date) {
        if (date == null) {
            return null;
        }
        return DateFormat.format(mDateFormat, date).toString();
    }

    /**
     *  Convert one date format string  to another date format string in android
     * @param inputDateFormat Input SimpleDateFormat
     * @param outputDateFormat Output SimpleDateFormat
     * @param inputDate  input Date String
     * @return
     * @throws ParseException
     */
    public static String formatDateFromDateString(String inputDateFormat, String outputDateFormat, String inputDate) throws ParseException {
        Date mParsedDate;
        String mOutputDateString;
        SimpleDateFormat mInputDateFormat = new SimpleDateFormat(inputDateFormat, java.util.Locale.getDefault());
        SimpleDateFormat mOutputDateFormat = new SimpleDateFormat(outputDateFormat, java.util.Locale.getDefault());
        mParsedDate = mInputDateFormat.parse(inputDate);
        mOutputDateString = mOutputDateFormat.format(mParsedDate);
        return mOutputDateString;

    }
}

Welcome, Buddy… In this android tutorials we demonstrate, Build a multi-language support in the android application. As per Android best practice, Application should be kept culture-specific resource. So that resource to be translated to the language based on current locale. In android term, it is called Internationalization and Localization

Localization is play very important role for app reach and user experience (especially GUI). Multi-language support increases your audience reach and provides a better user interface based on locale. Let’s make your Android app more usable and comfortable so more and more audience can use it

How do Internationalization and Localization work in android?

In, Android you can specify resources to the culture of the targeted audience. All content related resource is held in drawable and value folder in Android Project. In this tutorials as will give support of 3 languages Spanish, Hindi (hi_IN)and English(en_US)

1. Let’s build a sample app in 4 step

1.1 In the Create New Project, enter the following values:

Application Name: “Multi-Language App”// Replace name with your application
Company Domain: “com.androidwave.multilangauge” // replace package name here

Go Next and select target device, keep the default values and click Finish.
In Android, the project structure seems like below figure.

How locale work in android.
Locale Manager in Android

1.2 Add new resource file strings.xml for the Hindi language

Go to value resource folder and right click on got to the Value resource file.
Select locale from available qualifier clicks the right arrow. Select language and specific region (the region is not mandatory). Check below figure

Multi Language support in android
Android tutorials, android developer

Similarly create file for Spanish locale.

<resources>
    <string name="app_name">Multi Language App</string>
    <string name="navigation_drawer_open">Open navigation drawer</string>
    <string name="navigation_drawer_close">Close navigation drawer</string>
    <string name="nav_header_title">Android Wave</string>
    <string name="nav_header_subtitle">androidwave.com</string>
    <string name="nav_header_desc">Android &amp; iOS Developer Blog</string>
    <string name="action_settings">Settings</string>
    <string name="description_androidwave">We demonstrate android &amp; iOS app development tutorials for Firebase, Camera2 API, Exo Player, Youtube API, Dagger2, RxJava, MVVM, MVP, Realm, and more.</string>
    <string name="text_hindi" translatable="false">हिंदी</string>
    <string name="text_english" translatable="false">English</string>
    <string name="text_spanish" translatable="false">" Español"</string>

    <!-- Navigation Menu-->
    <string name="title_import">Import</string>
    <string name="title_gallery">Gallery</string>
    <string name="title_slideshow">Slideshow</string>
    <string name="title_tools">Tools</string>
    <string name="title_communication">Communicate</string>
    <string name="title_share">Share</string>
    <string name="title_send">Send</string>
</resources>

For Hindi

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">बहु भाषा ऐप</string>
    <string name="navigation_drawer_open">नेविगेशन ड्रावर को खोले</string>
    <string name="navigation_drawer_close">नेविगेशन ड्रावर को बंद करें </string>
    <string name="nav_header_title">एंड्रॉइड वेव</string>
    <string name="nav_header_subtitle">androidwave.com</string>
    <string name="nav_header_desc">एंड्रॉइड और आईओएस डेवलपर ब्लॉग</string>
    <string name="action_settings">सेटिंग</string>
    <string name="description_androidwave">हम फायरबेस, कैमरा 2 एपीआई, एक्सो प्लेयर, यूट्यूब एपीआई, डैगर 2, आरएक्सजेवा, एमवीवीएम, एमवीपी, दायरे, आदि के लिए एंड्रॉइड और आईओएस ऐप डेवलपमेंट ट्यूटोरियल का प्रदर्शन करते हैं।</string>
    <!-- Navigation Menu-->
    <string name="title_import">आयात</string>
    <string name="title_gallery">गैलरी</string>
    <string name="title_slideshow">स्लाइड शो</string>
    <string name="title_tools">उपकरण</string>
    <string name="title_communication">संवाद</string>
    <string name="title_share">शेयर</string>
    <string name="title_send">भेजना</string>
</resources>

For Spanish

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Aplicación multi idioma</string>
    <string name="navigation_drawer_open">Cajón de navegación abierto.</string>
    <string name="navigation_drawer_close">Cerrar el cajon de navegacion</string>
    <string name="nav_header_title">Android Wave</string>
    <string name="nav_header_subtitle">androidwave.com</string>
    <string name="nav_header_desc">Android &amp; Blog del desarrollador de iOS</string>
    <string name="action_settings">Ajustes</string>
    <string name="description_androidwave">Demostramos android &amp; Tutoriales de desarrollo de aplicaciones iOS para Firebase, Camera2 API, Exo Player, Youtube API, Dagger2, RxJava, MVVM, MVP, Realm y más</string>
    <!-- Navigation Menu-->
    <string name="title_import">Importar</string>
    <string name="title_gallery">Galería</string>
    <string name="title_slideshow">Diapositivas</string>
    <string name="title_tools">Herramientas</string>
    <string name="title_communication">Comunicar</string>
    <string name="title_share">Compartir</string>
    <string name="title_send">Enviar</string>
</resources>
The complete project hierarchy looks like this figure
Android Developer

3. Now build LocalManager.java utilities.

3.1. Define some specific constant key
    /**
     * For english locale
     */
    public static final String LANGUAGE_KEY_ENGLISH = "en";
    /**
     * for hindi locale
     */
    public static final String LANGUAGE_KEY_HINDI = "hi";
    /***
     * // for spanish locale
     */

    public static final String LANGUAGE_KEY_SPANISH = "es";
    /**
     *  SharedPreferences Key
     */
    private static final String LANGUAGE_KEY = "language_key";
3.2. Save locale preference in PreferenceManager

Build Getter and Setter of local, It’s Setter responsible for persisting data in SharedPreferences and Getter is responsible for pulling data from SharedPreferences.

/**
     * Get saved Locale from SharedPreferences
     * @param mContext current context
     * @return current locale key by default return english locale
     */
    public static String getLanguagePref(Context mContext) {
        SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
        return mPreferences.getString(LANGUAGE_KEY, LANGUAGE_KEY_ENGLISH);
    }

    /**
     *  set pref key
      * @param mContext
     * @param localeKey
     */
    private static void setLanguagePref(Context mContext, String localeKey) {
        SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
        mPreferences.edit().putString(LANGUAGE_KEY, localeKey).commit();
    }

Now set locale and update the resource in android.
 /**
     * update resource
     * @param context
     * @param language
     * @return
     */
    private static Context updateResources(Context context, String language) {
        Locale locale = new Locale(language);
        Locale.setDefault(locale);
        Resources res = context.getResources();
        Configuration config = new Configuration(res.getConfiguration());
        if (Build.VERSION.SDK_INT >= 17) {
            config.setLocale(locale);
            context = context.createConfigurationContext(config);
        } else {
            config.locale = locale;
            res.updateConfiguration(config, res.getDisplayMetrics());
        }
        return context;
    }

Finally the complete utilites class looks like below java class

package com.androidwave.multilanguage;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.preference.PreferenceManager;

import java.util.Locale;

public class LocaleManager {
    /**
     * For english locale
     */
    public static final String LANGUAGE_KEY_ENGLISH = "en";
    /**
     * for hindi locale
     */
    public static final String LANGUAGE_KEY_HINDI = "hi";
    /***
     * // for spanish locale
     */

    public static final String LANGUAGE_KEY_SPANISH = "es";
    /**
     *  SharedPreferences Key
     */
    private static final String LANGUAGE_KEY = "language_key";

    /**
     * set current pref locale
     * @param mContext
     * @return
     */
    public static Context setLocale(Context mContext) {
        return updateResources(mContext, getLanguagePref(mContext));
    }

    /**
     * Set new Locale with context
     * @param mContext
     * @param mLocaleKey
     * @return
     */
    public static Context setNewLocale(Context mContext, String mLocaleKey) {
        setLanguagePref(mContext, mLocaleKey);
        return updateResources(mContext, mLocaleKey);
    }

    /**
     * Get saved Locale from SharedPreferences
     * @param mContext current context
     * @return current locale key by default return english locale
     */
    public static String getLanguagePref(Context mContext) {
        SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
        return mPreferences.getString(LANGUAGE_KEY, LANGUAGE_KEY_ENGLISH);
    }

    /**
     *  set pref key
      * @param mContext
     * @param localeKey
     */
    private static void setLanguagePref(Context mContext, String localeKey) {
        SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
        mPreferences.edit().putString(LANGUAGE_KEY, localeKey).commit();
    }

    /**
     * update resource
     * @param context
     * @param language
     * @return
     */
    private static Context updateResources(Context context, String language) {
        Locale locale = new Locale(language);
        Locale.setDefault(locale);
        Resources res = context.getResources();
        Configuration config = new Configuration(res.getConfiguration());
        if (Build.VERSION.SDK_INT >= 17) {
            config.setLocale(locale);
            context = context.createConfigurationContext(config);
        } else {
            config.locale = locale;
            res.updateConfiguration(config, res.getDisplayMetrics());
        }
        return context;
    }

    /**
     * get current locale
     * @param res
     * @return
     */
    public static Locale getLocale(Resources res) {
        Configuration config = res.getConfiguration();
        return Build.VERSION.SDK_INT >= 24 ? config.getLocales().get(0) : config.locale;
    }
}

5. Locale Configration

Few minor configration have to need in activity and application class.

package com.androidwave.multilanguage;

import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

import static android.content.pm.PackageManager.GET_META_DATA;

public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        resetTitles();
    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(LocaleManager.setLocale(base));
    }

    protected void resetTitles() {
        try {
            ActivityInfo info = getPackageManager().getActivityInfo(getComponentName(), GET_META_DATA);
            if (info.labelRes != 0) {
                setTitle(info.labelRes);
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }

}

6. Implement Locale Configration in Application class

package com.androidwave.multilanguage;

import android.app.Application;
import android.content.Context;
import android.util.Log;

public class MultiLanguageApp extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(LocaleManager.setLocale(base));
    }

    @Override
    public void onConfigurationChanged(android.content.res.Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        LocaleManager.setLocale(this);
    }
}

Above all locale configration is complete and LocaleManager utilities is ready to use. In conclusion, just wrtie a single line code for change locale of application

 
    // For English
      LocaleManager.setNewLocale(this, LocaleManager.LANGUAGE_KEY_ENGLISH);
   // For Hindi
        LocaleManager.setNewLocale(this, LocaleManager.LANGUAGE_KEY_HINDI);
   // For Spanish
      LocaleManager.setNewLocale(this, LocaleManager.LANGUAGE_KEY_SPANISH);

For instance, for simple utities uses example.

package com.androidwave.multilanguage;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.design.widget.Snackbar;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

public class MainActivity extends BaseActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);
    }


    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch (id) {
            case R.id.local_english:
                LocaleManager.setNewLocale(this, LocaleManager.LANGUAGE_KEY_ENGLISH);
                recreate();
                return true;
            case R.id.local_hindi:
                LocaleManager.setNewLocale(this, LocaleManager.LANGUAGE_KEY_HINDI);
                recreate();
                return true;

            case R.id.local_spanish:
                LocaleManager.setNewLocale(this, LocaleManager.LANGUAGE_KEY_SPANISH);
                recreate();
                return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();
        switch (id) {
            case R.id.nav_camera:
                break;
            case R.id.nav_gallery:
                break;
            case R.id.nav_slideshow:
                break;
            case R.id.nav_manage:
                break;
            case R.id.nav_share:
                break;
            case R.id.nav_send:
                break;

            default:
                break;
        }
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

}

Download Sample Project- Android Multi Language Support Best Practices

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

1. Switch camera rear to front & front-rear in Camera2 API

In previous 2 blogs, I have explained Video Recoding using Camera2 API. Furtermore In this tutorials I’m explaing about swich camera front-rear via-varsa. As you we have build a CameraVideoFragment.java utility class.

2. Declare below variable in CameraVideoFragment.java.

    /* 0 forback camera 
     * 1 for front camera 
     * Initlity default camera is front camera    
     */
    public static final String CAMERA_FRONT = "1";
    public static final String CAMERA_BACK = "0";
    private String cameraId = CAMERA_FRONT;

3. Check available cameras

We can fetch all available cameras from Camera Manager instase by calling getCameraIdList .It’s return all available via getCameraIdList() (front/rear), then you can find the best camera that you want to use or suits your need.

You can get CameraCharacteristics by using camera ID. You can manage facing, resolution and filter also. In variable declear block we define front and back camera id.

In this tutorial, the front camera is the default camera.

   CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);

As above private String cameraId = CAMERA_FRONT; we set default camera is front camera, So cameraId have instance of front camera.

4. Furthermore we can Switch Camera usign below code

 public void switchCamera() {
        if (cameraId.equals(CAMERA_FRONT)) {
            cameraId = CAMERA_BACK;
            closeCamera();
            reopenCamera();


        } else if (cameraId.equals(CAMERA_BACK)) {
            cameraId = CAMERA_FRONT;
            closeCamera();
            reopenCamera();

        }
    }

After changing cameraId we just close the previous camera and reopen it.

 public void reopenCamera() {
        if (mTextureView.isAvailable()) {
            openCamera(mTextureView.getWidth(), mTextureView.getHeight());
        } else {
            mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
        }
    }

Finally just call switchCamera() method in child Fragment

Download Sample Project- Switch Camera in Camera2 API Android

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

Introduction

As usually youtube player API works well in Android in full-screen view. If you want to play a youtube video inside subview, it’s thrown illegal overlay exception for the reason that youtube API is not allowed any kind of overlay, padding, and margin inside the View. Same if you want to play a video in RecylerView similarly Twitter Player Card. In Twitter, Player Card video is played as WebView with some metadata. You no need to learn deep. We have on prebuilt lib for this.

If you facing illegal overlay issue in youtube player, You are in right place. Basically,
YouTube Android Player API is strictly prohibited overlay, So while you play in RecyclerView in then the video will be stopped after few second. Now I explain hows play a video in RecyclerView like twitter.
I’m using a library Android YouTube Player.

This is the fully open source library. The player is run on Webview within IFrame API, is easy to maintain and scalable.

Prerequisite

1. First of all, create a new project with the name YoutubePlayer.

Go to file menu => Create a new project => Enter a project name and packages name.

2. Add below dependency in build.gradle also

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'com.android.support:cardview-v7:27.1.1'
    implementation 'com.android.support:recyclerview-v7:27.1.1'
    // Image loading lib
    implementation('com.github.bumptech.glide:glide:4.7.1@aar') {
        transitive = true
    }
    // Youtube lib
    implementation 'com.github.PierfrancescoSoffritti:AndroidYouTubePlayer:7.0.1'
    // code generator for view
    implementation 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor "com.jakewharton:butterknife-compiler:8.8.1"
}
2.1 set compile option java sdk target 1.8 for using lambda expression.
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

3. Furthermore add uses permission in AndroidMenifest.xml

    <uses-permission android:name="android.permission.INTERNET" ></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" ></uses-permission>

The complete folder structure look like

4. Now create a Model class name with YoutubeVideo.java

Create a POJO with the following entity like – id, title, videoId, and imageUrl

package com.androidwave.youtuberecycler.model;

public class YoutubeVideo {
    private String title;
    private Long id;
    private String videoId;
    private String imageUrl;

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    public String getTitle() {
        return title;
    }

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

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getVideoId() {
        return videoId;
    }

    public void setVideoId(String videoId) {
        this.videoId = videoId;
    }
}

5. Create a Base RecyclerView.ViewHolder

Now I’m creating a generics file for BaseViewHolder which extends RecyclerView.ViewHolder. Which help a lot fetching current position of items in RecyclerView Adapter.

package com.androidwave.youtuberecycler.base;

import android.support.v7.widget.RecyclerView;
import android.view.View;

public abstract class BaseViewHolder extends RecyclerView.ViewHolder {

    private int mCurrentPosition;

    public BaseViewHolder(View itemView) {
        super(itemView);
    }

    protected abstract void clear();

    public void onBind(int position) {
        mCurrentPosition = position;
        clear();
    }

    public int getCurrentPosition() {
        return mCurrentPosition;
    }

}

6. Let’s create an item view for recycler adapter item_youtube_list.xml

The item view design seem like below
Player youtube video in like twitter
Player youtube video in RecyclerView

Let’s prepare item_youtube_list.xml RecylerView item layout. It seems like above image

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView 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"
    app:cardCornerRadius="6dp"
    app:cardElevation="6dp">

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/textViewTitle"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="16dp"
            android:layout_marginTop="16dp"
            android:letterSpacing="-0.02"
            android:lineSpacingExtra="5sp"
            android:text="TextView"
            android:textColor="@color/navy"
            android:textSize="18sp"
            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/imageViewItem"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:scaleType="matrix"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textViewTitle" />

        <com.pierfrancescosoffritti.youtubeplayer.player.YouTubePlayerView
            android:id="@+id/youtube_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:visibility="gone"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textViewTitle" />

        <ImageView
            android:id="@+id/btnPlay"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            app:layout_constraintBottom_toBottomOf="@+id/imageViewItem"
            app:layout_constraintEnd_toEndOf="@+id/imageViewItem"
            app:layout_constraintStart_toStartOf="@+id/imageViewItem"
            app:layout_constraintTop_toTopOf="@+id/imageViewItem"
            app:srcCompat="@drawable/ic_play_button" />
    </android.support.constraint.ConstraintLayout>

7. Create a new class YoutubeRecyclerAdapter.java for RecyclerView Adapter set data on item views

package com.androidwave.youtuberecycler.adapter;

import android.app.Activity;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.androidwave.youtuberecycler.R;
import com.androidwave.youtuberecycler.base.BaseViewHolder;
import com.androidwave.youtuberecycler.model.YoutubeVideo;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.pierfrancescosoffritti.youtubeplayer.player.AbstractYouTubePlayerListener;
import com.pierfrancescosoffritti.youtubeplayer.player.YouTubePlayerView;

import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;

public class YoutubeRecyclerAdapter extends RecyclerView.Adapter<BaseViewHolder> {


    public static final int VIEW_TYPE_NORMAL = 1;

    private List<YoutubeVideo> mYoutubeVideos;
    DisplayMetrics displayMetrics = new DisplayMetrics();

    public YoutubeRecyclerAdapter(List<YoutubeVideo> youtubeVideos) {
        mYoutubeVideos = youtubeVideos;
    }

    @NonNull
    @Override
    public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_youtube_list, parent, 
               false));
    }

    @Override
    public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
        holder.onBind(position);
    }


    @Override
    public int getItemViewType(int position) {
        return VIEW_TYPE_NORMAL;
    }

    @Override
    public int getItemCount() {
        if (mYoutubeVideos != null && mYoutubeVideos.size() > 0) {
            return mYoutubeVideos.size();
        } else {
            return 1;
        }
    }

    public void setItems(List<YoutubeVideo> youtubeVideos) {
        mYoutubeVideos = youtubeVideos;
        notifyDataSetChanged();
    }


    public class ViewHolder extends BaseViewHolder {
        @BindView(R.id.textViewTitle)
        TextView textWaveTitle;
        @BindView(R.id.btnPlay)
        ImageView playButton;

        @BindView(R.id.imageViewItem)
        ImageView imageViewItems;
        @BindView(R.id.youtube_view)
        YouTubePlayerView youTubePlayerView;

        public ViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }

        protected void clear() {

        }

        public void onBind(int position) {
            super.onBind(position);
            final YoutubeVideo mYoutubeVideo = mYoutubeVideos.get(position);
            ((Activity) itemView.getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
            int width = displayMetrics.widthPixels;
            if (mYoutubeVideo.getTitle() != null)
                textWaveTitle.setText(mYoutubeVideo.getTitle());

            if (mYoutubeVideo.getImageUrl() != null) {
                Glide.with(itemView.getContext())
                        .load(mYoutubeVideo.getImageUrl()).
                        apply(new RequestOptions().override(width - 36, 200))
                        .into(imageViewItems);
            }
            imageViewItems.setVisibility(View.VISIBLE);
            playButton.setVisibility(View.VISIBLE);
            youTubePlayerView.setVisibility(View.GONE);

            playButton.setOnClickListener(view -> {
                imageViewItems.setVisibility(View.GONE);
                youTubePlayerView.setVisibility(View.VISIBLE);
                playButton.setVisibility(View.GONE);
                youTubePlayerView.initialize(initializedYouTubePlayer -> initializedYouTubePlayer.addListener(new 
                AbstractYouTubePlayerListener() {
                    @Override
                    public void onReady() {
                        initializedYouTubePlayer.loadVideo(mYoutubeVideo.getVideoId(), 0);
                    }
                }), true);
            });
        }
    }
}

8. Comes in main activity and inject view using ButterKnief


    @BindView(R.id.recyclerViewFeed)
    RecyclerView recyclerViewFeed;

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

9. Now prepare list item data

  private List<YoutubeVideo> prepareList() {
        ArrayList mYoutubeVideo = new ArrayList();
        // add first item
        YoutubeVideo video1 = new YoutubeVideo();
        video1.setId(1l);
        video1.setImageUrl("https://i.ytimg.com/vi/zI-Pux4uaqM/maxresdefault.jpg");
        video1.setTitle("Thugs Of Hindostan - Official Trailer | Amitabh Bachchan | Aamir Khan | Katrina Kaif | 
                         Fatima");
        video1.setVideoId("zI-Pux4uaqM");
        mYoutubeVideo.add(video1);

        // add second item
        YoutubeVideo video2 = new YoutubeVideo();
        video2.setId(2l);
        video2.setImageUrl("https://i.ytimg.com/vi/8ZK_S-46KwE/maxresdefault.jpg");
        video2.setTitle("Colors for Children to Learning with Baby Fun Play with Color Balls Dolphin Slider Toy Set 
                         Kids Edu");
        video2.setVideoId("8ZK_S-46KwE");
        mYoutubeVideo.add(video2);

        // add third item
        YoutubeVideo video3 = new YoutubeVideo();
        video3.setId(3l);
        video3.setImageUrl("https://i.ytimg.com/vi/8czMWUH7vW4/hqdefault.jpg");
        video3.setTitle("Air Hostess Accepts Marriage Proposal Mid-Air, Airline Fires her.");
        video3.setVideoId("8czMWUH7vW4");
        mYoutubeVideo.add(video3);

        // add four item
        YoutubeVideo video4 = new YoutubeVideo();
        video4.setId(4l);
        video4.setImageUrl("https://i.ytimg.com/vi/YrQVYEb6hcc/maxresdefault.jpg");
        video4.setTitle("EXPERIMENT Glowing 1000 degree METAL BALL vs Gunpowder (100 grams)");
        video4.setVideoId("YrQVYEb6hcc");
        mYoutubeVideo.add(video4);

        // add four item
        YoutubeVideo video5 = new YoutubeVideo();
        video5.setId(5l);
        video5.setImageUrl("https://i.ytimg.com/vi/S84Fuo2rGoY/maxresdefault.jpg");
        video5.setTitle("What happened after Jauhar of Padmavati");
        video5.setVideoId("S84Fuo2rGoY");
        mYoutubeVideo.add(video5);

        mYoutubeVideo.add(video1);
        mYoutubeVideo.add(video2);
        mYoutubeVideo.add(video3);
        mYoutubeVideo.add(video4);
        return mYoutubeVideo;
    }

10. Set the adapter over RecyclerView.

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        // prepare data for list
        List<YoutubeVideo> youtubeVideos = prepareList();
        mRecyclerAdapter = new YoutubeRecyclerAdapter(youtubeVideos);
        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
        recyclerViewFeed.setLayoutManager(mLayoutManager);
        recyclerViewFeed.setItemAnimator(new DefaultItemAnimator());
        recyclerViewFeed.setAdapter(mRecyclerAdapter);
    }

11. The finally MainActivity.java source seems like below

package com.androidwave.youtuberecycler;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import com.androidwave.youtuberecycler.adapter.YoutubeRecyclerAdapter;
import com.androidwave.youtuberecycler.model.YoutubeVideo;

import java.util.ArrayList;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.recyclerViewFeed)
    RecyclerView recyclerViewFeed;

    YoutubeRecyclerAdapter mRecyclerAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        // prepare data for list
        List<YoutubeVideo> youtubeVideos = prepareList();
        mRecyclerAdapter = new YoutubeRecyclerAdapter(youtubeVideos);
        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
        recyclerViewFeed.setLayoutManager(mLayoutManager);
        recyclerViewFeed.setItemAnimator(new DefaultItemAnimator());
        recyclerViewFeed.setAdapter(mRecyclerAdapter);
    }

    private List<YoutubeVideo> prepareList() {
        ArrayList mYoutubeVideo = new ArrayList();
        // add first item
        YoutubeVideo video1 = new YoutubeVideo();
        video1.setId(1l);
        video1.setImageUrl("https://i.ytimg.com/vi/zI-Pux4uaqM/maxresdefault.jpg");
        video1.setTitle("Thugs Of Hindostan - Official Trailer | Amitabh Bachchan | Aamir Khan | Katrina Kaif | 
                         Fatima");
        video1.setVideoId("zI-Pux4uaqM");
        mYoutubeVideo.add(video1);

        // add second item
        YoutubeVideo video2 = new YoutubeVideo();
        video2.setId(2l);
        video2.setImageUrl("https://i.ytimg.com/vi/8ZK_S-46KwE/maxresdefault.jpg");
        video2.setTitle("Colors for Children to Learning with Baby Fun Play with Color Balls Dolphin Slider Toy Set 
                         Kids Edu");
        video2.setVideoId("8ZK_S-46KwE");
        mYoutubeVideo.add(video2);

        // add third item
        YoutubeVideo video3 = new YoutubeVideo();
        video3.setId(3l);
        video3.setImageUrl("https://i.ytimg.com/vi/8czMWUH7vW4/hqdefault.jpg");
        video3.setTitle("Air Hostess Accepts Marriage Proposal Mid-Air, Airline Fires her.");
        video3.setVideoId("8czMWUH7vW4");
        mYoutubeVideo.add(video3);

        // add four item
        YoutubeVideo video4 = new YoutubeVideo();
        video4.setId(4l);
        video4.setImageUrl("https://i.ytimg.com/vi/YrQVYEb6hcc/maxresdefault.jpg");
        video4.setTitle("EXPERIMENT Glowing 1000 degree METAL BALL vs Gunpowder (100 grams)");
        video4.setVideoId("YrQVYEb6hcc");
        mYoutubeVideo.add(video4);

        // add four item
        YoutubeVideo video5 = new YoutubeVideo();
        video5.setId(5l);
        video5.setImageUrl("https://i.ytimg.com/vi/S84Fuo2rGoY/maxresdefault.jpg");
        video5.setTitle("What happened after Jauhar of Padmavati");
        video5.setVideoId("S84Fuo2rGoY");
        mYoutubeVideo.add(video5);

        mYoutubeVideo.add(video1);
        mYoutubeVideo.add(video2);
        mYoutubeVideo.add(video3);
        mYoutubeVideo.add(video4);
        return mYoutubeVideo;
    }
}
Download Sample Project- Youtube player in RecyclerView in Android

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

We demonstrate Video Streaming from the server using the ExoPlayer. ExoPlayer is used in Youtube app as well. Read the full article for details

ExoPlayer is an open-source project. This is not a part of Android SDK. In Android, Exo player is application level media player. ExoPlayer’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.

Lets Demonstrate Exo player

1. Go to file menu create a new project.

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

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.android.support:design:28.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    // 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, butterknife is injected of view in the java file.

3. Create a layout activity_main.xml and add two Button like below code.

<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_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        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_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="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

In 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:
        
                break;
            case R.id.buttonPlayDefaultVideo:
           
                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);
        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

      Intent mIntent = ExoPlayerActivity.getStartIntent(this, VideoPlayerConfig.DEFAULT_VIDEO_URL);
                startActivity(mIntent);

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

  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 {
 
    public static final int MIN_BUFFER_DURATION = 3000;
    public static final int MAX_BUFFER_DURATION = 5000;
    public static final int MIN_PLAYBACK_START_BUFFER = 1500;
    public static final int MIN_PLAYBACK_RESUME_BUFFER = 5000;

    public static final String DEFAULT_VIDEO_URL = "https://androidwave.com/wp-content/uploads/2018/09/exo-player-androidwave-com-match-highlight.mp4";
}

7. Desgin an activity_exo_player.xml View with few component e.g. Progressbar(buffering indicator), ExoPlayer View

<?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_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="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_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:layout_marginLeft="16dp" />
</android.support.constraint.ConstraintLayout>

8.Create ExoPlayerActivity.Java

Go to create an Activity and set content layout is activity_exo_player. 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;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_exo_player);
        ButterKnife.bind(this);

   }
8.1. Define some variable and do some fullscreen configration
    String videoUri;
    SimpleExoPlayer player;
    Handler mHandler;
    Runnable mRunnable;

        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);
        getSupportActionBar().hide();
8.2. We are using vector icon so enable
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
8.3. Now Inilized video with configration
  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 We Check player state in below methods
 @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 full activity code seems like this

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

    }
}
Download Sample Project- Video Streaming ExoPlayer in Android

 
If you have any query, 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 a previous blog, 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 miss in this article download full source and a working sample apk is there.

Before starting to think in 2 min what we needed to solve.

  • First problem is managing ExoPlayer playback
  • Second is we need to know which view on the scrolled list is currently active. So we can initialize and release ExoPlayer intense in RecylerView
  • Create Project

    Simply go to file menu and create a project and add the ExoPlayer dependency in build.gradle.

        implementation 'com.google.android.exoplayer:exoplayer:2.7.3'
        implementation 'org.jsoup:jsoup:1.10.3'
    

    The complete project hierarchy seems like image

    ExoPlayer in RecyclerView in android tutorials
    ExoPlayer in RecyclerView in android

    Create a custom RecyclerView with ExoPlayerRecyclerView name

    Now comes in first problem is

    how to manage ExoPlayer playback?

    Create a class ExoPlayerRecyclerView.java which extend RecyclerView
    Furthermore we fetch start position and end position of view from LayoutManager after that calculate target position.
    Here now on target position, we will add ExoPlayerView and set ExoPlayer.

      //play the video in the row
        public void playVideo() {
            int startPosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
            int endPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
    
            if (endPosition - startPosition > 1) {
                endPosition = startPosition + 1;
            }
    
            if (startPosition < 0 || endPosition < 0) {
                return;
            }
    
            int targetPosition;
            if (startPosition != endPosition) {
                int startPositionVideoHeight = getVisibleVideoSurfaceHeight(startPosition);
                int endPositionVideoHeight = getVisibleVideoSurfaceHeight(endPosition);
                targetPosition = startPositionVideoHeight > endPositionVideoHeight ? startPosition : endPosition;
            } else {
                targetPosition = startPosition;
            }
    
            if (targetPosition < 0 || targetPosition == playPosition) {
                return;
            }
            playPosition = targetPosition;
            if (videoSurfaceView == null) {
                return;
            }
            videoSurfaceView.setVisibility(INVISIBLE);
            removeVideoView(videoSurfaceView);
    
            // get target View targetPosition in RecyclerView
            int at = targetPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
    
            View child = getChildAt(at);
            if (child == null) {
                return;
            }
    
            VideoRecyclerViewAdapter.ViewHolder holder
                    = (VideoRecyclerViewAdapter.ViewHolder) child.getTag();
            if (holder == null) {
                playPosition = -1;
                return;
            }
            mCoverImage = holder.mCover;
            FrameLayout frameLayout = holder.itemView.findViewById(R.id.video_layout);
            frameLayout.addView(videoSurfaceView);
            addedVideo = true;
            rowParent = holder.itemView;
            videoSurfaceView.requestFocus();
            // Bind the player to the view.
            videoSurfaceView.setPlayer(player);
    
            // Measures bandwidth during playback. Can be null if not required.
            DefaultBandwidthMeter defaultBandwidthMeter = 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);
            }
    
    
        }
    

    Now comes in the second problem get visible Video Surface scrolled list.

      private int getVisibleVideoSurfaceHeight(int playPosition) {
            int at = playPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
    
            View child = getChildAt(at);
            if (child == null) {
                return 0;
            }
    
            int[] location01 = new int[2];
            child.getLocationInWindow(location01);
    
            if (location01[1] < 0) {
                return location01[1] + videoSurfaceDefaultHeight;
            } else {
                return screenDefaultHeight - location01[1];
            }
        }
    

    The complete class code is below

    package com.androidwave.exoplayer.ui;
    
    import android.content.Context;
    import android.graphics.Point;
    import android.net.Uri;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.util.AttributeSet;
    import android.view.Display;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.WindowManager;
    import android.widget.AbsListView;
    import android.widget.FrameLayout;
    import android.widget.ImageView;
    
    import com.androidwave.exoplayer.R;
    import com.androidwave.exoplayer.adapter.VideoRecyclerViewAdapter;
    import com.androidwave.exoplayer.model.VideoInfo;
    import com.androidwave.exoplayer.utils.VideoPlayerConfig;
    import com.google.android.exoplayer2.DefaultLoadControl;
    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.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.DefaultAllocator;
    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.List;
    
    public class ExoPlayerRecyclerView extends RecyclerView {
    
        private List<VideoInfo> videoInfoList = new ArrayList<>();
        private int videoSurfaceDefaultHeight = 0;
        private int screenDefaultHeight = 0;
        SimpleExoPlayer player;
        //surface view for playing video
        private PlayerView videoSurfaceView;
        private ImageView mCoverImage;
        private Context appContext;
    
    
        /**
         * the position of playing video
         */
        private int playPosition = -1;
    
        private boolean addedVideo = false;
        private View rowParent;
    
        /**
         * {@inheritDoc}
         *
         * @param context
         */
        public ExoPlayerRecyclerView(Context context) {
            super(context);
            initialize(context);
        }
    
        /**
         * {@inheritDoc}
         *
         * @param context
         * @param attrs
         */
        public ExoPlayerRecyclerView(Context context,
                                     AttributeSet attrs) {
            super(context, attrs);
            initialize(context);
        }
    
        /**
         * {@inheritDoc}
         *
         * @param context
         * @param attrs
         * @param defStyleAttr
         */
        public ExoPlayerRecyclerView(Context context,
                                     AttributeSet attrs,
                                     int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initialize(context);
        }
    
        public void setVideoInfoList(List<VideoInfo> videoInfoList) {
            this.videoInfoList = videoInfoList;
    
        }
    
    
        /**
         * prepare for video play
         */
        //remove the player from the row
        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);
                addedVideo = false;
            }
    
        }
    
        //play the video in the row
        public void playVideo() {
            int startPosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
            int endPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
    
            if (endPosition - startPosition > 1) {
                endPosition = startPosition + 1;
            }
    
            if (startPosition < 0 || endPosition < 0) {
                return;
            }
    
            int targetPosition;
            if (startPosition != endPosition) {
                int startPositionVideoHeight = getVisibleVideoSurfaceHeight(startPosition);
                int endPositionVideoHeight = getVisibleVideoSurfaceHeight(endPosition);
                targetPosition = startPositionVideoHeight > endPositionVideoHeight ? startPosition : endPosition;
            } else {
                targetPosition = startPosition;
            }
    
            if (targetPosition < 0 || targetPosition == playPosition) {
                return;
            }
            playPosition = targetPosition;
            if (videoSurfaceView == null) {
                return;
            }
            videoSurfaceView.setVisibility(INVISIBLE);
            removeVideoView(videoSurfaceView);
    
            // get target View targetPosition in RecyclerView
            int at = targetPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
    
            View child = getChildAt(at);
            if (child == null) {
                return;
            }
    
            VideoRecyclerViewAdapter.ViewHolder holder
                    = (VideoRecyclerViewAdapter.ViewHolder) child.getTag();
            if (holder == null) {
                playPosition = -1;
                return;
            }
            mCoverImage = holder.mCover;
            FrameLayout frameLayout = holder.itemView.findViewById(R.id.video_layout);
            frameLayout.addView(videoSurfaceView);
            addedVideo = true;
            rowParent = holder.itemView;
            videoSurfaceView.requestFocus();
            // Bind the player to the view.
            videoSurfaceView.setPlayer(player);
    
            // Measures bandwidth during playback. Can be null if not required.
            DefaultBandwidthMeter defaultBandwidthMeter = 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);
            }
    
    
        }
    
        private int getVisibleVideoSurfaceHeight(int playPosition) {
            int at = playPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
    
            View child = getChildAt(at);
            if (child == null) {
                return 0;
            }
    
            int[] location01 = new int[2];
            child.getLocationInWindow(location01);
    
            if (location01[1] < 0) {
                return location01[1] + videoSurfaceDefaultHeight;
            } else {
                return screenDefaultHeight - location01[1];
            }
        }
    
    
        private void initialize(Context context) {
    
            appContext = context.getApplicationContext();
            Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
            Point point = new Point();
            display.getSize(point);
            videoSurfaceDefaultHeight = point.x;
    
            screenDefaultHeight = point.y;
            videoSurfaceView = new PlayerView(appContext);
            videoSurfaceView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM);
    
            BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
            TrackSelection.Factory videoTrackSelectionFactory =
                    new AdaptiveTrackSelection.Factory(bandwidthMeter);
            TrackSelector trackSelector =
                    new DefaultTrackSelector(videoTrackSelectionFactory);
            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);
    
            // 2. Create the player
            player = ExoPlayerFactory.newSimpleInstance(appContext, trackSelector, loadControl);
            // Bind the player to the view.
            videoSurfaceView.setUseController(false);
            videoSurfaceView.setPlayer(player);
    
            addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                    if (newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                        playVideo();
                    }
                }
    
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                }
            });
    
            addOnChildAttachStateChangeListener(new OnChildAttachStateChangeListener() {
                @Override
                public void onChildViewAttachedToWindow(View view) {
    
                }
    
                @Override
                public void onChildViewDetachedFromWindow(View view) {
                    if (addedVideo && rowParent != null && rowParent.equals(view)) {
                        removeVideoView(videoSurfaceView);
                        playPosition = -1;
                        videoSurfaceView.setVisibility(INVISIBLE);
                    }
    
                }
            });
            player.addListener(new Player.EventListener() {
                @Override
                public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
    
                }
    
                @Override
                public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
    
                }
    
                @Override
                public void onLoadingChanged(boolean isLoading) {
    
                }
    
                @Override
                public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
                    switch (playbackState) {
    
                        case Player.STATE_BUFFERING:
                            //   videoSurfaceView.setAlpha(0.5f);
                            break;
                        case Player.STATE_ENDED:
                            player.seekTo(0);
                            break;
                        case Player.STATE_IDLE:
    
                            break;
                        case Player.STATE_READY:
                            videoSurfaceView.setVisibility(VISIBLE);
                            videoSurfaceView.setAlpha(1);
                            mCoverImage.setVisibility(GONE);
    
                            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 onPausePlayer() {
            if (videoSurfaceView != null) {
                removeVideoView(videoSurfaceView);
                player.release();
                videoSurfaceView = null;
            }
        }
    
        public void onRestartPlayer() {
            if (videoSurfaceView == null) {
                playPosition = -1;
                playVideo();
            }
        }
    
        /**
         * release memory
         */
        public void onRelease() {
    
            if (player != null) {
                player.release();
                player = null;
            }
    
            rowParent = null;
        }
    
    
    }
    
    

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

    Create a VideoInfo.java model class

    public class VideoInfo {
        private int mId;
        private String mTitle;
        private String mUrl;
        private String mCoverUrl;
        private String mUserHandle;
    
        public String getUserHandle() {
            return mUserHandle;
        }
    
        public void setUserHandle(String mUserHandle) {
            this.mUserHandle = mUserHandle;
        }
    
        public int getId() {
            return mId;
        }
    
        public void setId(int mId) {
            this.mId = mId;
        }
    
        public String getTitle() {
            return mTitle;
        }
    
        public void setTitle(String mTitle) {
            this.mTitle = mTitle;
        }
    
        public String getUrl() {
            return mUrl;
        }
    
        public void setUrl(String mUrl) {
            this.mUrl = mUrl;
        }
    
        public String getCoverUrl() {
            return mCoverUrl;
        }
    
        public void setCoverUrl(String mCoverUrl) {
            this.mCoverUrl = mCoverUrl;
        }
    } 
    

    Create a RecyclerView adapter and bind views with holder

    package com.androidwave.exoplayer.adapter;
    
    import android.support.annotation.NonNull;
    import android.support.v7.widget.RecyclerView;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.Button;
    import android.widget.FrameLayout;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    import com.androidwave.exoplayer.R;
    import com.androidwave.exoplayer.model.VideoInfo;
    import com.androidwave.exoplayer.ui.BaseViewHolder;
    import com.bumptech.glide.Glide;
    import com.bumptech.glide.request.RequestOptions;
    
    import java.util.List;
    
    import butterknife.BindView;
    import butterknife.ButterKnife;
    import butterknife.OnClick;
    
    public class VideoRecyclerViewAdapter extends RecyclerView.Adapter<BaseViewHolder> {
    
        public static final int VIEW_TYPE_EMPTY = 0;
        public static final int VIEW_TYPE_NORMAL = 1;
    
        private List<VideoInfo> mInfoList;
    
        public VideoRecyclerViewAdapter(List<VideoInfo> infoList) {
            mInfoList = infoList;
        }
    
        @NonNull
        @Override
        public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            switch (viewType) {
                case VIEW_TYPE_NORMAL:
                    return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false));
                case VIEW_TYPE_EMPTY:
                    return new EmptyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_empty_view, parent, false));
                default:
                    return null;
            }
        }
    
        @Override
        public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
            holder.onBind(position);
        }
    
    
        @Override
        public int getItemViewType(int position) {
            if (mInfoList != null && mInfoList.size() > 0) {
                return VIEW_TYPE_NORMAL;
            } else {
                return VIEW_TYPE_EMPTY;
            }
        }
    
        @Override
        public int getItemCount() {
            if (mInfoList != null && mInfoList.size() > 0) {
                return mInfoList.size();
            } else {
                return 1;
            }
        }
    
    
    
        public void onRelease() {
            if (mInfoList != null) {
                mInfoList.clear();
                mInfoList = null;
            }
        }
    
        public class ViewHolder extends BaseViewHolder {
            @BindView(R.id.textViewTitle)
            TextView textViewTitle;
            @BindView(R.id.userHandle)
            TextView userHandle;
            @BindView(R.id.video_layout)
            public FrameLayout videoLayout;
            @BindView(R.id.cover)
            public ImageView mCover;
            public final View parent;
    
    
            public ViewHolder(View itemView) {
                super(itemView);
                ButterKnife.bind(this, itemView);
                parent = itemView;
            }
    
            protected void clear() {
    
            }
    
            public void onBind(int position) {
                super.onBind(position);
                parent.setTag(this);
                VideoInfo videoInfo = mInfoList.get(position);
                textViewTitle.setText(videoInfo.getTitle());
                userHandle.setText(videoInfo.getUserHandle());
                Glide.with(itemView.getContext())
                        .load(videoInfo.getCoverUrl()).apply(new RequestOptions().optionalCenterCrop())
                        .into(mCover);
            }
        }
    
        public class EmptyViewHolder extends BaseViewHolder {
    
            @BindView(R.id.btn_retry)
            Button retryButton;
    
            @BindView(R.id.tv_message)
            TextView messageTextView;
    
            public EmptyViewHolder(View itemView) {
                super(itemView);
                ButterKnife.bind(this, itemView);
                itemView.setVisibility(View.GONE);
            }
    
            @Override
            protected void clear() {
    
            }
    
            @OnClick(R.id.btn_retry)
            void onRetryClick() {
    
            }
        }
    }
    
    

    Now prepare video array list

       
      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);
    
        }
    

    Finally main actvity 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.VideoRecyclerViewAdapter;
    import com.androidwave.exoplayer.model.VideoInfo;
    import com.androidwave.exoplayer.ui.ExoPlayerRecyclerView;
    import com.androidwave.exoplayer.utils.DividerItemDecoration;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import butterknife.BindView;
    import butterknife.ButterKnife;
    
    import static android.widget.LinearLayout.VERTICAL;
    
    public class MainActivity extends AppCompatActivity {
    
        @BindView(R.id.recyclerViewFeed)
        ExoPlayerRecyclerView recyclerViewFeed;
    
        private List<VideoInfo> videoInfoList = new ArrayList<>();
        private VideoRecyclerViewAdapter mAdapter;
        private boolean firstTime = true;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
            prepareVideoList();
            recyclerViewFeed.setVideoInfoList(videoInfoList);
            mAdapter = new VideoRecyclerViewAdapter(videoInfoList);
            recyclerViewFeed.setLayoutManager(new LinearLayoutManager(this, VERTICAL, false));
            Drawable dividerDrawable = ContextCompat.getDrawable(this, R.drawable.divider_drawable);
            recyclerViewFeed.addItemDecoration(new DividerItemDecoration(dividerDrawable));
            recyclerViewFeed.setItemAnimator(new DefaultItemAnimator());
            recyclerViewFeed.setAdapter(mAdapter);
    
            if (firstTime) {
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        recyclerViewFeed.playVideo();
                    }
                });
                firstTime = false;
            }
    
        }
    
        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);
    
        }
    
        @Override
        protected void onPause() {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    recyclerViewFeed.onPausePlayer();
                }
            });
            super.onPause();
        }
    
        @Override
        protected void onResume() {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    recyclerViewFeed.onRestartPlayer();
                }
            });
            super.onResume();
        }
    
        @Override
        protected void onDestroy() {
            if(recyclerViewFeed!=null)
            recyclerViewFeed.onRelease();
            super.onDestroy();
        }
    }
    
    
    Download Sample Project- ExoPlayer in RecyclerView in Android

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

    Introduction

    If you facing an issue in video recording in camera2 APIs in Android. Almost, you are in right place. Camera2 APIs is not working in well in few devices like Samsung, Nexus 5, etc.

    Mostly the of followings issue has occurred in Camera2 APIs

    • The recording is successfully complete but while you play this file video will freeze in the first frame.
    • Sometimes you feel the video appears to be frozen in playback or recorded video playback video and audio is out of Sync
    • First audio plays completely and after then the video animates after the audio completes. If you look at the video playback controls the video starts when it looks like the playback is at the end of the video

    So all the above issue is the same.

    Its happen due to audio and video frame out of sync. Now come on solution We need to sync audio and video frame.

    1. Prerqusion

    If you want to no more about Camera2 APIs, Please go to my previous blog Camera2VideoRecording. This blog i have build play and plug solution for Camera2 APIs.

    2. Add mp4parser dependency in build.gradle

    dependencies {
        //Video Parser
        implementation 'com.googlecode.mp4parser:isoparser:1.1.22'
    }
    

    3. In the previous exampleVideo Recording in Camera2 API in CameraFragment.java.

    While we call startRecordingVideo() Utility methods. The file created in local storage get this file using getCurrentFile() methods.

                        startRecordingVideo();
                       //Receive out put file here
                        mOutputFilePath = getCurrentFile().getAbsolutePath();
    

    mOutputFilePath is the path out the recorded file. Now fetch audio and video tracks check audio and video delta. if delta is greater then 10000 means audio and video track is out of sync. You are thinking why we will take 10000 here,10000 seems sufficient since for 30 fps the normal delta is about 3000. If the frame is out of sync then merge these track using MP$ Parser lib. The code looks like

     private String parseVideo(String mFilePath) throws IOException {
            DataSource channel = new FileDataSourceImpl(mFilePath);
            IsoFile isoFile = new IsoFile(channel);
            List<TrackBox> trackBoxes = isoFile.getMovieBox().getBoxes(TrackBox.class);
            boolean isError = false;
            for (TrackBox trackBox : trackBoxes) {
                TimeToSampleBox.Entry firstEntry = trackBox.getMediaBox().getMediaInformationBox().getSampleTableBox().getTimeToSampleBox().getEntries().get(0);
                // Detect if first sample is a problem and fix it in isoFile
                // This is a hack. The audio deltas are 1024 for my files, and video deltas about 3000
                // 10000 seems sufficient since for 30 fps the normal delta is about 3000
                if (firstEntry.getDelta() > 10000) {
                    isError = true;
                    firstEntry.setDelta(3000);
                }
            }
            File file = getOutputMediaFile();
            String filePath = file.getAbsolutePath();
            if (isError) {
                Movie movie = new Movie();
                for (TrackBox trackBox : trackBoxes) {
                    movie.addTrack(new Mp4TrackImpl(channel.toString() + "[" + trackBox.getTrackHeaderBox().getTrackId() + "]", trackBox));
                }
                movie.setMatrix(isoFile.getMovieBox().getMovieHeaderBox().getMatrix());
                Container out = new DefaultMp4Builder().build(movie);
    
                //delete file first!
                FileChannel fc = new RandomAccessFile(filePath, "rw").getChannel();
                out.writeContainer(fc);
                fc.close();
                Log.d(TAG, "Finished correcting raw video");
                return filePath;
            }
            return mFilePath;
        } 
    

    getOutputMediaFile() mothods create a new file and return File Object.

        /**
         * Create directory and return file
         * returning video file
         */
        private File getOutputMediaFile() {
            // External sdcard file location
            File mediaStorageDir = new File(Environment.getExternalStorageDirectory(),
                    VIDEO_DIRECTORY_NAME);
            // Create storage directory if it does not exist
            if (!mediaStorageDir.exists()) {
                if (!mediaStorageDir.mkdirs()) {
                    Log.d(TAG, "Oops! Failed create "
                            + VIDEO_DIRECTORY_NAME + " directory");
                    return null;
                }
            }
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
                    Locale.getDefault()).format(new Date());
            File mediaFile;
    
            mediaFile = new File(mediaStorageDir.getPath() + File.separator
                    + "VID_" + timeStamp + ".mp4");
            return mediaFile;
        }
    

    Therefore final fragment looks like this

    **
     * A simple {@link Fragment} subclass.
     * Use the {@link CameraFragment#newInstance} factory method to
     * create an instance of this fragment.
     */
    public class CameraFragment extends CameraVideoFragment {
    
        private static final String TAG = "CameraFragment";
        private static final String VIDEO_DIRECTORY_NAME = "AndroidWave";
        @BindView(R.id.mTextureView)
        AutoFitTextureView mTextureView;
        @BindView(R.id.mRecordVideo)
        ImageView mRecordVideo;
        @BindView(R.id.mVideoView)
        VideoView mVideoView;
        @BindView(R.id.mPlayVideo)
        ImageView mPlayVideo;
        Unbinder unbinder;
        private String mOutputFilePath;
    
    
        public CameraFragment() {
            // Required empty public constructor
        }
    
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         */
    
    
        public static CameraFragment newInstance() {
            CameraFragment fragment = new CameraFragment();
            Bundle args = new Bundle();
            fragment.setArguments(args);
            return fragment;
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            // Inflate the layout for this fragment
            View view = inflater.inflate(R.layout.fragment_camera, container, false);
            unbinder = ButterKnife.bind(this, view);
            return view;
        }
    
        @Override
        public int getTextureResource() {
            return R.id.mTextureView;
        }
    
        @Override
        protected void setUp(View view) {
    
        }
    
        @OnClick({R.id.mRecordVideo, R.id.mPlayVideo})
        public void onViewClicked(View view) {
            switch (view.getId()) {
                case R.id.mRecordVideo:
                    /**
                     * If media is not recoding then start recording else stop recording
                     */
                    if (mIsRecordingVideo) {
                        try {
                            stopRecordingVideo();
                            prepareViews();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
    
                    } else {
                        startRecordingVideo();
                        mRecordVideo.setImageResource(R.drawable.ic_stop);
                        //Receive out put file here
                        mOutputFilePath = getCurrentFile().getAbsolutePath();
                    }
                    break;
                case R.id.mPlayVideo:
                    mVideoView.start();
                    mPlayVideo.setVisibility(View.GONE);
                    break;
            }
        }
    
        private void prepareViews() {
            if (mVideoView.getVisibility() == View.GONE) {
                mVideoView.setVisibility(View.VISIBLE);
                mPlayVideo.setVisibility(View.VISIBLE);
                mTextureView.setVisibility(View.GONE);
                try {
                    setMediaForRecordVideo();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private void setMediaForRecordVideo() throws IOException {
            mOutputFilePath = parseVideo(mOutputFilePath);
            // Set media controller
            mVideoView.setMediaController(new MediaController(getActivity()));
            mVideoView.requestFocus();
            mVideoView.setVideoPath(mOutputFilePath);
            mVideoView.seekTo(100);
            mVideoView.setOnCompletionListener(mp -> {
                // Reset player
                mVideoView.setVisibility(View.GONE);
                mTextureView.setVisibility(View.VISIBLE);
                mPlayVideo.setVisibility(View.GONE);
                mRecordVideo.setImageResource(R.drawable.ic_record);
            });
        }
    
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            unbinder.unbind();
        }
    
       <strong> private String parseVideo(String mFilePath) throws IOException {
            DataSource channel = new FileDataSourceImpl(mFilePath);
            IsoFile isoFile = new IsoFile(channel);
            List<TrackBox> trackBoxes = isoFile.getMovieBox().getBoxes(TrackBox.class);
            boolean isError = false;
            for (TrackBox trackBox : trackBoxes) {
                TimeToSampleBox.Entry firstEntry = trackBox.getMediaBox().getMediaInformationBox().getSampleTableBox().getTimeToSampleBox().getEntries().get(0);
                // Detect if first sample is a problem and fix it in isoFile
                // This is a hack. The audio deltas are 1024 for my files, and video deltas about 3000
                // 10000 seems sufficient since for 30 fps the normal delta is about 3000
                if (firstEntry.getDelta() > 10000) {
                    isError = true;
                    firstEntry.setDelta(3000);
                }
            }
            File file = getOutputMediaFile();
            String filePath = file.getAbsolutePath();
            if (isError) {
                Movie movie = new Movie();
                for (TrackBox trackBox : trackBoxes) {
                    movie.addTrack(new Mp4TrackImpl(channel.toString() + "[" + trackBox.getTrackHeaderBox().getTrackId() + "]", trackBox));
                }
                movie.setMatrix(isoFile.getMovieBox().getMovieHeaderBox().getMatrix());
                Container out = new DefaultMp4Builder().build(movie);
    
                //delete file first!
                FileChannel fc = new RandomAccessFile(filePath, "rw").getChannel();
                out.writeContainer(fc);
                fc.close();
                Log.d(TAG, "Finished correcting raw video");
                return filePath;
            }
            return mFilePath;
        }</strong>
    
        /**
         * Create directory and return file
         * returning video file
         */
        private File getOutputMediaFile() {
            // External sdcard file location
            File mediaStorageDir = new File(Environment.getExternalStorageDirectory(),
                    VIDEO_DIRECTORY_NAME);
            // Create storage directory if it does not exist
            if (!mediaStorageDir.exists()) {
                if (!mediaStorageDir.mkdirs()) {
                    Log.d(TAG, "Oops! Failed create "
                            + VIDEO_DIRECTORY_NAME + " directory");
                    return null;
                }
            }
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
                    Locale.getDefault()).format(new Date());
            File mediaFile;
    
            mediaFile = new File(mediaStorageDir.getPath() + File.separator
                    + "VID_" + timeStamp + ".mp4");
            return mediaFile;
        }
    }
    
    Download Sample Project- Camera2 API Android, Audio Video out of Sync in Issue

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

    Introduction

    Camera2 API is an upgraded model of the Camera device. Today you look many apps with rich camera features in markets like Instagram and Snapchat.   In earlier, we used the camera for video and image capture.

    In 2014 google introduce Camera2 API with lollipop version (API Version 21). I would like to suggest you must use Camera API 2 if not have version constraint. Camera2 API is not supported below 21 API.

    Camera2 device model takes input request to capture a single frame and a single image as per user. Each request is processed in a order, multiple requests can process at a time.

    If already integrate camera2 API in your project and you feel the video appears to be frozen in playback, read our another article Audio Video out of Sync in Issue

    Prerequisite

    • Create a new Project
    • Ensure is  MinSDK 21
    • Request storage, mic and camera permission
    • Setup Camera2 API

    Step-1. Project Setup

    Create a new project in android studio from File => New Project => Enter Project Name => Set min SDK 21 => Select Empty Activity from template.

    Step-2. First of all set Min SDK 21

    You must set minSdkVersion 21.  Camera2 API is not supported below 21 API.    

    android {
        compileSdkVersion 27
        defaultConfig {
            applicationId "com.androidwave.camera2video"
            minSdkVersion 21
            targetSdkVersion 27
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            vectorDrawables.useSupportLibrary = true
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    }
    

    Step-3. Furthermore, add below dependency Permission Request

    3.1 Set below dependency for runtime permission management

    request permission
    implementation ‘com.karumi:dexter:4.2.0’

    dependencies {
        implementation fileTree(include: ['*.jar'], dir: 'libs')
        implementation 'com.android.support:appcompat-v7:27.1.1'
        implementation 'com.android.support.constraint:constraint-layout:1.1.3'
        implementation 'com.android.support:support-v4:27.1.1'
        implementation 'com.android.support:design:27.1.1'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'com.android.support.test:runner:1.0.2'
        androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
        // request permission
        implementation 'com.karumi:dexter:4.2.0'
        // ButterKnife Dependency Injection
        implementation 'com.jakewharton:butterknife:8.8.1'
        annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
    
    }
    
    3.2 Declare below permission in AndroidMenifest.xml
         <!-- declare storage, camera and audio permission -->
        <uses-permission android:name="android.permission.CAMERA"/>
        <uses-permission android:name="android.permission.RECORD_AUDIO"/>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    
    3.3 Request Camera Permission in runtime in activity
        /**
         * Requesting permissions storage, audio and camera at once
         */
        public void requestPermission() {
            Dexter.withActivity(getActivity()).withPermissions(Manifest.permission.CAMERA,
                    Manifest.permission.RECORD_AUDIO,
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    .withListener(new MultiplePermissionsListener() {
                        @Override
                        public void onPermissionsChecked(MultiplePermissionsReport report) {
                            // check if all permissions are granted or not
                            if (report.areAllPermissionsGranted()) {
                                if (mTextureView.isAvailable()) {
                                    openCamera(mTextureView.getWidth(), mTextureView.getHeight());
                                } else {
                                    mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
                                }
                            }
                            // check for permanent denial of any permission show alert dialog
                            if (report.isAnyPermissionPermanentlyDenied()) {
                                // open Settings activity
                                showSettingsDialog();
                            }
                        }
    
                        @Override
                        public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
                            token.continuePermissionRequest();
                        }
                    }).withErrorListener(error -> Toast.makeText(getActivity().getApplicationContext(), "Error occurred! ", Toast.LENGTH_SHORT).show())
                    .onSameThread()
                    .check();
        }
        /**
         * Showing Alert Dialog with Settings option in case of deny any permission
         */
        private void showSettingsDialog() {
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setTitle(getString(R.string.message_need_permission));
            builder.setMessage(getString(R.string.message_permission));
            builder.setPositiveButton(getString(R.string.title_go_to_setting), (dialog, which) -> {
                dialog.cancel();
                openSettings();
            });
            builder.show();
    
        }
    
        // navigating settings app
        private void openSettings() {
            Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
            Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null);
            intent.setData(uri);
            startActivityForResult(intent, 101);
        }
    

    Step-4. Create AutoFitTextureView for camera preview

    Create a AutoFitTextureView which extends TextureView. The Java code looks Like this.

    package com.androidwave.camera2video.camera;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.TextureView;
    
    public class AutoFitTextureView extends TextureView {
    
        private int mRatioWidth = 0;
        private int mRatioHeight = 0;
    
        public AutoFitTextureView(Context context) {
            this(context, null);
        }
    
        public AutoFitTextureView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        /**
         * Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
         * calculated from the parameters. Note that the actual sizes of parameters don't matter, that
         * is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
         *
         * @param width  Relative horizontal size
         * @param height Relative vertical size
         */
        public void setAspectRatio(int width, int height) {
            if (width < 0 || height < 0) {
                throw new IllegalArgumentException("Size cannot be negative.");
            }
            mRatioWidth = width;
            mRatioHeight = height;
            requestLayout();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);
            if (0 == mRatioWidth || 0 == mRatioHeight) {
                setMeasuredDimension(width, height);
            } else {
                if (width < height * mRatioWidth / mRatioHeight) {
                    setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
                } else {
                    setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
                }
            }
        }
    }
    

    Step-5. Open Camera

    Check if all permissions are granted then open camera using below code

        /**
         * Tries to open a {@link CameraDevice}. The result is listened by `mStateCallback`.
         */
        private void openCamera(int width, int height) {
            final Activity activity = getActivity();
            if (null == activity || activity.isFinishing()) {
                return;
            }
            CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
            try {
                Log.d(TAG, "tryAcquire");
                if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                    throw new RuntimeException("Time out waiting to lock camera opening.");
                }
                /**
                 * default front camera will activate
                 */
                String cameraId = manager.getCameraIdList()[0];
    
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
                StreamConfigurationMap map = characteristics
                        .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
                if (map == null) {
                    throw new RuntimeException("Cannot get available preview/video sizes");
                }
                mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));
                mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
                        width, height, mVideoSize);
    
                int orientation = getResources().getConfiguration().orientation;
                if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                    mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
                } else {
                    mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
                }
                configureTransform(width, height);
                mMediaRecorder = new MediaRecorder();
                if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                    // TODO: Consider calling
                    requestPermission();
                    return;
                }
                manager.openCamera(cameraId, mStateCallback, null);
            } catch (CameraAccessException e) {
                Log.e(TAG, "openCamera: Cannot access the camera.");
            } catch (NullPointerException e) {
                Log.e(TAG, "Camera2API is not supported on the device.");
            } catch (InterruptedException e) {
                throw new RuntimeException("Interrupted while trying to lock camera opening.");
            }
        }
    

    Step-6. Choose the aspect ratio of video size

    Choose the aspect ratio of video size. The video aspect ratio of the video should be 3×4 or 16×9. Also, we don’t use sizes larger than 1080p, since MediaRecorder cannot handle such a high-resolution video.

        /**
         * @param choices The list of available sizes
         * @return The video size 1080p,720px
         */
        private static Size chooseVideoSize(Size[] choices) {
            for (Size size : choices) {
                if (1920 == size.getWidth() && 1080 == size.getHeight()) {
                    return size;
                }
            }
            for (Size size : choices) {
                if (size.getWidth() == size.getHeight() * 4 / 3 && size.getWidth() <= 1080) {
                    return size;
                }
            }
            Log.e(TAG, "Couldn't find any suitable video size");
            return choices[choices.length - 1];
        }
    
    chooses the smallest one whose width and height are at least as large as the respective requested values, and whose aspect ratio matches with the specified value.
    
       /**
         * Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose
         * width and height are at least as large as the respective requested values, and whose aspect
         * ratio matches with the specified value.
         *
         * @param choices     The list of sizes that the camera supports for the intended output class
         * @param width       The minimum desired width
         * @param height      The minimum desired height
         * @param aspectRatio The aspect ratio
         * @return The optimal {@code Size}, or an arbitrary one if none were big enough
         */
        private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
            // Collect the supported resolutions that are at least as big as the preview Surface
            List<Size> bigEnough = new ArrayList<>();
            int w = aspectRatio.getWidth();
            int h = aspectRatio.getHeight();
            for (Size option : choices) {
                if (option.getHeight() == option.getWidth() * h / w &&
                        option.getWidth() >= width && option.getHeight() >= height) {
                    bigEnough.add(option);
                }
            }
    
            // Pick the smallest of those, assuming we found any
            if (bigEnough.size() > 0) {
                return Collections.min(bigEnough, new CompareSizesByArea());
            } else {
                Log.e(TAG, "Couldn't find any suitable preview size");
                return choices[0];
            }
        }
    

    Step-7. Show video previews

    After allowing above permission render preview on AutofitTextureView

        /**
         * Start the camera preview.
         */
        private void startPreview() {
            if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
                return;
            }
            try {
                closePreviewSession();
                SurfaceTexture texture = mTextureView.getSurfaceTexture();
                assert texture != null;
                texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
                mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                Surface previewSurface = new Surface(texture);
                mPreviewBuilder.addTarget(previewSurface);
                mCameraDevice.createCaptureSession(Collections.singletonList(previewSurface),
                        new CameraCaptureSession.StateCallback() {
    
                            @Override
                            public void onConfigured(@NonNull CameraCaptureSession session) {
                                mPreviewSession = session;
                                updatePreview();
                            }
    
                            @Override
                            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                                Log.e(TAG, "onConfigureFailed: Failed ");
                            }
                        }, mBackgroundHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    

    Step-8. Setup media recorder

    So now you able to preview on screen. If we want to start video recording lets configure the MediaRecorder.

     private void setUpMediaRecorder() throws IOException {
            final Activity activity = getActivity();
            if (null == activity) {
                return;
            }
            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            /**
             * create video output file
             */
            mCurrentFile = getOutputMediaFile();
            /**
             * set output file in media recorder
             */
            mMediaRecorder.setOutputFile(mCurrentFile.getAbsolutePath());
            CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
            mMediaRecorder.setVideoFrameRate(profile.videoFrameRate);
            mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
            mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate);
            mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
            mMediaRecorder.setAudioEncodingBitRate(profile.audioBitRate);
            mMediaRecorder.setAudioSamplingRate(profile.audioSampleRate);
    
            int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
            switch (mSensorOrientation) {
                case SENSOR_ORIENTATION_DEFAULT_DEGREES:
                    mMediaRecorder.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation));
                    break;
                case SENSOR_ORIENTATION_INVERSE_DEGREES:
                    mMediaRecorder.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation));
                    break;
            }
            mMediaRecorder.prepare();
        }
    

    Step-9. After setting, media lets start Video Recording via below code

       public void startRecordingVideo() {
            if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
                return;
            }
            try {
                closePreviewSession();
                setUpMediaRecorder();
                SurfaceTexture texture = mTextureView.getSurfaceTexture();
                assert texture != null;
                texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
                mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
                List<Surface> surfaces = new ArrayList<>();
    
                /**
                 * Surface for the camera preview set up
                 */
    
                Surface previewSurface = new Surface(texture);
                surfaces.add(previewSurface);
                mPreviewBuilder.addTarget(previewSurface);
    
                //MediaRecorder setup for surface
                Surface recorderSurface = mMediaRecorder.getSurface();
                surfaces.add(recorderSurface);
                mPreviewBuilder.addTarget(recorderSurface);
    
                // Start a capture session
                mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
    
                    @Override
                    public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                        mPreviewSession = cameraCaptureSession;
                        updatePreview();
                        getActivity().runOnUiThread(() -> {
                            mIsRecordingVideo = true;
                            // Start recording
                            mMediaRecorder.start();
                        });
                    }
    
                    @Override
                    public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                        Log.e(TAG, "onConfigureFailed: Failed");
                    }
                }, mBackgroundHandler);
            } catch (CameraAccessException | IOException e) {
                e.printStackTrace();
            }
    
        }
    

    Step-10. After that stop video record via below code.

       public void stopRecordingVideo() throws Exception {
            // UI
            mIsRecordingVideo = false;
            try {
                mPreviewSession.stopRepeating();
                mPreviewSession.abortCaptures();
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
    
            // Stop recording
            mMediaRecorder.stop();
            mMediaRecorder.reset();
        }
    
    All things are done now Now CameraVideoFragment utility is ready to use. Just extend CameraVideoFragment class in place of Fragment. This final preview of CameraVideoFragment.java
    package com.androidwave.camera2video.camera;
    
    import android.Manifest;
    import android.app.Activity;
    import android.content.Context;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.content.res.Configuration;
    import android.graphics.Matrix;
    import android.graphics.RectF;
    import android.graphics.SurfaceTexture;
    import android.hardware.camera2.CameraAccessException;
    import android.hardware.camera2.CameraCaptureSession;
    import android.hardware.camera2.CameraCharacteristics;
    import android.hardware.camera2.CameraDevice;
    import android.hardware.camera2.CameraManager;
    import android.hardware.camera2.CameraMetadata;
    import android.hardware.camera2.CaptureRequest;
    import android.hardware.camera2.params.StreamConfigurationMap;
    import android.media.CamcorderProfile;
    import android.media.MediaRecorder;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.HandlerThread;
    import android.provider.Settings;
    import android.support.annotation.NonNull;
    import android.support.v4.app.ActivityCompat;
    import android.support.v7.app.AlertDialog;
    import android.util.Log;
    import android.util.Size;
    import android.util.SparseIntArray;
    import android.view.Surface;
    import android.view.TextureView;
    import android.view.View;
    import android.widget.Toast;
    
    import com.androidwave.camera2video.R;
    import com.androidwave.camera2video.ui.base.BaseFragment;
    import com.karumi.dexter.Dexter;
    import com.karumi.dexter.MultiplePermissionsReport;
    import com.karumi.dexter.PermissionToken;
    import com.karumi.dexter.listener.PermissionRequest;
    import com.karumi.dexter.listener.multi.MultiplePermissionsListener;
    
    import java.io.File;
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.Date;
    import java.util.List;
    import java.util.Locale;
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    
    public abstract class CameraVideoFragment extends BaseFragment {
    
        private static final String TAG = "CameraVideoFragment";
    
        private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270;
        private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90;
        private static final SparseIntArray INVERSE_ORIENTATIONS = new SparseIntArray();
        private static final SparseIntArray DEFAULT_ORIENTATIONS = new SparseIntArray();
    
        static {
            INVERSE_ORIENTATIONS.append(Surface.ROTATION_270, 0);
            INVERSE_ORIENTATIONS.append(Surface.ROTATION_180, 90);
            INVERSE_ORIENTATIONS.append(Surface.ROTATION_90, 180);
            INVERSE_ORIENTATIONS.append(Surface.ROTATION_0, 270);
        }
    
        static {
            DEFAULT_ORIENTATIONS.append(Surface.ROTATION_90, 0);
            DEFAULT_ORIENTATIONS.append(Surface.ROTATION_0, 90);
            DEFAULT_ORIENTATIONS.append(Surface.ROTATION_270, 180);
            DEFAULT_ORIENTATIONS.append(Surface.ROTATION_180, 270);
        }
    
    
        private File mCurrentFile;
    
        private static final String VIDEO_DIRECTORY_NAME = "AndroidWave";
    
        /**
         * An {@link AutoFitTextureView} for camera preview.
         */
        private AutoFitTextureView mTextureView;
    
        /**
         * A reference to the opened {@link CameraDevice}.
         */
        private CameraDevice mCameraDevice;
    
        /**
         * A reference to the current {@link CameraCaptureSession} for
         * preview.
         */
        private CameraCaptureSession mPreviewSession;
    
        /**
         * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a
         * {@link TextureView}.
         */
        private TextureView.SurfaceTextureListener mSurfaceTextureListener
                = new TextureView.SurfaceTextureListener() {
    
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
                                                  int width, int height) {
                openCamera(width, height);
            }
    
            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
                                                    int width, int height) {
                configureTransform(width, height);
            }
    
            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
                return true;
            }
    
            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
            }
    
        };
    
        /**
         * The {@link Size} of camera preview.
         */
        private Size mPreviewSize;
    
        /**
         * The {@link Size} of video recording.
         */
        private Size mVideoSize;
    
        /**
         * MediaRecorder
         */
        private MediaRecorder mMediaRecorder;
    
        /**
         * Whether the app is recording video now
         */
        public boolean mIsRecordingVideo;
    
        /**
         * An additional thread for running tasks that shouldn't block the UI.
         */
        private HandlerThread mBackgroundThread;
    
        /**
         * A {@link Handler} for running tasks in the background.
         */
        private Handler mBackgroundHandler;
    
        /**
         * A {@link Semaphore} to prevent the app from exiting before closing the camera.
         */
        private Semaphore mCameraOpenCloseLock = new Semaphore(1);
    
        /**
         * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its status.
         */
        private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
    
            @Override
            public void onOpened(@NonNull CameraDevice cameraDevice) {
                mCameraDevice = cameraDevice;
                startPreview();
                mCameraOpenCloseLock.release();
                if (null != mTextureView) {
                    configureTransform(mTextureView.getWidth(), mTextureView.getHeight());
                }
            }
    
            @Override
            public void onDisconnected(@NonNull CameraDevice cameraDevice) {
                mCameraOpenCloseLock.release();
                cameraDevice.close();
                mCameraDevice = null;
            }
    
            @Override
            public void onError(@NonNull CameraDevice cameraDevice, int error) {
                mCameraOpenCloseLock.release();
                cameraDevice.close();
                mCameraDevice = null;
                Activity activity = getActivity();
                if (null != activity) {
                    activity.finish();
                }
            }
    
        };
        private Integer mSensorOrientation;
        private CaptureRequest.Builder mPreviewBuilder;
    
    
        /**
         * In this sample, we choose a video size with 3x4 for  aspect ratio. for more perfectness 720 as well Also, we don't use sizes
         * larger than 1080p, since MediaRecorder cannot handle such a high-resolution video.
         *
         * @param choices The list of available sizes
         * @return The video size 1080p,720px
         */
        private static Size chooseVideoSize(Size[] choices) {
            for (Size size : choices) {
                if (1920 == size.getWidth() && 1080 == size.getHeight()) {
                    return size;
                }
            }
            for (Size size : choices) {
                if (size.getWidth() == size.getHeight() * 4 / 3 && size.getWidth() <= 1080) {
                    return size;
                }
            }
            Log.e(TAG, "Couldn't find any suitable video size");
            return choices[choices.length - 1];
        }
    
    
        /**
         * Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose
         * width and height are at least as large as the respective requested values, and whose aspect
         * ratio matches with the specified value.
         *
         * @param choices     The list of sizes that the camera supports for the intended output class
         * @param width       The minimum desired width
         * @param height      The minimum desired height
         * @param aspectRatio The aspect ratio
         * @return The optimal {@code Size}, or an arbitrary one if none were big enough
         */
        private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
            // Collect the supported resolutions that are at least as big as the preview Surface
            List<Size> bigEnough = new ArrayList<>();
            int w = aspectRatio.getWidth();
            int h = aspectRatio.getHeight();
            for (Size option : choices) {
                if (option.getHeight() == option.getWidth() * h / w &&
                        option.getWidth() >= width && option.getHeight() >= height) {
                    bigEnough.add(option);
                }
            }
    
            // Pick the smallest of those, assuming we found any
            if (bigEnough.size() > 0) {
                return Collections.min(bigEnough, new CompareSizesByArea());
            } else {
                Log.e(TAG, "Couldn't find any suitable preview size");
                return choices[0];
            }
        }
    
        public abstract int getTextureResource();
    
        @Override
        public void onViewCreated(final View view, Bundle savedInstanceState) {
            mTextureView = view.findViewById(getTextureResource());
    
        }
    
        @Override
        public void onResume() {
            super.onResume();
            startBackgroundThread();
            requestPermission();
        }
    
        @Override
        public void onPause() {
            closeCamera();
            stopBackgroundThread();
            super.onPause();
        }
    
        protected File getCurrentFile() {
            return mCurrentFile;
        }
    
        /**
         * Starts a background thread and its {@link Handler}.
         */
        private void startBackgroundThread() {
            mBackgroundThread = new HandlerThread("CameraBackground");
            mBackgroundThread.start();
            mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
        }
    
        /**
         * Stops the background thread and its {@link Handler}.
         */
        private void stopBackgroundThread() {
            mBackgroundThread.quitSafely();
            try {
                mBackgroundThread.join();
                mBackgroundThread = null;
                mBackgroundHandler = null;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * Requesting permissions storage, audio and camera at once
         */
        public void requestPermission() {
            Dexter.withActivity(getActivity()).withPermissions(Manifest.permission.CAMERA,
                    Manifest.permission.RECORD_AUDIO,
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    .withListener(new MultiplePermissionsListener() {
                        @Override
                        public void onPermissionsChecked(MultiplePermissionsReport report) {
                            // check if all permissions are granted or not
                            if (report.areAllPermissionsGranted()) {
                                if (mTextureView.isAvailable()) {
                                    openCamera(mTextureView.getWidth(), mTextureView.getHeight());
                                } else {
                                    mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
                                }
                            }
                            // check for permanent denial of any permission show alert dialog
                            if (report.isAnyPermissionPermanentlyDenied()) {
                                // open Settings activity
                                showSettingsDialog();
                            }
                        }
    
                        @Override
                        public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
                            token.continuePermissionRequest();
                        }
                    }).withErrorListener(error -> Toast.makeText(getActivity().getApplicationContext(), "Error occurred! ", Toast.LENGTH_SHORT).show())
                    .onSameThread()
                    .check();
        }
    
        /**
         * Showing Alert Dialog with Settings option in case of deny any permission
         */
        private void showSettingsDialog() {
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setTitle(getString(R.string.message_need_permission));
            builder.setMessage(getString(R.string.message_permission));
            builder.setPositiveButton(getString(R.string.title_go_to_setting), (dialog, which) -> {
                dialog.cancel();
                openSettings();
            });
            builder.show();
    
        }
    
        // navigating settings app
        private void openSettings() {
            Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
            Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null);
            intent.setData(uri);
            startActivityForResult(intent, 101);
        }
    
        /**
         * Tries to open a {@link CameraDevice}. The result is listened by `mStateCallback`.
         */
        private void openCamera(int width, int height) {
            final Activity activity = getActivity();
            if (null == activity || activity.isFinishing()) {
                return;
            }
            CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
            try {
                Log.d(TAG, "tryAcquire");
                if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                    throw new RuntimeException("Time out waiting to lock camera opening.");
                }
                /**
                 * default front camera will activate
                 */
                String cameraId = manager.getCameraIdList()[0];
    
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
                StreamConfigurationMap map = characteristics
                        .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
                if (map == null) {
                    throw new RuntimeException("Cannot get available preview/video sizes");
                }
                mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));
                mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
                        width, height, mVideoSize);
    
                int orientation = getResources().getConfiguration().orientation;
                if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                    mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
                } else {
                    mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
                }
                configureTransform(width, height);
                mMediaRecorder = new MediaRecorder();
                if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                    // TODO: Consider calling
                    requestPermission();
                    return;
                }
                manager.openCamera(cameraId, mStateCallback, null);
            } catch (CameraAccessException e) {
                Log.e(TAG, "openCamera: Cannot access the camera.");
            } catch (NullPointerException e) {
                Log.e(TAG, "Camera2API is not supported on the device.");
            } catch (InterruptedException e) {
                throw new RuntimeException("Interrupted while trying to lock camera opening.");
            }
        }
    
        /**
         * Create directory and return file
         * returning video file
         */
        private File getOutputMediaFile() {
    
            // External sdcard file location
            File mediaStorageDir = new File(Environment.getExternalStorageDirectory(),
                    VIDEO_DIRECTORY_NAME);
            // Create storage directory if it does not exist
            if (!mediaStorageDir.exists()) {
                if (!mediaStorageDir.mkdirs()) {
                    Log.d(TAG, "Oops! Failed create "
                            + VIDEO_DIRECTORY_NAME + " directory");
                    return null;
                }
            }
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
                    Locale.getDefault()).format(new Date());
            File mediaFile;
    
            mediaFile = new File(mediaStorageDir.getPath() + File.separator
                    + "VID_" + timeStamp + ".mp4");
            return mediaFile;
        }
    
        /**
         * close camera and release object
         */
        private void closeCamera() {
            try {
                mCameraOpenCloseLock.acquire();
                closePreviewSession();
                if (null != mCameraDevice) {
                    mCameraDevice.close();
                    mCameraDevice = null;
                }
                if (null != mMediaRecorder) {
                    mMediaRecorder.release();
                    mMediaRecorder = null;
                }
            } catch (InterruptedException e) {
                throw new RuntimeException("Interrupted while trying to lock camera closing.");
            } finally {
                mCameraOpenCloseLock.release();
            }
        }
    
        /**
         * Start the camera preview.
         */
        private void startPreview() {
            if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
                return;
            }
            try {
                closePreviewSession();
                SurfaceTexture texture = mTextureView.getSurfaceTexture();
                assert texture != null;
                texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
                mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                Surface previewSurface = new Surface(texture);
                mPreviewBuilder.addTarget(previewSurface);
                mCameraDevice.createCaptureSession(Collections.singletonList(previewSurface),
                        new CameraCaptureSession.StateCallback() {
    
                            @Override
                            public void onConfigured(@NonNull CameraCaptureSession session) {
                                mPreviewSession = session;
                                updatePreview();
                            }
    
                            @Override
                            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                                Log.e(TAG, "onConfigureFailed: Failed ");
                            }
                        }, mBackgroundHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * Update the camera preview. {@link #startPreview()} needs to be called in advance.
         */
        private void updatePreview() {
            if (null == mCameraDevice) {
                return;
            }
            try {
                setUpCaptureRequestBuilder(mPreviewBuilder);
                HandlerThread thread = new HandlerThread("CameraPreview");
                thread.start();
                mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, mBackgroundHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    
        private void setUpCaptureRequestBuilder(CaptureRequest.Builder builder) {
            builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
        }
    
        /**
         * Configures the necessary {@link Matrix} transformation to `mTextureView`.
         * This method should not to be called until the camera preview size is determined in
         * openCamera, or until the size of `mTextureView` is fixed.
         *
         * @param viewWidth  The width of `mTextureView`
         * @param viewHeight The height of `mTextureView`
         */
        private void configureTransform(int viewWidth, int viewHeight) {
            Activity activity = getActivity();
            if (null == mTextureView || null == mPreviewSize || null == activity) {
                return;
            }
            int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
            Matrix matrix = new Matrix();
            RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
            RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
            float centerX = viewRect.centerX();
            float centerY = viewRect.centerY();
            if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
                bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
                matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
                float scale = Math.max(
                        (float) viewHeight / mPreviewSize.getHeight(),
                        (float) viewWidth / mPreviewSize.getWidth());
                matrix.postScale(scale, scale, centerX, centerY);
                matrix.postRotate(90 * (rotation - 2), centerX, centerY);
            }
            mTextureView.setTransform(matrix);
        }
    
        private void setUpMediaRecorder() throws IOException {
            final Activity activity = getActivity();
            if (null == activity) {
                return;
            }
            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            /**
             * create video output file
             */
            mCurrentFile = getOutputMediaFile();
            /**
             * set output file in media recorder
             */
            mMediaRecorder.setOutputFile(mCurrentFile.getAbsolutePath());
            CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
            mMediaRecorder.setVideoFrameRate(profile.videoFrameRate);
            mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
            mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate);
            mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
            mMediaRecorder.setAudioEncodingBitRate(profile.audioBitRate);
            mMediaRecorder.setAudioSamplingRate(profile.audioSampleRate);
    
            int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
            switch (mSensorOrientation) {
                case SENSOR_ORIENTATION_DEFAULT_DEGREES:
                    mMediaRecorder.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation));
                    break;
                case SENSOR_ORIENTATION_INVERSE_DEGREES:
                    mMediaRecorder.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation));
                    break;
            }
            mMediaRecorder.prepare();
        }
    
        public void startRecordingVideo() {
            if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
                return;
            }
            try {
                closePreviewSession();
                setUpMediaRecorder();
                SurfaceTexture texture = mTextureView.getSurfaceTexture();
                assert texture != null;
                texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
                mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
                List<Surface> surfaces = new ArrayList<>();
    
                /**
                 * Surface for the camera preview set up
                 */
    
                Surface previewSurface = new Surface(texture);
                surfaces.add(previewSurface);
                mPreviewBuilder.addTarget(previewSurface);
    
                //MediaRecorder setup for surface
                Surface recorderSurface = mMediaRecorder.getSurface();
                surfaces.add(recorderSurface);
                mPreviewBuilder.addTarget(recorderSurface);
    
                // Start a capture session
                mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
    
                    @Override
                    public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                        mPreviewSession = cameraCaptureSession;
                        updatePreview();
                        getActivity().runOnUiThread(() -> {
                            mIsRecordingVideo = true;
                            // Start recording
                            mMediaRecorder.start();
                        });
                    }
    
                    @Override
                    public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                        Log.e(TAG, "onConfigureFailed: Failed");
                    }
                }, mBackgroundHandler);
            } catch (CameraAccessException | IOException e) {
                e.printStackTrace();
            }
    
        }
    
        private void closePreviewSession() {
            if (mPreviewSession != null) {
                mPreviewSession.close();
                mPreviewSession = null;
            }
        }
    
        public void stopRecordingVideo() throws Exception {
            // UI
            mIsRecordingVideo = false;
            try {
                mPreviewSession.stopRepeating();
                mPreviewSession.abortCaptures();
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
    
            // Stop recording
            mMediaRecorder.stop();
            mMediaRecorder.reset();
        }
    
        /**
         * Compares two {@code Size}s based on their areas.
         */
        static class CompareSizesByArea implements Comparator<Size> {
    
            @Override
            public int compare(Size lhs, Size rhs) {
                // We cast here to ensure the multiplications won't overflow
                return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
                        (long) rhs.getWidth() * rhs.getHeight());
            }
    
        }
    
    }
    

    10. Now all utilities are done. Now comes on a real implementation.

    10.1 Create a Fragment with the name of CameraFragment. Which extends CameraVideoFragment utility class
    10.2 Create fragment_camera.xml. add below 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"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".CameraFragment">
    
    
        <com.androidwave.camera2video.camera.AutoFitTextureView
            android:id="@+id/mTextureView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <ImageView
            android:id="@+id/mRecordVideo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="24dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:contentDescription="@string/play_stop"
            android:src="@drawable/ic_record"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />
    
        <VideoView
            android:id="@+id/mVideoView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <ImageView
            android:id="@+id/mPlayVideo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:src="@drawable/ic_play_button"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </android.support.constraint.ConstraintLayout>
    
    10.3. SetTextureResource by implement getTextureResource() methods of parent class
    package com.androidwave.camera2video;
    
    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.MediaController;
    import android.widget.VideoView;
    
    import com.androidwave.camera2video.camera.AutoFitTextureView;
    import com.androidwave.camera2video.camera.CameraVideoFragment;
    
    import butterknife.BindView;
    import butterknife.ButterKnife;
    import butterknife.OnClick;
    import butterknife.Unbinder;
    
    
    /**
     * A simple {@link Fragment} subclass.
     * Use the {@link CameraFragment#newInstance} factory method to
     * create an instance of this fragment.
     */
    public class CameraFragment extends CameraVideoFragment {
    
        @BindView(R.id.mTextureView)
        AutoFitTextureView mTextureView;
        @BindView(R.id.mRecordVideo)
        ImageView mRecordVideo;
        @BindView(R.id.mVideoView)
        VideoView mVideoView;
        @BindView(R.id.mPlayVideo)
        ImageView mPlayVideo;
        Unbinder unbinder;
        private String mOutputFilePath;
    
        public CameraFragment() {
            // Required empty public constructor
        }
    
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         */
    
    
        public static CameraFragment newInstance() {
            CameraFragment fragment = new CameraFragment();
            Bundle args = new Bundle();
            fragment.setArguments(args);
            return fragment;
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            // Inflate the layout for this fragment
            View view = inflater.inflate(R.layout.fragment_camera, container, false);
            unbinder = ButterKnife.bind(this, view);
            return view;
        }
    
        @Override
        public int getTextureResource() {
            return R.id.mTextureView;
        }
    
        @Override
        protected void setUp(View view) {
    
        }
    
        @OnClick({R.id.mRecordVideo, R.id.mPlayVideo})
        public void onViewClicked(View view) {
            switch (view.getId()) {
                case R.id.mRecordVideo:
                    /**
                     * If media is not recoding then start recording else stop recording
                     */
                    if (mIsRecordingVideo) {
                        try {
                            stopRecordingVideo();
                            prepareViews();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
    
                    } else {
                        startRecordingVideo();
                        mRecordVideo.setImageResource(R.drawable.ic_stop);
                        //Receive out put file here
                        mOutputFilePath = getCurrentFile().getAbsolutePath();
                    }
                    break;
                case R.id.mPlayVideo:
                    mVideoView.start();
                    mPlayVideo.setVisibility(View.GONE);
                    break;
            }
        }
    
        private void prepareViews() {
            if (mVideoView.getVisibility() == View.GONE) {
                mVideoView.setVisibility(View.VISIBLE);
                mPlayVideo.setVisibility(View.VISIBLE);
                mTextureView.setVisibility(View.GONE);
                setMediaForRecordVideo();
            }
        }
    
        private void setMediaForRecordVideo() {
            // Set media controller
            mVideoView.setMediaController(new MediaController(getActivity()));
            mVideoView.requestFocus();
            mVideoView.setVideoPath(mOutputFilePath);
            mVideoView.seekTo(100);
            mVideoView.setOnCompletionListener(mp -> {
                // Reset player
                mVideoView.setVisibility(View.GONE);
                mTextureView.setVisibility(View.VISIBLE);
                mPlayVideo.setVisibility(View.GONE);
                mRecordVideo.setImageResource(R.drawable.ic_record);
            });
        }
    
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            unbinder.unbind();
        }
    }
    

    Step-11. As a result Fragment in with Activity seems below.

     
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            getSupportActionBar().hide();
            AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
            if (null == savedInstanceState) {
                getFragmentManager().beginTransaction()
                        .replace(R.id.container, CameraFragment.newInstance())
                        .commit();
            }
        }
    }
    
    Download Sample Project- Video Recording with Camera2 API in Android

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