The Android Plugin I Couldn’t Live Without — So I Rebuilt It from Scratch
Tired of manually scaling Android images into density buckets? Here’s how I rebuilt a dead plugin to save my sanity — and yours.

Photo by Jonny Gios on Unsplash
The Android Plugin I Couldn’t Live Without — So I Rebuilt It from Scratch
If you’ve ever had to scale 80 images into 5 Android density buckets, congratulations — you’ve unlocked a core memory of Android developer’s suffering. What should be a simple “import image” task quickly spirals into a mind-numbing torture of file management.
For years, there was a humble Android Studio plugin that undertook this meticulous task and became an essential tool for everyday developers. Alas, all good things must come to an end. It wasn’t the plugin’s fault, but the passage of time with deprecations upon deprecations and code maintainers just couldn’t keep up with it. As a result, this once great plugin was abandoned.
Here is my experience attempting to bring it back from the dead and breath new life into it. It taught me about plugin development in IntelliJ, perseverance, and the power of tooling.
But first! Some overview about why am I going on and on about density buckets.
You would have definitely dealt with Android’s Screen Density System before. Basically, whenever we import images, we have to scale your rasterised images into a range of densities to cater Android’s rich ecosystem of different screen densities, especially in the early experimental days.

Android Density Qualifiers
If you take a sample size of the most popular Android devices in the 2008–2010 period, it would have been these usual suspects.
- HTC G1: 320×480, ~180dpi
- Motorola Droid: 480×854, ~265dpi
- Nexus One: 480×800, ~252dpi
Having a big margin of screen densities between the HTC G1 and the Motorola Droid would result in same image assets rendered differently. For instance, take a 48px button, you would have.
- 0.27" on the HTC G1 (physically huge)
- 0.18" on the Motorola Droid (tiny, hard to touch)
That’s why in Android Donut 1.6, density pixel dp was introduced so that no matter the screen’s density. It will scale according to which density bucket the phone belongs to.
This was the scale that the Android team came up with, as long you provide your images in those densities. The phone will do the rest!
ldpi (~120dpi) - Low density
mdpi (~160dpi) - Medium (baseline)
hdpi (~240dpi) - High
xhdpi (~320dpi) - Extra high
xxhdpi (~480dpi) - Extra extra high
xxxhdpi (~640dpi) - Extra extra extra high
Problem
Whether you’re building a media-heavy app or not, there’s no escaping image assets. Even a small handful can turn into a logistical nightmare!
5 Images = 5 x 5dpi variants ( 25 images)
10 Images = 10 x 5dpi variants ( 50 images)
20 Images = 20 x 5dpi variants (100 images)
40 Images = 40 x 5dpi variants (200 images)
80 Images = 80 x 5dpi variants (400 images)
As you can see, the workload can quickly grow exponentially with every new image. And don’t even get me started on supporting different locales, that’s a whole new level of chaos. Managing and sorting images into the right density buckets? There are only so many hours in a day.
Why don’t you just use SVG or vector images? 💭
As of 2025 writing of this article, Android still doesn’t natively support .svgimages. You still have to convert your .svg file to an Android XML vector format.
If it’s a simple icon or image, it will work beautifully. But as soon as the image gets more complicated or more colourful, that’s when you will hit a wall. Images will start to stutter as it requires more memory, especially if you’re running on a lower end device that doesn’t have the power to render “big” vectorised image.
Do you know Android Studio’s Resource Manager supports importing images into different densities? 💭
Yes, you are correct. But, you still need to provide those images in all its densities.

Android Studio Resource Manager — Import Drawables
1 step forward, 2 steps back.
Solution
For the longest time, since the start of my career as an Android Developer. I would always refer people to Marc Prengemann’s Android Drawable Import Plugin on Android Studio.
It’s a really powerful plugin that is doing what Android Studio’s Resource Manager is trying to do before it was even introduced. Still today, Resource Manager hasn’t even reach feature parity with an IDE plugin from 2015.
Among all their plugin’s rich features, the Batch Drawable Import feature is something I can’t live without and still not available on Resource Manager.

Marc Prengemann — Batch Drawable Import
Basically you just drag and drop an image to the plugin, then it will downscale your images to density buckets in your resource drawable folder directly.
ldpi (~120dpi) - Low density
mdpi (~160dpi) - Medium (baseline)
hdpi (~240dpi) - High
xhdpi (~320dpi) - Extra high
xxhdpi (~480dpi) - Extra extra high
xxxhdpi (~640dpi) - Extra extra extra high
Bam! Something that used to take 10 minutes — resizing assets and dropping them into the right folders — now takes just seconds.
Unfortunately like any good machine, if you don’t maintain it and modernise your codebase. It slowly deteriorates until a point that a major overhaul is needed or it will break down eventually.
Since it’s an open sourced project, we have volunteers like MPArnold and Skeaner who managed to patch things up to get it chugging along for a bit longer. Until the faithful day when Android Studio Meerkat 2024.3.1 launched and pulled the final straw that broke the plugin’s back!
Woefully, it will take more than just patches to get this ancient plugin to work again. I have even popped a question in Stackoverflow and started a bounty to bring some attention to the issue and hopefully there would be an angel that would come fix it for me.

Stackoverflow
Indeed someone did came along and offered help, but not the one you would expect.

Martin Zeitler — Answer (More on Stackoverflow)
Essentially, I was given a reality check that there won’t be any more magical one liner to make this plugin run as good as new. He pointed out that the plugin’s Gradle is outdated until the point that it is already obsolete.
Martin was kind enough to gave me a few pointers to get started and resources to refer to. I spent days trying to just get the project to build and to no avail. Like how he has forewarned, it ain’t an easy feat.
Alas, just before I want to give up. I thought to myself, why don’t I just leverage the last remaining days of my ChatGPT Plus subscription to get something out of the door in this weekend.
So that’s exactly what I set out to do — starting a fresh plugin project in IntelliJ IDEA, with Nobuo Uematsu’s Bombing Mission playing in the background. My weekend mission had begun, diving into the Mako reactor of plugin development!
[embed]№1 Mako Reactor Bombing (Yes, that’s how I got the name for the plugin)
Mako — Android Drawable Importer (The Journey)
You need to break a few eggs to make an omelette. But, in this case it’s blowing up the whole Mako reactor! (FF7 reference)
If would be too ambitious and pointless to port every single feature from Marc’s plugin, since Resource Manager already support AndroidIcons and Material Icons Drawable Import.
I set my sights on something tangible and building something useful for my own workflow. Hence, I’ve set out the goal to just allow me to.
- Drag and drop an
xxxhdpisource image. - Downscale that source image to
mdpi,hdpi,xhdpiandxxhdpi. - Import them to it’s respective density bucket
drawablefolder.
Then, I would die a happy man.
Logic ⚙️
Surprisingly, I managed to get the downscale functionality to work really early on. There’s already native Java method that allows you to do exact what I wanted for scaling images.
val image = ImageIO.read(imageFile) ?: return@forEach
val baseName = imageFile.nameWithoutExtension
scales.forEach { (bucket, scale) ->
val scaledWidth = (image.width * scale).roundToInt()
val scaledHeight = (image.height * scale).roundToInt()
val resized = BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_ARGB)
val g2d = resized.createGraphics()
g2d.drawImage(image, 0, 0, scaledWidth, scaledHeight, null)
g2d.dispose()
val suffix = if (modifier.isNotBlank()) "-${modifier.lowercase()}" else ""
val bucketDir = File(outputDir, "drawable$suffix-$bucket")
bucketDir.mkdirs()
val originalFormat = imageFile.extension.lowercase()
val format = when {
ImageIO.getImageWritersByFormatName(originalFormat).hasNext() -> originalFormat
else -> FileFormat.PNG.extension
}
val outputFile = File(bucketDir, "$baseName.$format")
ImageIO.write(resized, format, outputFile)
}
UI 🎨
Now, it comes the hard part. How to make it actually usable and what came to my mind are these.
Must haves ✅
- Output directory
- Drag and drop / Input file directory
- Error Handling
Good to have 💫
- Modifiers
- Delete input selection
- Done toast message
It took me quite sometime to get it right, because ChatGPT kept suggesting me solutions that are either deprecated or using the wrong import packages. I supposed that’s because they’re trained on older data.
That’s when you have to take the reins and dig into Jetbrain’s documentation and figure out its implementation details like these components.
- JBTable, JBColor
- ExtendableTextComponent
- FileChooser
It’s quite impressive that just by prompt alone, ChatGPT can get me 80% of the way, although the remaining 20% can be a bit painful, but at the same time it’s quite an insightful experience to understand how plugin development works.
Actually it does remind me a lot of Java Swing development back in the college days.

ImageImportDialog.kt
Modifier
There’s one thing that Marc’s Android Drawable Importer library doesn’t support, that’s modifiers. What’s modifiers you say?
For example, density is already a modifier itself. The same file with different variants. So, what if you want to support images with different locales or have different images for light and dark mode?
Now, you can easily provide an optional modifier when importing your images. It might not apply to you, but it’s quite handy when you need them!
drawable-de-xxhdpi
drawable-night-xxhdpi
Bonus ⭐
After getting all the main functionality and my wish list items to work, I asked myself. What else could I add to future proof this plugin?
Two things came into my mind.
WEBP and JPEGXL File Format Support
.webp format is already out and about for sometime now, it should have better support. A quick ChatGPT prompt suggested to use [com.twelvemonkeys.imageio](https://github.com/haraldk/TwelveMonkeys) library and I was amazed how easy is it to implement it’s library from someone who is developing a plugin for the first time. It just works so seamlessly with Java.
There should be no reason not to support .webp!
dependencies {
implementation("com.twelvemonkeys.imageio:imageio-webp:3.10.1")
implementation("com.twelvemonkeys.imageio:imageio-core:3.10.1")
}
private fun registerWebPReaderIfNeeded() {
val registry = IIORegistry.getDefaultInstance()
val isRegistered = registry.getServiceProviders(javax.imageio.spi.ImageReaderSpi::class.java, true)
.asSequence()
.any { it.javaClass.name.contains(FileFormat.WEBP.extension, ignoreCase = true) }
if (!isRegistered) {
registry.registerServiceProvider(WebPImageReaderSpi())
}
}
JPEGXL on another hand, doesn’t have any “magically plug and play” third party library out there. Maybe the future hasn’t arrive yet for JPEGXL, but I’m already happy that we support .webp.
So, let’s move on!
Localisation
It’s often a neglected subject, but if I am already centralising my strings in MessagesBundle.properties like any good developer. So, what’s another extra 10 minutes to support the top 5 popular languages on IntelliJ platform?
dialog.title=Mako Android Drawable Importer
# Output Directory
dialog.label.outputDirectory=Output Directory
dialog.placeholder.outputDirectory=Select output directory
dialog.tooltip.outputDirectory=Browse
# Modifier
dialog.label.modifier=Modifier
dialog.placeholder.modifier=Optional
# Drag and Drop
dialog.title.dragAndDrop=Drop images here
dialog.description.dragAndDrop=Drag and drop PNG, JPG, JPEG or WEBP files here
dialog.label.imagesReadyToImport={0} image(s) ready to import
dialog.label.unsupportedFileFormat=Unsupported file format dropped
# Table
dialog.label.tableRowPreview=Preview
dialog.label.tableRowFilename=Filename
dialog.label.tableRowSize=Size
dialog.label.tableRowPathDirectory=Path directory
# Action
notification.success.title=Mako
notification.success.description={0} image(s) have been imported successfully.
notification.errorNoImages.title=Error
notification.errorNoImages.description=No images dropped.
A quick ChatGPT prompt, I was given the top 5 localisation used on IntelliJ platform and it immediately given me back the translations I needed!
- English
- Chinese
- Japanese
- Spanish
- Brazilian Portugese
Here’s Spanish for example.
dialog.title=Importador de Imágenes Drawable de Android - Mako
# Output Directory
dialog.label.outputDirectory=Directorio de salida
dialog.placeholder.outputDirectory=Seleccionar directorio de salida
dialog.tooltip.outputDirectory=Examinar
# Modifier
dialog.label.modifier=Modificador
dialog.placeholder.modifier=Opcional
# Drag and Drop
dialog.title.dragAndDrop=Suelta las imágenes aquí
dialog.description.dragAndDrop=Arrastra y suelta archivos PNG, JPG, JPEG o WEBP aquí
dialog.label.imagesReadyToImport={0} imagen(es) lista(s) para importar
dialog.label.unsupportedFileFormat=Formato de archivo no compatible
# Table
dialog.label.tableRowPreview=Vista previa
dialog.label.tableRowFilename=Nombre de archivo
dialog.label.tableRowSize=Tamaño
dialog.label.tableRowPathDirectory=Directorio de ruta
# Action
notification.success.title=Mako
notification.success.description={0} imagen(es) importadas con éxito.
notification.errorNoImages.title=Error
notification.errorNoImages.description=No se han soltado imágenes.
Final Product

With a some determination, perseverance and a dash of luck, I am quite satisfied with the outcome. Since I am an Android developer myself, maintaining this plugin in Kotlin would be second nature to me.
I have even add some structural housekeeping that ChatGPT just wouldn’t do it for you.
File Format
enum class FileFormat(
val extension: String
) {
PNG(extension = "png"),
JPG(extension = "jpg"),
JPEG(extension = "jpeg"),
WEBP(extension = "webp");
}
val supportedFileFormats by lazy {
FileFormat.entries.map { it.extension }
}
File Grouping
io.dontsayboj.mako
- AndroidDrawableImporterAction
- ImageImportDialog
ui
- Bundle
- Theme
model
- FileFormat
For maintainability, readability and future contributors, these are quality of life changes!
Afterword
Funny thing is, just as I started writing this article, I found myself wondering “do we even need density grouping for image assets anymore?” I mean, it’s 2025, and in the wise (paraphrased) words of MKBHD.
“Good phones are getting cheap, cheap phones are getting better.”
It’s true — not all six density buckets are as relevant as they once were:
ldpi (~120dpi) - Low density
mdpi (~160dpi) - Medium (baseline)
hdpi (~240dpi) - High
xhdpi (~320dpi) - Extra high
xxhdpi (~480dpi) - Extra extra high
xxxhdpi (~640dpi) - Extra extra extra high
In fact when I started out back in 2017, we didn’t even bother with ldpi. These days, most mid-tier and higher-end devices land comfortably in the xxhdpi or xxxhdpi range. And if you’ve already dropped support for Android Lollipop and below, performance concerns on low-end hardware aren’t as big a deal as they used to be.
Wait! So, did I just waste your time and mine rebuilding a tool for an outdated problem? 💭
I don’t think so. Density buckets still play a key role in keeping UI consistent across the Android ecosystem. And who knows? Maybe xxxxhdpi and xxxxxhdpi arejust around the corner.
More importantly, Mako still has its place — especially with features like modifier support for dark mode, locales, and other variants. And for me personally, this journey did more than bring back a tool I loved — it helped shattered my fear of building IntelliJ plugins and showed just how powerful integrating AI into your workflow can be.
Hopefully, Mako the Android Drawable Importer can bring some joy and saving time for our fellow developers out there to focus on bigger fish to fry!

메타데이터
- post_id
- 73adda89ddd3
- slug
- the-android-plugin-i-couldnt-live-without-so-i-rebuilt-it-from-scratch-73adda89ddd3
- url
- https://medium.com/bugless/the-android-plugin-i-couldnt-live-without-so-i-rebuilt-it-from-scratch-73adda89ddd3
- canonical_url
- https://medium.com/bugless/the-android-plugin-i-couldnt-live-without-so-i-rebuilt-it-from-scratch-73adda89ddd3
- author_url
- https://medium.com/@delacrixmorgan
- status
- ok
- fetched_at
- 2026-06-29 22:44:20