ImagePickerAndroid
๐ Image and Video Picker library for Android that supports Base64 for images and is compatible with both Layouts and Jetpack Compose.
A modern and easy-to-use Android library for picking images and videos from the gallery or capturing them with the camera. It offers a unified API for traditional Views (Activities/Fragments with XML) and Jetpack Compose. The project is written primarily in Kotlin, distributed under the Apache License 2.0 license, first published in 2023. Key topics include: android, android-library, androiddev, base64, base64-image.
Image Picker Android
A modern and easy-to-use Android library for picking images and videos from the gallery or capturing
them with the camera. It offers a unified API for traditional Views (Activities/Fragments with XML)
and Jetpack Compose. <br />
Note: The example project does not include examples for all methods. <br />
๐ Features
This library is designed to simplify media selection in your Android app with a robust set of
features:
- ๐ผ๏ธ Single Image Picker: Select a single image from the gallery.
- ๐จ Multiple Image Picker: Choose multiple images (up to 9).
- ๐ธ Camera Capture: Capture a new photo, with streamlined permission handling that can direct
users to app settings or can use the onPermanentCameraPermissionDenied() callback to implement
your own custom logic). - ๐น Video Picker: Select a single video from the gallery.
- ๐ Base64 Conversion: Automatically convert selected images to a Base64 string.
- โ๏ธ Image Scaling: Easily resize images to your desired dimensions.
- ๐ Jetpack Compose Support: First-class support for Jetpack Compose, with dedicated
composables.
๐ค Why Use This Library?
- Unified API: Supports both traditional Views (Activities/Fragments) and modern Jetpack Compose
UIs. - Time-Saving: Provides a simple, out-of-the-box solution to a common Android task, saving you
significant development time. - Boilerplate Reduction: Handles
ActivityResultLauncher, permissions, and file processing,
letting you focus on your app's logic. - Advanced Functionality: Includes powerful features like Base64 encoding and image scaling
without needing extra dependencies.
๐ผ๏ธ Preview (Demo)
| Views (XML) | Jetpack Compose |
|---|---|
| <img src="examples/view.gif" width="200"> | <img src="examples/jetpack_compose.gif" width="200"> |
| <p align="center">Demo project</p> | <p align="center">Demo project</p> |
๐ ๏ธ Versioning
- JDK Version:
17 - Target SDK:
36 - Minimum SDK:
24 - Kotlin Version:
2.3.20 - Gradle Version:
9.1.0 - Build Tool Version:
36.0.0
!! IMPORTANT NOTE
THE BETA RELEASES MAY CONTAIN MAJOR OR MINOR CHANGES. <br /> <br />
๐ Migration Guide
[!IMPORTANT]
Breaking changes from the version 2.3.0 and higher <br /> <br />
takeSingleCameraImage()changed totakeSingleCameraImage(context = context)<br /> <br />
takeSingleCameraImageWithBase64Value()changed to
takeSingleCameraImageWithBase64Value(context = context)
[!IMPORTANT]
Breaking changes from the version 2.4.0 and higher <br /> <br />
Added a new optional parametertakeImageType: TakeImageType = TakeImageType.TAKE_IMAGE(By
default isTakeImageType.TAKE_IMAGE) <br /> <br />
Those parameters are used to select whether you wantActivityResultContracts.TakePicture()or
ActivityResultContracts.TakePicturePreview(). <br /> <br />
The two options (enum) areTakeImageType.TAKE_IMAGEor
TakeImageType.TAKE_IMAGE_PREVIEW<br /> <br />
Kotlin// The two options (enum) are `TakeImageType.TAKE_IMAGE` or `TakeImageType.TAKE_IMAGE_PREVIEW`, // by default is `TakeImageType.TAKE_IMAGE` TakeSingleCameraImage( scaleBitmapModel = null, takeImageType = TakeImageType.TAKE_IMAGE, listener = { bitmap, uri -> //...your code here }) // The two options (enum) are `TakeImageType.TAKE_IMAGE` or `TakeImageType.TAKE_IMAGE_PREVIEW`, // by default is `TakeImageType.TAKE_IMAGE` TakeSingleCameraImageWithBase64Value( scaleBitmapModel = null, takeImageType = TakeImageType.TAKE_IMAGE, listener = { bitmap, uri, base64 -> //...your code here })
[!IMPORTANT]
Breaking changes from the version 2.5.0 and higher <br /> <br />Activity/Fragment/XML support <br />
Implemented a check to ensure the image picker is available; no migration required. <br />
Added a new optional callbackfun onImagePickerNotAvailable() { super.onImagePickerNotAvailable() }<br />Compose Support <br />
Added two new parameters to the Image Picker methods called in listener, one parameter is required, the second is optional. <br />
- context (required) <br />
- onImagePickerNotAvailable (optional) <br />
Note for both types (Activity/Fragment/XML/Compose support): When the image is not available there is a Log.w(...), show only when the BuildConfig.DEBUG is true. <br />
logcatfilterImagePickerAndroid com.nicos.imagepickerandroidcompose W Image Picker is not available
KotlinpickSingleImage(context = context, onImagePickerNotAvailable = { // show custom dialog - showDialog.value = true }) pickSingleImageWithBase64Value(context = context, onImagePickerNotAvailable = { // show custom dialog - showDialog.value = true }) pickMultipleImages(context = context, onImagePickerNotAvailable = { // show custom dialog - showDialog.value = true }) pickMultipleImagesWithBase64Values(context = context, onImagePickerNotAvailable = { // show custom dialog - showDialog.value = true })
[!IMPORTANT]
Breaking changes from the version 2.5.6 and higher <br /> <br />Activity/Fragment/XML support <br />
Implemented a check to ensure the image picker is available; no migration required. <br />
Added a new optional callbackfun onImagePickerNotAvailable() { super.onImagePickerNotAvailable() }<br />Compose Support <br />
Removed the context parameter from the Image Picker methods, leaving only the optional listener parameter. <br />
- context <- NO NEED TO PASS THIS ANYMORE, SO REMOVE THE PARAMETER <br />
- onImagePickerNotAvailable (optional) <br />
Note for both types (Activity/Fragment/XML/Compose support): When the image is not available there is a Log.w(...), show only when the BuildConfig.DEBUG is true. <br />
logcatfilterImagePickerAndroid com.nicos.imagepickerandroidcompose W Image Picker is not available
KotlinpickSingleImage(onImagePickerNotAvailable = { // show custom dialog - showDialog.value = true }) pickSingleImageWithBase64Value(onImagePickerNotAvailable = { // show custom dialog - showDialog.value = true }) pickMultipleImages(onImagePickerNotAvailable = { // show custom dialog - showDialog.value = true }) pickMultipleImagesWithBase64Values(onImagePickerNotAvailable = { // show custom dialog - showDialog.value = true })
โ๏ธ Basic Configuration (Gradle Dependencies)
[!IMPORTANT]
Check my article with the implementation <br />
:point_right: ImagePickerAndroid - My Android Image Picker Library ๐งโ๐ป - Medium :point_left: <br />
Groovy
Groovyimplementation 'com.github.NicosNicolaou16:ImagePickerAndroid:2.5.6'
Groovyallprojects { repositories { maven { url "https://jitpack.io" } } }
Kotlin DSL
Kotlinimplementation("com.github.NicosNicolaou16:ImagePickerAndroid:2.5.6")
KotlindependencyResolutionManagement { //... repositories { //... maven { url = uri("https://jitpack.io") } } }
libs.versions.toml
toml[versions] # other versions here... imagePickerAndroid = "2.5.6" [libraries] # other libraries here... image-picker-android = { group = "com.github.NicosNicolaou16", name = "ImagePickerAndroid", version.ref = "imagePickerAndroid" }
Kotlinimplementation(libs.image.picker.android)
KotlindependencyResolutionManagement { //... repositories { //... maven { url = uri("https://jitpack.io") } } }
๐ Standard Configuration (XML)
Step 1 - Get Instance
Kotlinclass MainActivity : AppCompatActivity(), ImagePickerInterface { //... private var imagePicker: ImagePicker? = null //... fun initImagePicker() { //Builder //Note: fragmentActivity or fragment are mandatory one of them imagePicker = ImagePicker( fragmentActivity = this, //activity instance - private fragment = this, // fragment instance - private coroutineScope = lifecycleScope, // mandatory - coroutine scope from activity or fragment - private scaleBitmapModelForSingleImage = ScaleBitmapModel( height = 100, width = 100 ), // optional, change the scale for image, by default is null scaleBitmapModelForMultipleImages = ScaleBitmapModel( height = 100, width = 100 ), // optional, change the scale for image, by default is null scaleBitmapModelForCameraImage = ScaleBitmapModel( height = 100, width = 100 ), // optional, change the scale for image, by default is null enabledBase64ValueForSingleImage = true, // optional, by default is false - private enabledBase64ValueForMultipleImages = true, // optional, by default is false - private enabledBase64ValueForCameraImage = true, // optional, by default is false - private imagePickerInterface = this, // call back interface shouldRedirectedToSettingsIfPermissionDenied = false // optional, by default is true - private, if it set false, need to call the callback onPermanentCameraPermissionDenied() ) //...other image picker initialization method(s) } //... }
Step 2 - Initialize the methods for Image Pickers (choose the preferred method(s))
KotlinimagePicker?.initPickSingleImageFromGalleryResultLauncher() imagePicker?.initPickMultipleImagesFromGalleryResultLauncher() imagePicker?.initTakePhotoWithCameraResultLauncher() imagePicker?.initPickSingleVideoFromGalleryResultLauncher()
Step 3 Call from Click Listeners (choose the preferred method(s))
KotlinimagePicker?.pickSingleImageFromGallery() imagePicker?.pickMultipleImagesFromGallery() imagePicker?.takeSinglePhotoWithCamera() imagePicker?.pickSingleVideoFromGallery()
Step 4 - Callbacks (Optionals)
Kotlinclass MainActivity : AppCompatActivity(), ImagePickerInterface { //... override fun onGallerySingleImage(bitmap: Bitmap?, uri: Uri?) { super.onGalleryImage(bitmap, uri) //...your code here } override fun onCameraImage(bitmap: Bitmap?) { super.onCameraImage(bitmap) //...your code here } override fun onMultipleGalleryImages( bitmapList: MutableList<Bitmap>?, uriList: MutableList<Uri>? ) { super.onMultipleGalleryImages(bitmapList, uriList) //...your code here } override fun onGallerySingleImageWithBase64Value( bitmap: Bitmap?, uri: Uri?, base64AsString: String? ) { super.onGalleryImage(bitmap, uri, base64AsString) //...your code here } override fun onCameraImageWithBase64Value(bitmap: Bitmap?, base64AsString: String?) { super.onCameraImage(bitmap, base64AsString) //...your code here } override fun onMultipleGalleryImagesWithBase64Value( bitmapList: MutableList<Bitmap>?, uriList: MutableList<Uri>?, base64AsStringList: MutableList<String>? ) { super.onMultipleGalleryImages(bitmapList, uriList, base64AsStringList) //...your code here } override fun onGallerySingleVideo(uri: Uri?) { super.onGallerySingleVideo(uri) //...your code here } // Need to call and set the shouldRedirectedToSettingsIfPermissionDenied = false from builder to use this callback override fun onPermanentCameraPermissionDenied() { super.onPermanentCameraPermissionDenied() //...your code here } override fun onImagePickerNotAvailable() { super.onImagePickerNotAvailable() //...your code here } }
๐ Compose Configuration
Step 1 - Initialize the Callbacks (Optionals)
KotlinPickSingleImage( scaleBitmapModel = ScaleBitmapModel( height = 100, width = 100 ), listener = { bitmap, uri -> //...your code here }) PickSingleImageWithBase64Value( scaleBitmapModel = null, listener = { bitmap, uri, base64 -> //...your code here }) PickMultipleImages( scaleBitmapModel = null, listener = { bitmapList, uriList -> //...your code here }) PickMultipleImagesWithBase64Values( scaleBitmapModel = null, listener = { bitmapList, uriList, base64List -> //...your code here }) // The two options (enum) are `TakeImageType.TAKE_IMAGE` or `TakeImageType.TAKE_IMAGE_PREVIEW`, // by default is `TakeImageType.TAKE_IMAGE` TakeSingleCameraImage( scaleBitmapModel = null, takeImageType = TakeImageType.TAKE_IMAGE, listener = { bitmap, uri -> //...your code here }) // The two options (enum) are `TakeImageType.TAKE_IMAGE` or `TakeImageType.TAKE_IMAGE_PREVIEW`, // by default is `TakeImageType.TAKE_IMAGE` TakeSingleCameraImageWithBase64Value( scaleBitmapModel = null, takeImageType = TakeImageType.TAKE_IMAGE, listener = { bitmap, uri, base64 -> //...your code here }) PickSingleVideo(listener = { uri -> //...your code here })
Step 2 Call from Click Listeners (choose the preferred method(s))
KotlinpickSingleImage(onImagePickerNotAvailable = { // show custom dialog - showDialog.value = true }) pickSingleImageWithBase64Value(onImagePickerNotAvailable = { // show custom dialog - showDialog.value = true }) pickMultipleImages(onImagePickerNotAvailable = { // show custom dialog - showDialog.value = true }) pickMultipleImagesWithBase64Values(onImagePickerNotAvailable = { // show custom dialog - showDialog.value = true }) /** * onPermanentCameraPermissionDeniedCallBack is optional * */ takeSingleCameraImage(context = context, onPermanentCameraPermissionDeniedCallBack { // show custom dialog - showDialog.value = true }) takeSingleCameraImageWithBase64Value(context = context, onPermanentCameraPermissionDeniedCallBack { // show custom dialog - showDialog.value = true }) pickSingleVideo()
Example for Compose Implementation
kotlin@Composable fun ImagePicker() { val context = LocalContext.current val bitmapValue = remember { mutableStateOf(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)) } //Initialize the call back PickSingleImage(scaleBitmapModel = null, listener = { bitmap, uri -> if (bitmap != null) { bitmapValue.value = bitmap } }) Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(20.dp, alignment = Alignment.Top), horizontalAlignment = Alignment.CenterHorizontally, ) { //other code Button(modifier = Modifier.size(150.dp, 50.dp), onClick = { //pick image from the gallery pickSingleImage(context = context, onImagePickerNotAvailable = {}) }) { Text( text = stringResource(R.string.pick_single_image), style = TextStyle(textAlign = TextAlign.Center) ) } //other code } }
โญ Stargazers
If you enjoy this project, please give it a star!
Check out all the stargazers
here: Stargazers on GitHub
๐ Support & Contributions
This library is actively maintained. Feedback, bug reports, and feature requests are welcome! Please feel free to open an issue or submit a pull request.
Contributors
Showing top 1 contributor by commit count.
