Pyro
Pyro

Reputation: 45

Android Kotlin: Photos saved with EXTRA_OUTPUT are 0 bytes

I've followed the android documentation on photos (https://developer.android.com/training/camera/photobasics#kotlin) to try and take a photo and save it. If I don't use EXTRA_OUTPUT I can successfully get the small image from data.extra, but I need a bigger picture. Using extra_output the same way they do in that link, I never get an actual photo saved, only 0 byte files. So something is going wrong with my extra_output, but I have no idea what. Any ideas?

I did find other people with similar issues, but no actual solution

class CameraFragment2 : Fragment() {

    private lateinit var binding: CameraFragment2FragmentBinding
    private lateinit var textRecognizer: TextRecognizer
    private lateinit var photoFile: File
    private lateinit var photoUri: Uri

    companion object {
        fun newInstance() = CameraFragment2()
    }

    private lateinit var viewModel: CameraFragment2ViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = CameraFragment2FragmentBinding.inflate(inflater)

        textRecognizer = TextRecognizer.Builder(context).build()

        dispatchTakePictureIntent()

        return binding.root
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProvider(this).get(CameraFragment2ViewModel::class.java)
        // TODO: Use the ViewModel
    }

    private fun dispatchTakePictureIntent() {
        val packageManager = context!!.packageManager
        Intent(Intents.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
            // Ensure that there's a camera activity to handle the intent
            takePictureIntent.resolveActivity(packageManager)?.also {
                // Create the File where the photo should go
                val photoFile: File? = try {
                    createImageFile()
                } catch (ex: IOException) {
                    // Error occurred while creating the File
                    Log.wtf("creating file failed", "creating file failed")
                    null
                }
                // Continue only if the File was successfully created
                photoFile?.also {
                    val photoURI: Uri = FileProvider.getUriForFile(
                        context!!,
                        //BuildConfig.APPLICATION_ID + ".provider",
                        "com.example.myapplication.fileprovider",
                        it
                    )
                    takePictureIntent.putExtra(EXTRA_OUTPUT, photoURI)
                    takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                    takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
                    startActivityForResult(takePictureIntent, 1)
                }
            }
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        //super.onActivityResult(requestCode, resultCode, data)


        //val file = File(currentPhotoPath)

        //val bitmap = BitmapFactory.decodeFile(currentPhotoPath)

        //scan(bitmap)

        val filePathUri = Uri.parse(currentPhotoPath)
        val myFile = File(filePathUri.path)
        val file_size = (myFile.length() / 1024).toString().toInt()
        Log.wtf("path", currentPhotoPath)
        Log.wtf("size", file_size.toString())

        //val image = File(currentPhotoPath)
        val bmOptions = BitmapFactory.Options()
        bmOptions.inJustDecodeBounds = false
        bmOptions.inSampleSize = 4
        //var bitmap = BitmapFactory.decodeFile(image.absolutePath, bmOptions)
        //scan(bitmap)

        var thing: Bitmap

        BitmapFactory.decodeFile(currentPhotoPath, bmOptions)?.also { bitmap ->
            scan(bitmap)
            thing = bitmap
        }



        if (resultCode == Intents.RESULT_OK && requestCode == 1){
            //val photo = data!!.extras!!.get(EXTRA_OUTPUT) as Bitmap
            //scan(photo)
            //val bitmap = BitmapFactory.decodeFile(photoFile.absolutePath)

            //scan(bitmap)

        }
    }

    lateinit var currentPhotoPath: String

    @Throws(IOException::class)
    private fun createImageFile(): File {
        // Create an image file name
        val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
        val storageDir: File = context!!.getExternalFilesDir(Environment.DIRECTORY_PICTURES)!!
        return File.createTempFile(
            "JPEG_${timeStamp}_", /* prefix */
            ".jpg", /* suffix */
            storageDir /* directory */
        ).apply {
            // Save a file: path for use with ACTION_VIEW intents
            currentPhotoPath = absolutePath
        }
    }


    private fun scan(photo: Bitmap){
        //val intent = Intent(ACTION_IMAGE_CAPTURE)
        val imageFrame = Frame.Builder()
            .setBitmap(photo)
            .build()
        val detections = textRecognizer.detect(imageFrame)

        val builder = StringBuilder()
        if (detections.size() != 0){
            for (x in 0..detections.size()) {
                builder.append(detections[x].value)
                builder.append("\n")
            }
        }


        binding.camFragResult.text = builder
    }

}

in my manifest:

<provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.example.myapplication.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"></meta-data>
        </provider>

provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-files-path
        name="my_images"
        path="." />
</paths>

Upvotes: 2

Views: 1351

Answers (2)

iDuckk
iDuckk

Reputation: 1

    private fun dispatchTakePictureIntent() {
    val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    // Create the File where the photo should go
    photoFile = createImageFile()

    // Continue only if the File was successfully created
    if(photoFile != null){
        val photoURI: Uri = FileProvider.getUriForFile(
            requireContext(),
            "com.billsAplication.fileprovider", // Your package
            photoFile!!)
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
    }

    if (requireContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) {
        // Start the image capture intent to take photo
        startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
    }

}

Upvotes: 0

CommonsWare
CommonsWare

Reputation: 1006819

Your primary bug is is that you did not add FLAG_GRANT_WRITE_URI_PERMISSION to your Intent. You granted the user's chosen camera app read access, not write access. So, the camera app cannot write to your designated location.

In addition:

  • You will lose the value of currentPhotoPath if your process is terminated while the camera app is in the foreground, which happens quite a bit

  • You might also consider cleaning up provider_paths.xml (you have two conflicting entries)

Upvotes: 1

Related Questions