Step 1: Add Dependencies
The first thing you need to do is add the necessary dependencies to your app's build.gradle file. Add the following lines to your dependencies block:
build.gradle depedency
implementation 'com.soundcloud.android:android-crop:1.0.1@aar'
implementation 'com.github.bumptech.glide:glide:4.12.0'
These dependencies will allow us to use the Picasso library for loading and displaying images, and the Compressor library for compressing and saving images.
Step 2: Add Permission to Manifest
Next, you need to add the WRITE_EXTERNAL_STORAGE permission to your app's manifest file. Add the following line to your manifest:
Manifest permissions and activity
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.soundcloud.android.crop.CropImageActivity"
android:theme="@style/Base.Theme.AppCompat"/>
</application>
</manifest>
This permission will allow your app to write to the device's external storage, which is where the gallery is located.
Step 3: Add the following code to your activity_main.xml file
activiy_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="4dp"
android:layout_marginTop="68dp"
android:scaleType="centerCrop"
android:background="@color/black"
/>
<Button
android:id="@+id/btnSelectImage"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="153dp"
android:text="Select Image" />
</RelativeLayout>
Step 4: Add the following code to your MainActivity.kt file
MainActivity.kt
import android.Manifest
import android.content.ContentValues
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.widget.Button
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.soundcloud.android.crop.Crop
import java.io.File
class MainActivity : AppCompatActivity() {
private lateinit var imageView: ImageView
companion object {
private const val REQUEST_CODE_PERMISSIONS = 101
private const val REQUEST_CODE_PICK_IMAGE = 102
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
imageView = findViewById(R.id.imageView)
val permissions = arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.READ_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(
this,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
permissions,
REQUEST_CODE_PERMISSIONS
)
}
val btnSelectImage = findViewById<Button>(R.id.btnSelectImage)
btnSelectImage.setOnClickListener {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(intent, REQUEST_CODE_PICK_IMAGE)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED &&
grantResults[1] == PackageManager.PERMISSION_GRANTED
) {
Toast.makeText(this, "Permissions granted", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Permissions not granted", Toast.LENGTH_SHORT).show()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_PICK_IMAGE && resultCode == RESULT_OK && data != null) {
val uri = data.data
if (uri != null) {
Crop.of(uri, Uri.fromFile(File(cacheDir, "cropped")))
.asSquare()
.start(this)
}
} else if (requestCode == Crop.REQUEST_CROP && resultCode == RESULT_OK) {
val croppedUri = Crop.getOutput(data)
if ( croppedUri != null) {
val bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, croppedUri)
imageView.setImageBitmap(bitmap)
saveImageToGallery(bitmap)
}
}
}
private fun saveImageToGallery(bitmap: Bitmap) {
val contentValues = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "Image_${System.currentTimeMillis()}.jpg")
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/MyApp")
}
val contentResolver = contentResolver
val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
if (uri != null) {
contentResolver.openOutputStream(uri).use { outputStream ->
if (outputStream != null) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream)
Toast.makeText(this, "Image saved to gallery", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Failed to save image to gallery", Toast.LENGTH_SHORT).show()
}
}
}
}
}
All done. now run your app. if you get any errors then contact me I will check and help you.
That's it! You've now learned how to crop and save images to the gallery in Android Studio using Kotlin.
Conclusion:
In this tutorial, we have shown you how to implement image cropping and saving functionalities in Android Studio using Kotlin. By following these steps, you can easily add this functionality to your app and provide a better user experience. Remember to always request permission from the user before writing to external storage to ensure your app complies with Android's security policies.