Search code examples
androidimagekotlinandroid-camerainternal-storage

Save image to internal storage using ActivityResultContracts.GetContent(). - (Kotlin/Java/Android)


I'm new to file storage. My main goal is to take a picture with the camera, store it with good quality, then display it in an ImageView. I want to avoid asking for user permission (to use camera and external storage), and want to make this as simple as possible.

To take a picture, I'm using

val capturePicture = registerForActivityResult(ActivityResultContracts.TakePicture()) { it: Boolean -> }. I don't know how to get the bitmap from this function or if I should. To my understanding I should send the uri when calling capturePicture.launch(uri).

My question is if this is correct, also how do I get the URI, save it to internal storage (.openFileOutput()), then load it from internal storage.

I prefer the answer in Kotlin but Java is fine too. An explanation on how paths work in internal storage could be helpful too.


Solution

  • I followed this medium article tutorial, adjusted it and added more functionality for my use case.

    Saved images in the cache directory

    To take the picture:

    private val takeImageResult =
        registerForActivityResult(ActivityResultContracts.TakePicture()) { isSuccess ->
            if (isSuccess) {
                latestTmpUri?.let { uri ->
                    loadPhotosFromInternalStorageIntoRecyclerView()
                }
            }
        }
    

    To call take picture, save it, and get the uri:

    private var latestTmpUri: Uri? = null
    private fun takeImage() {
        lifecycleScope.launchWhenStarted {
            getTmpFileUri().let { uri ->
                latestTmpUri = uri
                takeImageResult.launch(uri)
            }
        }
    }
    private fun getTmpFileUri(): Uri {
        val tmpFile = File.createTempFile("tmp_image_file", ".jpg", requireActivity().cacheDir).apply {
            createNewFile()
            deleteOnExit()
        }
        return FileProvider.getUriForFile(requireActivity().applicationContext, "${BuildConfig.APPLICATION_ID}.provider", tmpFile)
    }
    

    To load the picture (loads the first picture in the list of pictures):

    private fun loadPhotosFromInternalStorage(): List<InternalStoragePhoto> {
        val files = requireActivity().cacheDir.listFiles()
        return files?.filter {
            it.canRead() && it.isFile && it.name.endsWith(".jpg")
        }?.map {
            InternalStoragePhoto(it.name, it.toUri())
        } ?: listOf()
    }
    private fun displayImage() {
        Glide.with(photoImg.context)
            .load(loadPhotosFromInternalStorage()[0].uri)
            .into(photoImg)
    }
    

    Here's the custom object for the images:

    data class InternalStoragePhoto(
        val name: String,
        val uri: Uri?
    )
    

    This is a simplified version of my code, here's the source code for the test app github repo