Plugin to Solve Pain Point in Android Studio

My favorite solution to a recurring task is automation. It comes from my love of process. But, when you have a process that often means doing the same thing over and over. As an engineer I get tired of the repetitiveness fast! So, I am always on the lookout for a way to speed up or automate parts of any process I have.

TLDR:

I wrote a plugin for bulk Build Variant Changes. O(n) -> O(1)

Since I work on Android, I am in Android Studio nearly every day. And one of the oft-repeated operations is switching from a Debug build to a Release build. If you work on a single-module project this is a pretty simple process, and takes less than 30 seconds:

  • Change Build Variant

  • Wait for Gradle refresh

  • Keep working

However, if you have a project with a couple modules, and maybe some different flavors this can quickly become onerous. Android Studio tries it’s best to match up different modules when and where it can. Many projects can still accomplish this in the same single change, and 30 second refresh time. But, the moment that you have a project that Android Studio can’t automatically configure all the relationships, you get into a longer process of Change… Wait… Hunt for what didn’t change… Change… Wait… repeat until you have gotten everything synced or the heat-death of the Universe, whichever comes first.

One project I work on can take upwards of 5-10 minutes to get everything switched over so that you can know for sure that everything is going to link properly. That’s a few minutes for any developer on the project, any time they need to change. In a normal day I may not ever change this. Then there are days when I need to switch back a dozen times for testing. That can be an hour or more of my day dedicated to just changing Build Variants. Not how I want to spend my time, and not how my manager really wants me using it either I would imagine! Just imagine the hours of lost developer time because of this one small task!

Bring on the curiosity!

I’m in the midst of a project that required me to change back and forth a bunch and I hated wasting all this time constantly having to massage the Build Variants across all my modules. So I go immediately wonder why I can’t just change all of them together. Immediately I Google and hunt for a solution. Surely I’m just missing something. But the results were all either how to change them simply (Android’s Developer documentation), or lots of people wondering the same thing with varying suggestions all of which were broken in the latest version of Android Studio (3.6+). [Note: I have added a link to my plugin to those Stack Overflow questions because it will help where nothing else could!]

I asked around at work, and in some of my local Android communities. Nobody knew. Everybody had experienced it, though, and as soon as I mentioned it I often heard “How is this not already a thing!?” It should be!

No documentation? Source Code will guide the way

The Android Studio layer on top of IntelliJ doesn’t have a lot of documentation. But, I’ve been here before. I’ve written a handful of plugins on top of code with no documentation by simply hunting my way through the source code. I already know that there is likely to be a class somewhere in the Android Studio source that references Build Variants.

BuildVariant classes not in my project that I could use as a guide

BuildVariant classes not in my project that I could use as a guide

Hunting around found that the BuildVariantUpdater was the right place to start. Which makes a lot of sense based on the name of the class! It also has some hooks for change listeners. Maybe there is already a way to plug an extra step there? Nope. Nothing there. Keep looking. Ultimately landed on updateSelectedBuildVariant() as the entry point. It takes a Project, moduleName, and selectedBuildVariant as inputs.

Now to figure out which modules need to be changed. Starting with the root of the tree that BuildVariantUpdater uses, which comes from the ProjectStructure Android class, and references the “leafModules” for the Project. This is simultaneously the best starting point and the root of the problem I’m trying to fix. So I do some testing with changing these, and find that it will miss project dependencies that are only used in certain build types (debugImplementation or testImplementation).

But before moving on I wanted to solve how to present the options. In my projects there are different combinations of BuildFlavors and BuildTypes that can be resolved across the project. (ie: debug, internalDebug, and internalFreeDebug). Using just the modules I found so far, I came up with a pretty straight-forward way to allow selections, just grouping by the full array of resolved variants per module. And then coded a simple interface for this selection (allowing for a variable number of DropDown selectors).

At this point, I know that I this works for ~95% of modules across the projects I work on, but seriously… can we just get EVERYTHING!?

Pulling everything together and one last hurdle

When I was first looking for those leafModules that let me minimally influence the way Android Studio makes it’s changes, I also found a way to get the all the modules within the project, regardless of whether Android Studio thinks they are related. It’s part of the ModuleManager that I need to find all the buildVariants already! I added in one last step after letting Android Studio do the heavy lifting that checks all the modules are set as expected. (If everything is already set, it’s essentially a No-Op)

I built the plugin and tested it.

SUCCESS!

Now I could change all the variants in my project that used to take 5-10 minutes in under 30 seconds.

From here it’s just compile the plugin and publish to the JetBrains repo! Except… wait. While everything compiles nicely in a debug mode on my machine for me to test it doesn’t compile to the release package I need to publish. Why? Because the crucial method I need is package-private. Insert face-palm here.

But, luckily Java is our friend in this case. I added a small top-level extension method to my project inside the same package, that let me make the call I needed from my plugin.

As an added bonus Pandora kindly let me also publish the source along with the plugin itself, so you are welcome to check it out, and contribute!

Build Variant Quick-Selector Plugin @ JetBrains

Build Variant Quick-Selector Source @ GitHub

Want to see how easy it is? Here ya go:

Kotlin Conversion And Plugin