Kotlin Select Multiple Video From Gallery programmatically | Video Picker

kotlin sqlite multiple tables, kotlin recyclerview checkbox, kotlin recyclerview button, kotlin load html javascript, kotlin recyclerview searchview, kotlin recyclerview search using edittext, kotlin listview with edittext, kotlin recyclerview with edittext, kotlin swipe to delete listview, kotlin recyclerview swipe to delete, kotlin expandablelistview with checkbox, kotlin custom ratingbar, kotlin change tab, Kotlin Select Multiple Images From Gallery, Kotlin Custom Dialog With Image And Title, kotlin select multiple video

Read about Kotlin Select Multiple Video From Gallery programmatically.

We will create video picker which enables the user to select multiple videos.

Once the user selects multiple videos, compiler will get the paths of all the selected videos.

When you are making app application when you need to select one or more video from internal storage of the android device, this tutorial will help you to implement this feature.

After selecting the videos, we will preview them inside the video view thus we will be able to get the video paths also.

First of all, see the below video to learn how our example will work.

 

Step 1. Granting Permission

Make a new project in the android studio with kotlin as the main source language.

Along with this, also make empty activity as the default one.

Now in your new project, open AndroidManifest.xml file. You need to add the below line in it

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

This line will allow us to read all the video of the android device.

If do not ask permission from user then we will not be able to read video from the android device.

This permission is some what dangerous for the user privacy, thus we will write runtime permission for it in the Main activity kotlin file.

Step 2. Writing Gradle

Asking runtime permission from user with android’s native code lines is little complex.

So we will use external library for this purpose.

Now open build.gradle(Module:app) file. Add the following in it

 implementation 'com.karumi:dexter:5.0.0'

This line will add some code lines in our project which creates the dexter library.

Dexter library can save our time and coding lines in the process of asking runtime permission from the user.

This library will help us to implement the runtime permissions in the smoothest possible way.

Step 3. Main Files Alteration

There should be two files in your project : activity_main.xml and MainActivity.kt

In your activity_main.xml file, add the below code lines

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              tools:context=".MainActivity">

    <VideoView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:id="@+id/vv"
            android:layout_marginTop="10dp"
            android:layout_marginLeft="10dp"/>
    <VideoView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:id="@+id/vv2"
            android:layout_marginTop="10dp"
            android:layout_marginLeft="10dp"/>

    <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btn"
            android:layout_marginTop="10dp"
            android:text="Select Multiple Videos From Gallery"/>

</LinearLayout>

There are two video views and one button is there.

When the user clicks the button, compiler will open new screen to show all the videos of the device.

Video views will preview the selected videos. There are two video views in total.

Now Write down the below code snippet in the MainActivity.kt file.

import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.ContentUris
import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.annotation.RequiresApi
import android.util.Log
import android.widget.Button
import android.widget.Toast
import android.widget.VideoView
import com.karumi.dexter.Dexter
import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.DexterError
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.PermissionRequestErrorListener
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import java.util.ArrayList

class MainActivity : AppCompatActivity() {

    private var videoView: VideoView? = null
    private var videoView2: VideoView? = null
    private var btn: Button? = null
    private var selectedVideos: List<String>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        requestReadPermissions()

        btn = findViewById(R.id.btn) as Button
        videoView = findViewById(R.id.vv) as VideoView
        videoView2 = findViewById(R.id.vv2) as VideoView

        btn!!.setOnClickListener {
            if (Build.VERSION.SDK_INT < 19) {
                val intent = Intent()
                intent.type = "video/mp4"
                intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
                intent.action = Intent.ACTION_GET_CONTENT
                startActivityForResult(Intent.createChooser(intent, "Select videos"), SELECT_VIDEOS)
            } else {
                val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
                intent.addCategory(Intent.CATEGORY_OPENABLE)
                intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
                intent.type = "video/mp4"
                startActivityForResult(intent, SELECT_VIDEOS_KITKAT)
            }
        }

    }

    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
    public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        if (resultCode == Activity.RESULT_OK) {
            selectedVideos = getSelectedVideos(requestCode, data!!)
            Log.d("path", selectedVideos!!.toString())
            videoView!!.setVideoPath(selectedVideos!![0])
            videoView!!.requestFocus()
            videoView!!.start()

            if (selectedVideos!!.size > 1) {
                videoView2!!.setVideoPath(selectedVideos!![1])
                videoView2!!.requestFocus()
                videoView2!!.start()
            }
        }

    }

    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
    private fun getSelectedVideos(requestCode: Int, data: Intent): List<String> {

        val result = ArrayList<String>()

        val clipData = data.clipData
        if (clipData != null) {
            for (i in 0 until clipData.itemCount) {
                val videoItem = clipData.getItemAt(i)
                val videoURI = videoItem.uri
                val filePath = getPath(this, videoURI)
                if (filePath != null) {
                    result.add(filePath)
                }
            }
        } else {
            val videoURI = data.data
            val filePath = getPath(this, videoURI)
            if (filePath != null) {
                result.add(filePath)
            }
        }

        return result
    }

    companion object {
        private val TAG = "VideoPickerActivity"

        private val SELECT_VIDEOS = 1
        private val SELECT_VIDEOS_KITKAT = 1

        @SuppressLint("NewApi")
        fun getPath(context: Context, uri: Uri?): String? {

            val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT

            // DocumentProvider
            if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
                // ExternalStorageProvider
                if (isExternalStorageDocument(uri!!)) {
                    val docId = DocumentsContract.getDocumentId(uri)
                    val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                    val type = split[0]

                    if ("primary".equals(type, ignoreCase = true)) {
                        return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
                    }

                    // TODO handle non-primary volumes
                } else if (isDownloadsDocument(uri)) {

                    val id = DocumentsContract.getDocumentId(uri)
                    val contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)
                    )

                    return getDataColumn(context, contentUri, null, null)
                } else if (isMediaDocument(uri)) {
                    val docId = DocumentsContract.getDocumentId(uri)
                    val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                    val type = split[0]

                    var contentUri: Uri? = null
                    if ("image" == type) {
                        contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                    } else if ("video" == type) {
                        contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                    } else if ("audio" == type) {
                        contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                    }

                    val selection = "_id=?"
                    val selectionArgs = arrayOf(split[1])

                    return getDataColumn(context, contentUri, selection, selectionArgs)
                }// MediaProvider
                // DownloadsProvider
            } else if ("content".equals(uri!!.scheme!!, ignoreCase = true)) {

                // Return the remote address
                return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(context, uri, null, null)

            } else if ("file".equals(uri.scheme!!, ignoreCase = true)) {
                return uri.path
            }// File
            // MediaStore (and general)

            return null
        }

        fun getDataColumn(
            context: Context, uri: Uri?, selection: String?,
            selectionArgs: Array<String>?
        ): String? {

            var cursor: Cursor? = null
            val column = "_data"
            val projection = arrayOf(column)

            try {
                cursor = context.contentResolver.query(uri!!, projection, selection, selectionArgs, null)
                if (cursor != null && cursor.moveToFirst()) {
                    val index = cursor.getColumnIndexOrThrow(column)
                    return cursor.getString(index)
                }
            } finally {
                cursor?.close()
            }
            return null
        }


        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is ExternalStorageProvider.
         */
        fun isExternalStorageDocument(uri: Uri): Boolean {
            return "com.android.externalstorage.documents" == uri.authority
        }

        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is DownloadsProvider.
         */
        fun isDownloadsDocument(uri: Uri): Boolean {
            return "com.android.providers.downloads.documents" == uri.authority
        }

        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is MediaProvider.
         */
        fun isMediaDocument(uri: Uri): Boolean {
            return "com.android.providers.media.documents" == uri.authority
        }

        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is Google Photos.
         */
        fun isGooglePhotosUri(uri: Uri): Boolean {
            return "com.google.android.apps.photos.content" == uri.authority
        }
    }

    private fun requestReadPermissions() {
        Dexter.withActivity(this)
            .withPermissions( Manifest.permission.READ_EXTERNAL_STORAGE )
            .withListener(object : MultiplePermissionsListener {
                override fun onPermissionsChecked(report: MultiplePermissionsReport) {
                    // check if all permissions are granted
                    if (report.areAllPermissionsGranted()) {
                        Toast.makeText(applicationContext, "All permissions are granted by user!", Toast.LENGTH_SHORT)
                            .show()
                    }

                    // check for permanent denial of any permission
                    if (report.isAnyPermissionPermanentlyDenied) {
                        // show alert dialog navigating to Settings
                        //openSettingsDialog();
                    }
                }

                override fun onPermissionRationaleShouldBeShown(permissions: List<PermissionRequest>, token: PermissionToken) {
                    token.continuePermissionRequest()
                }
            }).withErrorListener(object : PermissionRequestErrorListener {
                override fun onError(error: DexterError) {
                    Toast.makeText(applicationContext, "Some Error! ", Toast.LENGTH_SHORT).show()
                }
            })
            .onSameThread()
            .check()
    }

}

More Focus

First of all, see the following code snippet

 private var videoView: VideoView? = null
    private var videoView2: VideoView? = null
    private var btn: Button? = null
    private var selectedVideos: List<String>? = null

For the first two lines, compiler will create the objects of the Video view class.

Then, it will give us the button class’s object.

Last line will create the arrayList with the string variables. It’s name would be selectedVideos 

In the onCreate() method, compiler will run the requestReadpermissions() method.

Following are the source lines for requestReadpermissions() method.

 private fun requestReadPermissions() {
        Dexter.withActivity(this)
            .withPermissions( Manifest.permission.READ_EXTERNAL_STORAGE )
            .withListener(object : MultiplePermissionsListener {
                override fun onPermissionsChecked(report: MultiplePermissionsReport) {
                    // check if all permissions are granted
                    if (report.areAllPermissionsGranted()) {
                        Toast.makeText(applicationContext, "All permissions are granted by user!", Toast.LENGTH_SHORT)
                            .show()
                    }

                    // check for permanent denial of any permission
                    if (report.isAnyPermissionPermanentlyDenied) {
                        // show alert dialog navigating to Settings
                        //openSettingsDialog();
                    }
                }

                override fun onPermissionRationaleShouldBeShown(permissions: List<PermissionRequest>, token: PermissionToken) {
                    token.continuePermissionRequest()
                }
            }).withErrorListener(object : PermissionRequestErrorListener {
                override fun onError(error: DexterError) {
                    Toast.makeText(applicationContext, "Some Error! ", Toast.LENGTH_SHORT).show()
                }
            })
            .onSameThread()
            .check()
    }

This method is implementing the runtime permissions.

In this process, we are using Dexter library which have integrated in our project during step 2.

It will ask user to grant Read external storage permission so that we can read all the videos from the android device of the user.

Compiler will follow the below coding lines when the user clicks the button.

    btn!!.setOnClickListener {
            if (Build.VERSION.SDK_INT < 19) {
                val intent = Intent()
                intent.type = "video/mp4"
                intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
                intent.action = Intent.ACTION_GET_CONTENT
                startActivityForResult(Intent.createChooser(intent, "Select videos"), SELECT_VIDEOS)
            } else {
                val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
                intent.addCategory(Intent.CATEGORY_OPENABLE)
                intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
                intent.type = "video/mp4"
                startActivityForResult(intent, SELECT_VIDEOS_KITKAT)
            }
        }

On the button click, if the android device have version of android less than 19, it will go to if() block otherwise it will run else() block.

Inside both the blocks, compiler will create one intent object.

It will attach some settings like Video,mp4, Allow multiple etc. to the intent.

Now using this intent, compiler will start new activity.

This activity will show up all the videos of the android device.

User can select one or more videos from this screen.

When the user have selected his desired videos, compiler will execute the onActivityResult() method.

Following is the source snippet for onActivityResult() method.

 @RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
    public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        if (resultCode == Activity.RESULT_OK) {
            selectedVideos = getSelectedVideos(requestCode, data!!)
            Log.d("path", selectedVideos!!.toString())
            videoView!!.setVideoPath(selectedVideos!![0])
            videoView!!.requestFocus()
            videoView!!.start()

            if (selectedVideos!!.size > 1) {
                videoView2!!.setVideoPath(selectedVideos!![1])
                videoView2!!.requestFocus()
                videoView2!!.start()
            }
        }

    }

In this method, compiler will first get the paths of all the selected videos using getSelecetdVideos() method.

After this, it will set the videos into the video views to preview them.

If user have selected more than one video than only compiler will preview the second video view otherwise not.

Download Kotlin Select Multiple Video From Github

https://github.com/demonuts/Kotlin-Select-Multiple-Video-From-Gallery-programmatically-Video-Picker