androidkotlinimage-galleryimage-compressionimagepicker

How to compress a photo picked from gallery in kotlin


I found so many questions on the topic but none had a full working example.

My case is simple:

  1. get picked image
  2. compress image
  3. upload compressed image to Firebase Storage

My current code (without compression):

Fragment:

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

        when (requestCode) {
            RC_PICK_IMAGE ->
                if (resultCode == Activity.RESULT_OK)
                    data?.data?.let { viewModel.updateUserPicture(it) }
        }
    }

ViewModel:

    fun updatePhotoUrl(photoUrl: Uri?): Task<Void> =
        Storage.uploadFile("/$PS_USERS/$uid", photoUrl)
                    .continueWithTask { ... }

Storage: (object to wrap Firebase interactions)

    fun uploadFile(path: String, uri: Uri): Task<Uri> {
        val imageRef = storageRef.child(path)
        val uploadTask = imageRef.putFile(uri)

        // Return the task getting the download URL
        return uploadTask.continueWithTask { task ->
            if (!task.isSuccessful) {
                task.exception?.let {
                    throw it
                }
            }
            imageRef.downloadUrl
        }
    }

This works perfectly.

Now my question is: what is the right way to add compression in this process?

On top of that, both solutions demand a context. I think compression is a process that should belong in the backend (or Repository class. The Storage object in my case), So they feel a bit off.

Can someone please share a full working example of this trivial use case?


Solution

  • Use this it might help: Call compressAndSetImage from onActivityResult by passing compressAndSetImage(data.data)

    fun compressAndSetImage(result: Uri){
            val job = Job()
            val uiScope = CoroutineScope(Dispatchers.IO + job)
            val fileUri = getFilePathFromUri(result, context!!) 
            uiScope.launch {
                val compressedImageFile = Compressor.compress(context!!, File(fileUri.path)){
                    quality(50) // combine with compressor constraint
                    format(Bitmap.CompressFormat.JPEG)
                }
                resultUri = Uri.fromFile(compressedImageFile)
    
                activity!!.runOnUiThread {
                    resultUri?.let {
                        //set image here 
                    }
                }
            }
        }
    

    To work around this issue i had to do first convert this path to a real path and i was able to solve this issue that way.. First of all this is the dependency to be added in the build.gradle (app) : //image compression dependency

    implementation 'id.zelory:compressor:3.0.0'
    

    //converting uri path to real path

    @Throws(IOException::class)
    fun getFilePathFromUri(uri: Uri?, context: Context?): Uri? {
        val fileName: String? = getFileName(uri, context)
        val file = File(context?.externalCacheDir, fileName)
        file.createNewFile()
        FileOutputStream(file).use { outputStream ->
            context?.contentResolver?.openInputStream(uri).use { inputStream ->
                copyFile(inputStream, outputStream)
                outputStream.flush()
            }
        }
        return Uri.fromFile(file)
    }
    
    @Throws(IOException::class)
    private fun copyFile(`in`: InputStream?, out: OutputStream) {
        val buffer = ByteArray(1024)
        var read: Int? = null
        while (`in`?.read(buffer).also({ read = it!! }) != -1) {
            read?.let { out.write(buffer, 0, it) }
        }
    }//copyFile ends
    
    fun getFileName(uri: Uri?, context: Context?): String? {
        var fileName: String? = getFileNameFromCursor(uri, context)
        if (fileName == null) {
            val fileExtension: String? = getFileExtension(uri, context)
            fileName = "temp_file" + if (fileExtension != null) ".$fileExtension" else ""
        } else if (!fileName.contains(".")) {
            val fileExtension: String? = getFileExtension(uri, context)
            fileName = "$fileName.$fileExtension"
        }
        return fileName
    }
    
    fun getFileExtension(uri: Uri?, context: Context?): String? {
        val fileType: String? = context?.contentResolver?.getType(uri)
        return MimeTypeMap.getSingleton().getExtensionFromMimeType(fileType)
    }
    
    fun getFileNameFromCursor(uri: Uri?, context: Context?): String? {
        val fileCursor: Cursor? = context?.contentResolver
            ?.query(uri, arrayOf<String>(OpenableColumns.DISPLAY_NAME), null, null, null)
        var fileName: String? = null
        if (fileCursor != null && fileCursor.moveToFirst()) {
            val cIndex: Int = fileCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
            if (cIndex != -1) {
                fileName = fileCursor.getString(cIndex)
            }
        }
        return fileName
    }