Home > Blockchain >  camera intent not working correctly for android 11 (API versions 30 )
camera intent not working correctly for android 11 (API versions 30 )

Time:08-28

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

  • Related