Initially I choose a Intent chooser to pick up a media file, and in onActivityResult i got the uri
fun openVideo(view: View) {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
intent.type = "video/*"
startActivityForResult(
Intent.createChooser(intent, "Select video"),
REQUEST_TAKE_GALLERY_VIDEO
)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
...
...
val selectedVideoUri: Uri? = data!!.data
val vidPath: String = selectedVideoUri.toString()
video_uri = Uri.parse(vidPath)
// the video uri looks like below
//content://com.mi.android.globalFileexplorer.myprovider/external_files/Download/Dolittle%20(2020)%20%5B720p%5D%20%5BBluRay%5D%20%5BYTS.MX%5D/Dolittle.2020.720p.BluRay.x264.AAC-%5BYTS.MX%5D.mp4
initializePlayer(video_uri)
...
...
}
}
Then I was able to playing the local file in exoplayer.
fun initializePlayer(video_uri) {
println("log initializePlayer called")
player = ExoPlayerFactory.newSimpleInstance(this) // `player` was defined in GlobalVariables
id_player_view.player = player
if (video_uri != null) {
println("log $video_uri ${video_uri!!::class.simpleName}")
id_player_view.useController = true
var mediaSource: MediaSource = buildMediaSource(video_uri!!)
player?.playWhenReady = playWhenReady!!
player?.seekTo(currentWindow!!, playBackPosition!!)
player?.prepare(mediaSource, false, false)
}
}
But now, think that I saved the uri in database as a string.
Then, assume I retrieved the content from database and pass it as a intent to video playing activity
now, if i write a code like this, and call initializePlayer the video is not playing.
video_uri = Uri.parse((intent.getStringExtra("video_url")))
println("log $video_uri ${video_uri!!::class.simpleName}")
initializePlayer(video_uri)
i.e for all hardcoded content uri of local media files. the exoplayer is not working.
How to solve this issue ?
think that I saved the uri in database as a string
You no longer have rights to access the content.
How to solve this issue ?
Use ACTION_OPEN_DOCUMENT to let the user choose the content. Then, in onActivityResult(), call takePersistableUriPermission() on a ContentResolver, to have access to that content for a longer period of time. See this blog post and the documentation for more.
Related
I'm trying to retrieve URI from gallery and camera in one place (as record in Room database), therefore i need to standardize the access between them. I get it done for the camera by create a file and then retrieve its URI. The URI looks like this:
// first URI
content://media/external_primary/images/media/45
It works and i can retrieve the image based on that URI after reopening the app.
Here is the code for camera
private val launcherIntentCamera = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK) {
val bitmap = result?.data?.extras?.get("data") as Bitmap
val uri = savePhotoToExternalStorage(
requireActivity().contentResolver,
UUID.randomUUID().toString(),
bitmap
)
uri?.let {
viewModel.updateProfilePic(it.toString())
}
}
}
private fun startTakePhoto() {
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
launcherIntentCamera.launch(intent)
}
And then i try for gallery intent and what i receive is this:
// second URI
content://com.android.providers.media.documents/document/image%3A44
Based on what i observe, this URI is temporary. And when i try to retrieve it after reopening the app, it doesnt work.
This is my code
private val launcherIntentGallery = registerForActivityResult(
ActivityResultContracts.GetContent()
) { uri ->
viewModel.updateProfilePic(uri.toString())
}
private fun startGallery() {
requireActivity().intent.type = "image/*"
launcherIntentGallery.launch("image/*")
}
Is there any solution to convert the second URI to the format of first URI so i could retrieve the image when reopening the app?
So I have some code to choose an image from phone gallery and display it in an ImageView and also use it's URI in ExifInterface and get the exif data.
But it seems that, only works for the images in internal storage and not for external storage like sdcard.
So here is what I got:
I have a button that when it's clicked, First it checkes to see if the app has READ_EXTERNAL_STORAGE permission and if not it asks for it.
After it's granted with the permission it launches the function below :
private fun launchIntentForPhotos() {
val gallery = Intent(Intent.ACTION_PICK)
gallery.type = "image/*"
startActivityForResult(Intent.createChooser(gallery, "Choose an image"), PICK_PHOTO_CODE)
}
and than for onActivityResult I have this:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == PICK_PHOTO_CODE) {
imageUri = data?.data
imageView.setImageResource(0)
imageView.setImageURI(imageUri)
showExif(imageUri)
}
}
and finally the function for EXIF data:
private fun showExif(imageUri: Uri?) {
val inputStream :InputStream
try
{
inputStream = imageUri?.let { contentResolver.openInputStream(it) }!!
val exifInterface = ExifInterface(inputStream)
// Now you can extract any Exif tag you want
// Assuming the image is a JPEG or supported raw format
val imgWidthExif: String? = exifInterface?.getAttribute(ExifInterface.TAG_IMAGE_WIDTH)
}
catch (e: IOException) {
// Handle any errors
Log.v(TAG, "ERROR")
Toast.makeText(this, "Some went wrong!", Toast.LENGTH_LONG).show()
}
The showExif doesn't work when selecting an image from external storage and also I get this error:
W/ImageView: resolveUri failed on bad bitmap uri: content://com.google.android.apps.photos.contentprovider/-1/1/content....
Can Someone please tell me what I'm doing wrong?!
Ok I found a solution and it works fine for me!
Instead of using ACTION_PICK I used ACTION_GET_CONTENT like this:
private fun launchIntentForPhotos() {
val gallery = Intent(Intent.ACTION_GET_CONTENT, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
gallery.type = "image/*"
startActivityForResult(Intent.createChooser(gallery, "Choose an image"), PICK_PHOTO_CODE)
}
I have an activity from which I launch the gallery, select an image, and want to display the selected image in another activity. I have referred to the following solution and implemented the same.
How to get Image URI from Gallery?
Though I am able to pass the URI to the next activity, I cannot see anything on the image view. Any help as to where I am going wrong, appreciated.
btn_launch_gallery.setOnClickListener {
val requestCode = 0
val launchGalleryIntent = Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(launchGalleryIntent, requestCode)
}
My OnActivityResult looks like this, basically implemented the same as given in the example cited above.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode === 0 && resultCode === Activity.RESULT_OK ) {
val selectedImage: Uri? = data?.data
val picturePath = getRealPathFromURI(
selectedImage,
this
)
val intent = Intent(this, LogFoodDetail::class.java)
intent.putExtra("image_from_gallery", picturePath)
try {
startActivity(intent)
}
catch (e: Exception)
{
e.printStackTrace()
Log.e("Error",e.printStackTrace().toString())
}
}
}
fun getRealPathFromURI(contentURI: Uri?, context: Activity): String? {
val projection =
arrayOf(MediaStore.Images.Media.DATA)
val cursor = context.managedQuery(
contentURI, projection, null,
null, null
)
?: return null
val column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
return if (cursor.moveToFirst()) {
// cursor.close();
cursor.getString(column_index)
} else null
// cursor.close();
}
In my next activity, I getting the intent like this and passing the URI to ImageView. However, I cannot see the image. I get the following error - W/System.err: java.io.FileNotFoundException: No content provider: /storage/emulated/0/DCIM/Camera/***.jpg
val resId = intent.getStringExtra("image_from_gallery")
val imgThumbnail: ImageView = findViewById(R.id.food_thumbnail)
try{
val imageStream: InputStream? = contentResolver.openInputStream(Uri.parse(resId))
val bitmap = BitmapFactory.decodeStream(imageStream)
imgThumbnail.setImageBitmap(bitmap)
}
catch (e: Exception)
{
e.printStackTrace()
}
I see the following image in the next activity:
UPDATE:
As commented by #blackapps in his answer passing the URI as a string to the next activity on an intent.putExtra() and resolving the URI in the subsequent activity solved it, the updated code in OnActivityResult() is,
...
val selectedImage: Uri? = data?.data
val intent = Intent(this, LogFoodDetail::class.java)
intent.putExtra("image_from_gallery",
selectedImage.toString())
startActivity(intent)
Dont convert a nice uri to a file system path.
Uri uri = data.getData();
Pass the obtained uri directly to the next activity.
And there you can use it for
imageView.setImageUri(uri);
Instead of the uri you can also pass the uri as string with uri.toString().
You can directly load an local image Uri using:
imgThumbnail.setImageUri(yourUri);
Instead of sending the string path to the activity, you should send the raw uri and then set it directly to the imageView.
Good afternon,
I crate an app with Room database to save items (name, desc, price, image). I have 3 activities. The 1st activity for create, the 2nd MainActivity with RecyclerView and the last activity (ChangeActivity) open when I click on item in MainActivity. When I create an item I pick image and save uri in database. I want to set image in ImageView in ChangeActivity but get
Unable to start activity ComponentInfo {hop.test.aah/hop.test.aah.ChangeActivity}: java.lang.SecurityException: Permission Denial: opening provider com.android.providers.media.MediaDocumentsProvider from ProcessRecord{7a3e034 25078:hop.test.aah/u0a95} (pid=25078, uid=10095) requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs.
I tried some solutions what I found but nothing help or maybe I wrong and it is why I ask some help.
ChangeActivity:
In manifest I only add READ_EXTERNAL_PERMISSION and WRITE_EXTERNAL PERMISSION.
This is how I pick image:
UPD 2:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val resolver = applicationContext.contentResolver
val flags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
resolver.takePersistableUriPermission(image!!, flags)
and fun to get
#Throws(IOException::class)
fun getBitmapFromUri(uri: Uri): Bitmap {
val parcelFileDescriptor: ParcelFileDescriptor? =
contentResolver.openFileDescriptor(uri, "r")
val fileDescriptor: FileDescriptor = parcelFileDescriptor!!.fileDescriptor
val image: Bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor)
parcelFileDescriptor.close()
return image
}
This works for me:
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/jpeg"
startActivityForResult(
Intent.createChooser(
intent,
getString(R.string.chooser_title)
), RC_GALLERY)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
if (requestCode == RC_GALLERY) {
imgUri = data?.data
val contentResolver = applicationContext.contentResolver
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
imgUri?.let { contentResolver.takePersistableUriPermission(it, takeFlags) }
}
}
}
Step #1: Switch to ACTION_OPEN_DOCUMENT.
Step #2: In onActivityResult(), when you get the Uri, call takePersistableUriPermission() on a ContentResolver
Then, and only then, will you be able to save the Uri in a database and use it to access content in the future.
With your current approach, the Uri works for the activity that receives the onActivityResult() call, but it will not work for future processes.
I am learning Kotlin and Exoplayer and I am making an app that allows the user to select a video and play it on the next screen. However, after starting Intent, the selected video is not played.
First Activity:
private fun openGallery() {
intent = Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
intent.type = "video/*"
startActivityForResult(intent, 1)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK && requestCode == 1) {
val selectedMediaUri = data!!.getData()
if (selectedMediaUri.toString().contains("video")) {
//handle video
// Get selected gallery image
val selectedVideo = data.getData()
// Get and resize profile image
val filePathColumn = arrayOf(MediaStore.Video.Media.DATA)
val cursor = applicationContext.contentResolver.query(selectedVideo!!, filePathColumn, null, null, null)
cursor!!.moveToFirst()
val columnIndex = cursor.getColumnIndex(filePathColumn[0])
val videoStoragePath = cursor.getString(columnIndex)
cursor.close()
val intent = Intent(this#MainActivity,PostarVideoActivity::class.java)
intent.putExtra("path",videoStoragePath)
startActivity(intent)
}
}
Second Activity:
val path = intent.extras!!.getString("path")
Log.i("mypath", path)
val player = ExoPlayerFactory.newSimpleInstance(applicationContext)
videoPreview.player = player
val dataSourceFactory = DefaultDataSourceFactory(
applicationContext,
Util.getUserAgent(applicationContext, applicationContext.getString(R.string.app_name))
)
// This is the MediaSource representing the media to be played.
val videoSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(Uri.parse(path!!))
// Prepare the player with the source.
player.prepare(videoSource)
How to resolve this error? Thanks in advance.
The error was simple ...
Just request READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions
This can be easily resolved with the Dexter library.
Maybe this can help someone