Reputation: 2120
I've been stuck with this for almost a week. Triend almost any answer/tutorial I found, but no luck yet.
I have an app that uses the camera. After the user takes a pic, and select the check/OK button in the camera, the camera returns, among other things, the Uri where the image was stored. It cames in the form of: content://media/external/images/media/122
(this is an actual pic in the phone), if I ask for the Uri.path I'll get in this case: /external/images/media/123
Now, with the Uri object, I can assign it to an imageView like imageView.setImageURI()
and it works, so I'm pretty confident the images are stored somewhere.
Now, next thing my app should do, is take all the pictures taken, and send them to an API.
My problem is, when I try to read the Uri with any method, it gives me a FileNotFound Exception
, no matter if I use the Uri or the path.
So I really don't know what to do. What am I missing? Do I have to explicitly save the image in a working directory? If so, how is it done? If not, how do I get the full path name including the filename? When I look at the DCIM directoy in the phone, I don't find the pics.
Thanks in advance!
EDIT
Code to take the pic:
btnTomarFoto.setOnClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(android.Manifest.permission.CAMERA) ==
PackageManager.PERMISSION_DENIED || ContextCompat.checkSelfPermission(
this,
android.Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_DENIED
) {
// El permiso no fue concedido
val permission = arrayOf(android.Manifest.permission.CAMERA,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
requestPermissions(permission, PERMISSION_CODE)
} else {
// El permiso ya estaba concedido
openCamera()
}
} else {
// El SO es menor que Marshmallow
}
}
}
Code to open the camera:
private fun openCamera() {
val values = ContentValues()
values.put(MediaStore.Images.Media.TITLE, "New Picture")
values.put(MediaStore.Images.Media.DESCRIPTION, "From the camera")
image_uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, image_uri)
startActivityForResult(cameraIntent, IMAGE_CAPTURE_CODE)
}
Code to store the Uri into the database:
private fun guardarFoto() {
var foto = FotografiaModel(actividad_id!!, image_uri.toString(), txtObservacion.text.toString())
Log.w(tag, "URI: $image_uri PATH: " + image_uri!!.path + " Encoded Path: " + image_uri!!.encodedPath)
fotografiaDBHelper.insertFoto(foto)
visitaDBHelper.actualizaEstado(actividad_id!!, "INICIADO")
}
There are some more things the code does, but this is the main thing. To show the received Uri into an Imageview I use:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
imageView.setImageURI(image_uri)
Log.v(tag, "Image URI: $image_uri")
}
}
Upvotes: 2
Views: 2278
Reputation: 1006734
My problem is, when I try to read the Uri with any method, it gives me a FileNotFound Exception, no matter if I use the Uri or the path.
That is because it is not a file. It is a Uri
.
For example, https://stackoverflow.com/questions/56839478/kotlin-how-to-read-a-uri-in-android
is a Uri
. Your code assumes that the path /questions/56839478/kotlin-how-to-read-a-uri-in-android
is the only thing that matters in a Uri
. By your argument, every single computer on the planet (plus those in orbit) have a file located at /questions/56839478/kotlin-how-to-read-a-uri-in-android
. That is not how a Uri
works. You need to use the entire Uri
to determine how to use it. In this case, https
as a scheme means that you use HTTPS as a protocol to talk to the designated server (stackoverflow.com
) to retrieve the content.
Similarly, the content
scheme in your Uri
from insert()
indicates that you use ContentResolver
to work with it. In particular, you can use openInputStream()
to get an InputStream
on the content identified by the Uri
, just as you might use HttpUrlConnection
or OkHttp to get an InputStream
on an https
Uri
.
In particular, since you delegated data storage to some other app (the MediaStore
), where the image is stored is up to somebody else, and it is not necessarily on the filesystem in a place where you can access it. That is particularly true on Android Q and higher, where you have limited access to arbitrary locations on the device via filesystem APIs.
Now, next thing my app should do, is take all the pictures taken, and send them to an API.
I am going to guess that "send them to an API" means "upload them to a server" (versus "call an API exposed by a library on the device" or "call a method in the Android SDK"). In that case, you have some code for talking to that server. It might be general-purpose code (e.g., OkHttp, Retrofit) or API-specific code (e.g., Facebook SDK).
Regardless, you will need to see what that code supports for your image content:
If it supports a Uri
, try using your Uri
If it supports InputStream
(or a Reader
of some type), use openInputStream()
on a ContentResolver
If it supports a FileDescriptor
or AssetFileDescriptor
, use openFileDescriptor()
on a ContentResolver
If it only supports File
, instead of using contentResolver.insert()
to get the Uri
to send to the camera app, use FileProvider
, so you can have the images saved in a file that you control (e.g., in getCacheDir()
)
Etc.
Upvotes: 2