Code (rewrote it to run in MainActivity, so if anybody wants to reproduce the problem it will be easier):
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import android.os.PersistableBundle
import android.provider.MediaStore
import android.util.AttributeSet
import android.view.View
import android.widget.Button
import android.widget.ImageView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.FileProvider
import simplyquotes.example.myapplication.databinding.ActivityMainBinding
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*
class MainActivity : AppCompatActivity() {
private var _binding: ActivityMainBinding? = null
private val binding get() = _binding!!
private var currentPhotoUri: Uri = Uri.EMPTY
private val intentLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
val photoResult: Uri? = it.data?.data
if(photoResult != null) {
// user picked from gallery
this.contentResolver.takePersistableUriPermission(
photoResult,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
currentPhotoUri = photoResult
changeProfilePicture(currentPhotoUri)
} else {
// user made a photo
changeProfilePicture(currentPhotoUri)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
_binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.button.setOnClickListener {
openIntentChooserForImageSources()
}
}
@Throws(IOException::class)
private fun createImageFile(): File {
// Create an image file name
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val storageDir = this.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val file = File.createTempFile(
"JPEG_${timeStamp}_", /* prefix */
".jpg", /* suffix */
storageDir /* directory */
)
currentPhotoUri = FileProvider.getUriForFile(this.applicationContext ,this.packageName, file)
return file
}
private fun openIntentChooserForImageSources() {
// creating gallery intent
val galleryIntent = Intent(Intent.ACTION_OPEN_DOCUMENT, MediaStore.Images.Media.INTERNAL_CONTENT_URI)
// creating camera intent
val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
cameraIntent.also { takePictureIntent ->
takePictureIntent.resolveActivity(this.packageManager)?.also {
val photoFile: File? = try {
createImageFile()
} catch (e: IOException){
null
}
photoFile?.also {
val photoFileUri: Uri = FileProvider.getUriForFile(
this.applicationContext,
this.packageName,
it
)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoFileUri)
}
}
}
val intentChooser = Intent.createChooser(galleryIntent, "Select an app")
intentChooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(cameraIntent))
intentLauncher.launch(intentChooser)
}
private fun changeProfilePicture(uri: Uri) {
binding.imageView2.setImageURI(uri)
}
}
This code was based on this page from the documentation (had to modify some parts), the part where the user chooses a picture from the gallery works fine, but the part where the user has to use the camera works fine... only for the devices with API < 30 (and android versions < 11 I believe). Tbh I have no idea why but for the newer devices the camera intent starts and... doesn't return any Uri? the only message I got is:
W/ImageView: resolveUri failed on bad bitmap uri:
and as you can see, there isn't any bitmap uri included in the message at all
edit: No permissions are missing (checked that many times) and I already saw questions similar to this, but neither picasso library or using a bitmap helped
second edit (how the code runs):
The user presses a button, which starts an intent chooser (for gallery and camera) which can be found here:
private fun openIntentChooserForImageSources() {
// creating gallery intent
val galleryIntent = Intent(Intent.ACTION_OPEN_DOCUMENT, MediaStore.Images.Media.INTERNAL_CONTENT_URI)
// creating camera intent
val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
cameraIntent.also { takePictureIntent ->
takePictureIntent.resolveActivity(this.packageManager)?.also {
val photoFile: File? = try {
createImageFile()
} catch (e: IOException){
null
}
photoFile?.also {
val photoFileUri: Uri = FileProvider.getUriForFile(
this.applicationContext,
this.packageName,
it
)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoFileUri)
}
}
}
val intentChooser = Intent.createChooser(galleryIntent, "Select an app")
intentChooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(cameraIntent))
intentLauncher.launch(intentChooser)
}
after the user selects the camera and takes a picture, the picture file is created using the "createImageFile()" function:
@Throws(IOException::class)
private fun createImageFile(): File {
// Create an image file name
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val storageDir = this.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val file = File.createTempFile(
"JPEG_${timeStamp}_", /* prefix */
".jpg", /* suffix */
storageDir /* directory */
)
currentPhotoUri = FileProvider.getUriForFile(this.applicationContext ,this.packageName, file)
return file
}
which creates a file with collision-resistant name, and after creating the file it updates the currentPhotoUri variable:
currentPhotoUri = FileProvider.getUriForFile(this.applicationContext ,this.packageName, file)
after saves the file I believe it reaches this part:
if (it.resultCode == Activity.RESULT_OK) {
val photoResult: Uri? = it.data?.data
if(photoResult != null) {
// user picked from gallery
this.contentResolver.takePersistableUriPermission(
photoResult,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
currentPhotoUri = photoResult
changeProfilePicture(currentPhotoUri)
} else {
// user made a photo
changeProfilePicture(currentPhotoUri) // <-- referring to this
}
}
which should update the photo using the currentPhotoUri variable that is already changed