ImageWorker

Introduction: ImageWorker is Library for Android to Save Images in External Storage
More: Author   ReportBugs   
Tags:

ImageWorker Logo

A modern, lightweight, zero-permission image handling library for Android.
Save, retrieve, delete, and convert images seamlessly in application-specific scoped storage.

JitPack Release Platform Min SDK 21 Target SDK 34+ Kotlin 1.9+ Jetpack Compose Ready Buy Me A Coffee


🚀 Key Highlights

  • 🛡️ Zero Runtime Permissions: Fully compliant with modern Android Scoped Storage (Android 11, 12, 13, 14, 15+). Files are saved to app-specific external storage (context.getExternalFilesDir(null)), eliminating Google Play permission declarations.
  • ⚡ Lightweight & Efficient: Pure native Android APIs with automatic stream lifecycle management (.use { ... }) to avoid memory leaks.
  • 🎨 Multi-Format Compression: Seamlessly compress and store in PNG, JPEG, JPG, and WEBP (including WEBP_LOSSY on Android 11+).
  • 🔄 Bidirectional Converter: Effortlessly convert between Bitmap, Drawable, and Base64 strings.
  • 🧩 Compose & Coroutine Ready: Clean fluent API that slots naturally into Dispatchers.IO and Jetpack Compose state pipelines.

📦 Installation

ImageWorker is published via JitPack.

1. Add Maven Repository

In your root settings.gradle.kts (or settings.gradle):

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven("https://jitpack.io")
    }
}

2. Add Dependency

[versions]
imageWorker = "1.4.0"

[libraries]
imageworker = { module = "com.github.ihimanshurawat:ImageWorker", version.ref = "imageWorker" }

In your module's build.gradle.kts:

dependencies {
    implementation(libs.imageworker)
}

Option B: Kotlin DSL (build.gradle.kts)

dependencies {
    implementation("com.github.ihimanshurawat:ImageWorker:1.4.0")
}

Option C: Groovy DSL (build.gradle)

dependencies {
    implementation 'com.github.ihimanshurawat:ImageWorker:1.4.0'
}

🛠️ Usage Guide

1. Saving an Image (ImageWorker.to)

Save Bitmap, Drawable, or Base64 encoded strings directly to external app-dedicated storage:

// Save a Bitmap with 85% quality in PNG format
ImageWorker.to(context)
    .directory("ProfilePhotos")          // Optional: Defaults to package name
    .subDirectory("Avatars")             // Optional: Supports nesting
    .setFileName("user_avatar_101")      // Required
    .withExtension(Extension.PNG)        // Required: PNG, JPEG, JPG, WEBP
    .save(bitmap = profileBitmap, quality = 85)

// Save a Drawable
ImageWorker.to(context)
    .setFileName("badge_icon")
    .withExtension(Extension.WEBP)
    .save(drawable = context.getDrawable(R.drawable.ic_badge))

// Save a Base64 string
ImageWorker.to(context)
    .setFileName("receipt")
    .withExtension(Extension.JPEG)
    .save(base64 = base64ImageString, quality = 90)

Quality parameter: Integer between 1 and 100 (defaults to 100).


2. Loading an Image (ImageWorker.from)

Retrieve a previously saved image as an Android Bitmap?:

val bitmap: Bitmap? = ImageWorker.from(context)
    .directory("ProfilePhotos")
    .subDirectory("Avatars")
    .setFileName("user_avatar_101")
    .withExtension(Extension.PNG)
    .load()

3. Deleting an Image (delete)

Remove stored files when no longer needed:

val isDeleted: Boolean = ImageWorker.from(context)
    .directory("ProfilePhotos")
    .subDirectory("Avatars")
    .setFileName("user_avatar_101")
    .withExtension(Extension.PNG)
    .delete()

4. Format Conversion (ImageWorker.convert)

Convert between Bitmap, Drawable, and Base64 representations without writing to disk:

val converter = ImageWorker.convert()

// Bitmap conversions
val drawable: Drawable? = converter.bitmapToDrawable(bitmap)
val base64String: String? = converter.bitmapToBase64(bitmap)

// Base64 conversions
val bitmap: Bitmap? = converter.base64ToBitmap(base64String)
val drawable: Drawable? = converter.base64ToDrawable(base64String)

// Drawable conversions
val bitmap: Bitmap? = converter.drawableToBitmap(drawable)
val base64String: String? = converter.drawableToBase64(drawable)

Save directly into the device's public MediaStore (Pictures/<directory>/<subDirectory>) so pictures immediately appear in system Gallery apps (Samsung Gallery, Google Photos) without requiring runtime storage permissions on Android 10+ (API 29+):

// Save Bitmap, Drawable, or Base64 into the public Gallery
val mediaStoreUri: Uri? = ImageWorker.to(context)
    .directory("Anime Wallpapers")
    .subDirectory("Naruto")
    .setFileName("wallpaper_101")
    .withExtension(Extension.JPEG)
    .saveToGallery(drawable = wallpaperDrawable, quality = 95, syncLocalCopy = true)

// Check if image is present in Gallery
val inGallery: Boolean = ImageWorker.from(context)
    .directory("Anime Wallpapers")
    .subDirectory("Naruto")
    .setFileName("wallpaper_101")
    .withExtension(Extension.JPEG)
    .existsInGallery()

// Get content Uri for intents (Set as wallpaper, Share chooser)
val contentUri: Uri? = ImageWorker.from(context)
    .directory("Anime Wallpapers")
    .subDirectory("Naruto")
    .setFileName("wallpaper_101")
    .withExtension(Extension.JPEG)
    .getGalleryUri()

// Load Bitmap directly from MediaStore
val bitmap: Bitmap? = ImageWorker.from(context)
    .directory("Anime Wallpapers")
    .subDirectory("Naruto")
    .setFileName("wallpaper_101")
    .withExtension(Extension.JPEG)
    .loadFromGallery()

// Delete from Gallery and local cache
val isDeleted: Boolean = ImageWorker.from(context)
    .directory("Anime Wallpapers")
    .subDirectory("Naruto")
    .setFileName("wallpaper_101")
    .withExtension(Extension.JPEG)
    .deleteFromGallery()

// Get app-specific local File handle
val localFile: File = ImageWorker.from(context)
    .directory("Anime Wallpapers")
    .subDirectory("Naruto")
    .setFileName("wallpaper_101")
    .withExtension(Extension.JPEG)
    .getFile()

📱 Modern Android Patterns

Asynchronous Execution with Kotlin Coroutines

Disk operations should always be performed off the main thread. Combine ImageWorker with withContext(Dispatchers.IO):

suspend fun saveUserProfile(context: Context, bitmap: Bitmap): Boolean = withContext(Dispatchers.IO) {
    try {
        ImageWorker.to(context)
            .directory("UserProfiles")
            .setFileName("current_user")
            .withExtension(Extension.WEBP)
            .save(bitmap, quality = 90)
        true
    } catch (e: Exception) {
        false
    }
}

suspend fun loadUserProfile(context: Context): Bitmap? = withContext(Dispatchers.IO) {
    ImageWorker.from(context)
        .directory("UserProfiles")
        .setFileName("current_user")
        .withExtension(Extension.WEBP)
        .load()
}

Integration with Jetpack Compose

Load and display your images easily in a @Composable:

@Composable
fun ProfileImageCard(context: Context) {
    var userBitmap by remember { mutableStateOf<Bitmap?>(null) }

    LaunchedEffect(Unit) {
        userBitmap = withContext(Dispatchers.IO) {
            ImageWorker.from(context)
                .directory("UserProfiles")
                .setFileName("current_user")
                .withExtension(Extension.WEBP)
                .load()
        }
    }

    userBitmap?.let { bitmap ->
        Image(
            bitmap = bitmap.asImageBitmap(),
            contentDescription = "User Avatar",
            modifier = Modifier
                .size(100.dp)
                .clip(CircleShape),
            contentScale = ContentScale.Crop
        )
    } ?: Box(
        modifier = Modifier.size(100.dp),
        contentAlignment = Alignment.Center
    ) {
        CircularProgressIndicator()
    }
}

🔒 Storage & Permissions Breakdown

Starting in modern Android versions, broad storage permissions (WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE) are deprecated and blocked on Google Play for general apps.

ImageWorker leverages application-specific storage:

/storage/emulated/0/Android/data/<your-application-id>/files/<directory>/<subDirectory>/<filename>.<ext>
Android Version API Level Permissions Required? Details
Android 11 – Android 15+ API 30+ None Full scoped storage compliance; zero user permission prompts needed.
Android 4.4 – Android 10 API 19 – 29 None App-specific external storage does not require runtime permissions.
Android 4.3 & below API <= 18 WRITE_EXTERNAL_STORAGE Legacy fallback (handled automatically).

[!NOTE] Files stored in application-specific directories are automatically cleared by the Android OS when your app is uninstalled.


⚙️ Supported Extensions

Extension Enum File Suffix Format Details
Extension.PNG .png Lossless compression, preserves alpha channel
Extension.JPEG .jpeg Standard lossy compression, adjustable quality
Extension.JPG .jpg Alias for JPEG format
Extension.WEBP .webp Modern web format (WEBP_LOSSY on Android 11+)

🤝 Contributing

Contributions, issues, and feature requests are welcome! Feel free to check the issues page.


📄 License

This project is licensed under the open-source community standards. See individual source files for details.

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools
AI Daily Digest