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
CodePudding user response:
There wasn't an issue with the code (because it worked for gallery images and older android versions AND was basically copied from the documentation), after double checking the differences between the app on different android devices I noticed that the contents of: takePictureIntent.resolveActivity(requireContext().packageManager)?.also {...}
were never executed because it retuned Null, so after a quick google I found this answer, which basically resolved my issue