sejn
sejn

Reputation: 2644

Unable to select/pass the selected URI of Gallery video in the android 12 and 13 and not able to view in the selected screen

I am using the android app and trying to add the gallery videos to my video editing screen. But it failed to show the video and showing a black screen.

Can any one let me know what I am doing wrong.

I am click on the gallery icon and try to open the video from the ACTION_PICK and send that video to the next Activity.

Problem: I am getting this error in LogCat:

java.lang.SecurityException: 
UID 10170 does not have permission to content:

//com.google.android.apps.photos.contentprovider/-1/2/content%3A%2F%2Fmedia%2Fexternal%2Fvideo%2Fmedia%2F25/ORIGINAL/NONE/video%2Fmp4/1574077794 

[user 0] at android.os.Parcel.createExceptionOrNull 

Here is my code:

VideoFragment.kt

R.id.imageViewVideo -> {
            if (android.os.Build.VERSION.SDK_INT >= 32) {
                val intent = Intent(Intent.ACTION_PICK, null) // It shows the videos alone
                intent.type = "video/*"
                resultLauncher.launch(intent)
            } else {
            requestPermission()
            }
        }

private val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == RESULT_OK) {
            val data: Intent? = result.data
            val videoUri: Uri = data?.data!!
            Log.i("videoUri", videoUri.toString()) // content://com.google.android.apps.photos.contentprovider/-1/2/content%3A%2F%2Fmedia%2Fexternal%2Fvideo%2Fmedia%2F1000000024/ORIGINAL/NONE/video%2Fmp4/1940625115
            val videoPath = parsePath(videoUri)
            if (videoPath != null) {
                Log.i("videoPath", videoPath) 
 /storage/emulated/0/Movies/VID_20231115_163657.mp4
                Log.i("videoPath22", videoUri.path.toString()) /-1/2/content://media/external/video/media/1000000024/ORIGINAL/NONE/video/mp4/1940625115
            }
           

            val intent = Intent([email protected](), SelectVideoActivity::class.java)
            intent.putExtra("path", videoPath)
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            startActivity(intent)
}
}

Next Activity File: (SelectVideoActivity.kt)

onCreate()

val source = File(intent!!.getStringExtra("path")!!)
                    copyFile(source, inputFile)
                    Log.i("input file ===", inputFile.toString())


@Throws(IOException::class)
    fun copyFile(sourceFile: File?, destFile: File) {
        try {
                if (!destFile.parentFile!!.exists()) destFile.parentFile!!.mkdirs()
                if (!destFile.exists()) {
                    destFile.createNewFile()
                }
                var source: FileChannel? = null
                var destination: FileChannel? = null

                source = FileInputStream(sourceFile).channel
                destination = FileOutputStream(destFile).channel
                destination.transferFrom(source, 0, source.size())
            }
            catch (e:Exception){
                Log.i("error","selectvideo ${e.message}")
            }finally {
//                source?.close()
//                destination?.close()
                initViews()
                setListener()
            }
    }

For the lowest version of Androids the path looks like below and it works fine

Filepath:

/storage/emulated/0/DCIM/Camera/VID_20231121_193407.mp4 /storage/emulated/0/Android/data/com.app.packagename/files/app-name/1700718359149.mp4

Code:

private lateinit var inputFile: File

onCreate method:

     inputFile =
                File(getExternalFilesDir("slow-mow")!!.absolutePath.plus("/" + "${System.currentTimeMillis()}.mp4"))

Upvotes: 2

Views: 525

Answers (3)

sejn
sejn

Reputation: 2644

It got worked with the below ACTION_OPEN_DOCUMENT and saved the file in cache and used the path

R.id.imageViewVideo -> {

    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT, null)
                    intent.type = "video/*"
        override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
                super.onActivityResult(requestCode, resultCode, data)
             if (resultCode == RESULT_OK) {
                                data?.data?.also { uri ->
                                    //Permission needed if you want to retain access even after reboot
                                    requireContext().contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
            
                                    val path = copyInCacheDir(uri)
                                    Log.e("file path", "onActivityResult: path ${path.toString()} ")
                                    val intent = Intent(requireActivity(), com.example.exoplayerslowmotion.SelectVideoActivity::class.java)
                                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
                                    intent.putExtra("path", path.toString())
                                    startActivity(intent)
                                    requireActivity().finish()
                                }
                            }}
            
            private fun copyInCacheDir(videoUri: Uri): String? {
                    try {
                        val contentResolver = requireContext().contentResolver
                       
                        if (videoUri.authority == "com.google.android.apps.photos.contentprovider") {
                            contentResolver.takePersistableUriPermission(
                                videoUri,
                                Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                            )
                        }
                        val returnCursor = contentResolver.query(videoUri, null, null, null, null)
                        returnCursor?.use { cursor ->
                            if (cursor.moveToFirst()) {
                                val displayNameIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME)
                                val sizeIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE)
            
                                val displayName = cursor.getString(displayNameIndex)
                                val size = cursor.getLong(sizeIndex)
            
                                val inputStream = contentResolver.openInputStream(videoUri)
                                val cacheDir = requireContext().cacheDir
                                val outputFile = File(cacheDir, displayName)
                                val outputStream = FileOutputStream(outputFile)
            
                                val bufferSize = 1024
                                val buffer = ByteArray(bufferSize)
                                var bytesRead: Int
            
                                while (inputStream?.read(buffer, 0, bufferSize).also { bytesRead = it!! } != -1) {
                                    outputStream.write(buffer, 0, bytesRead)
                                }
            
                                inputStream?.close()
                                outputStream.close()
            
                                Log.d("Video Copy", "Video copied to: ${outputFile.absolutePath}")
                                return outputFile.absolutePath
                            }
                        }
                    } catch (ex: Exception) {
                        Log.e("Video Copy Error", "Error copying video: ${ex.message}", ex)
                    }
            
                    return null
                }

Upvotes: 0

Dinh Lam
Dinh Lam

Reputation: 765

There is no problem with action pick video, it works fine and retured data. You dont need path of source video, that cause the security problem, just use uri and read it with ContentResolver.

// copy file
val uri = intent.getData()
contentResolver.openFileInputStream(uri)?.use { inStream ->
  val destFile = // you dest file, prefer to use context.getExternalFilesDir
  destFile.outputStream().use { os ->
    inStream.copyTo(os)
  }
}

Noted

  • Android 12 devices new installation targetting sdk 30: requestLegacyExternalStorage is always false. So you need handle for storage permission.
  • I prefer use app dir (Android/data/package-name/...) to copy file, save edited fil, then you can scan the file to Gallery if you need.

Upvotes: 0

CommonsWare
CommonsWare

Reputation: 1007296

Replace:

                val intent = Intent(requireActivity(), SelectVideoActivity::class.java)
                intent.putExtra("path", videoUri.path)
                startActivity(intent)

with:

                val intent = Intent(requireActivity(), SelectVideoActivity::class.java)
                intent.setData(videoUri).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                startActivity(intent)

And, in SelectVideoActivity, use getData() on the Intent to retrieve the Uri.

See this blog post of mine for more.

Even better would be to only have one activity and use fragments or composables for separate screens.

Upvotes: 2

Related Questions