ExifInterface not working with files from sdcard - android

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

Related

Automatic video selector from galary android kotlin

i am using this code for selecting the video from gallery app but the problem is that this will open the gallery app and user select the video manually i dont want that.I am looking for the code that will automatically pick video from gallery app by just giving the video path.. I just want to upload a specific video by giving video path from mobile storage to firebase
Intent intent = new Intent();
intent.setType("video/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Select Video"), REQUEST_TAKE_GALLERY_VIDEO);
this code basically allow user to pick video from mobile storage and it is working fine.. but i want to automatically upload first video to firebase from folder Movies/CamerX-Videos. This folder is in my mobile storage
class MainActivity : AppCompatActivity() {
val VIDEO : Int = 3
lateinit var uri : Uri
lateinit var mStorage : StorageReference
//val uri: Uri = Uri.fromFile(File("/Internal storage/Movies/cameraX-Video/Has.mp4"))
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mStorage = FirebaseStorage.getInstance().getReference("Uploads")
val videoBtn = findViewById<View>(R.id.videoBtn) as Button
mStorage = FirebaseStorage.getInstance().reference.child("Uploads")
videoBtn.setOnClickListener(View.OnClickListener {
val intent = Intent()
intent.type = "video/*"
intent.action = Intent.ACTION_GET_CONTENT
startActivityForResult(Intent.createChooser(intent, "Select Video"), VIDEO)
})
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val uriTxt = findViewById<View>(R.id.uriTxt) as TextView
if (resultCode == RESULT_OK) {
if (requestCode == VIDEO) {
uri = data!!.data!!
uriTxt.text = uri.toString()
upload ()
}
}
super.onActivityResult(requestCode, resultCode, data)
}
private fun upload() {
var mReference = uri.lastPathSegment?.let { mStorage.child(it) }
try {
mReference?.putFile(uri)?.addOnSuccessListener { taskSnapshot: UploadTask.TaskSnapshot? -> var url = taskSnapshot!!
val dwnTxt = findViewById<View>(R.id.dwnTxt) as TextView
dwnTxt.text = url.toString()
Toast.makeText(this, "Successfully Uploaded :)", Toast.LENGTH_LONG).show()
}
}catch (e: Exception) {
Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show()
}
}
}

uploading image from gallery and camera to server

I need to save the image that is taken from camera or image taken from gallery in kotlin. for now I've done this part and i'm finding other examples in kotlin but i'm unable to find. In other examples, images were send in multipart which was coded in java.
These are listeners
private val cameraRequest = 1888
private val pickImage = 100
private var imageUri: Uri? = null
takePhoto.setOnClickListener {
val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(cameraIntent, cameraRequest)
}
choosePicture.setOnClickListener {
val gallery = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI)
startActivityForResult(gallery, pickImage)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == cameraRequest) {
val photo: Bitmap = data?.extras?.get("data") as Bitmap
takePhoto.setImageBitmap(photo)
}
if (resultCode == RESULT_OK && requestCode == pickImage) {
imageUri = data?.data
if (imageUri != null) {
choosePicture.setImageURI(imageUri)
}
}
}
You can use this library for making a multipart request: https://github.com/gotev/android-upload-service
Also, it will handle app behaviour for you which includes upload-retry on slow internet connection and many more.

Get Image URI from Gallery and passing it to another activity, cannot see image, get an exception

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.

Can't set image URI because Permission Denial: opening provider

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.

How to get base64 from file's URI?

In general, my task is to get base64 from chosen file. In order to open File Browser, I call following function:
private fun showFileBrowser() {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.setType("*/*")
startActivityForResult(intent, FILE_CHOOSE_REQUEST_CODE)
}
It is successfully opened. When some file is chosen, onActivityResult is called. Here it is:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == FILE_CHOOSE_REQUEST_CODE) {
// not that data.data is Uri
if(data != null && data.data != null) {
val encodedBase64 = getBase64FromPath(data.data.path)
print(encodedBase64)
}
}
}
Here is how I convert File to base64:
private fun getBase64FromPath(path: String): String {
try {
val bytes = File(path).readBytes()
return Base64.encodeToString(bytes, Base64.DEFAULT)
} catch (error: IOException) {
error.printStackTrace() // This exception always occurs
}
}
Seems like I do everything right, but I get FileNotFoundException. I don't know what is the reason for this. I didn't add any permission, because I don't to write anything to scared, I just want a user to choose a file, I will convert it to base64 and send it to the server. So, what is the problem this my code?
my task is to get base64 from chosen file
Your code has little to do with files. ACTION_GET_CONTENT is not limited to files on the device, let alone files on the filesystem that you can access.
When some file is chosen, onActivityResult is called
You get a Uri via onActivityResult(). A Uri is not a file, and getPath() on a Uri only has meaning if the scheme of that Uri is file. Most Uri values will have a scheme of content.
Replace your function with:
private fun getBase64ForUriAndPossiblyCrash(uri: Uri): String {
try {
val bytes = contentResolver.openInputStream(uri).readBytes()
return Base64.encodeToString(bytes, Base64.DEFAULT)
} catch (error: IOException) {
error.printStackTrace() // This exception always occurs
}
The AndPossiblyCrash portion of the function name is because you are going to run out of memory if the content is too large. Also note that you are doing this work on the main application thread, so your UI will be frozen while you are reading this in.

Categories

Resources