Java to Kotlin Migration
Modernizing a 7-Year-Old Android App with Gemini
It’s been over 6 years since I first released the Clouds of Thought app. At the time, I built it as a simple Java-based WebView wrapper because I wanted a “mobile presence” without the overhead of maintaining a separate codebase for every blog post.
Fast forward to 2026, and the app was in a sad state. It was broken on latest Android versions, the code was legacy (Java, old Gradle, ancient SDK targets), and honestly, it wasn’t easy to even get it to run on a modern machine, let alone maintain it.
Below is a story of how I took an almost non-functional Android app in Java and migrated it to the latest Kotlin Android stack to give it a new life of its own!
The Problem: Legacy Debt
- Broken on New Androids: The app would crash or behave weirdly on newer devices because it wasn’t respecting modern permissions or SDK requirements.
- Maintenance Nightmare: Every time I tried to open the project, I’d face a wall of Gradle errors and deprecated library warnings.
- Strategic Opportunity: Having focused on other domains recently, I saw this as a chance to explore modern Android tooling and AI-assisted development in unfamiliar codebases.
Despite all this, there’s a certain personal satisfaction in keeping your own creations alive. It’s a low-cost, high-reward project for the soul.
The Solution: Agentic AI (Gemini)
Instead of manually fighting with build.gradle files and converting Java to Kotlin by hand, I decided to use Gemini (integrated into Android Studio) as my “lead engineer.”
What followed wasn’t a “one-shot” magic trick. It was a series of iterative prompts where I acted as the project manager and validator, guiding the AI through each step like directing a skilled team.
My Strategy:
- Desired State First: I always told the AI exactly what I wanted the final state to look like (e.g., “Migrate this to Kotlin,” “Update to SDK 35,” “Use Material 3”).
- Verification Loop: After every change, I’d ask how to check the progress. “How do I build this?”, “How do I run the release build?”, “Where is the output?”
- Iterate, Don’t Revert: If a build failed, I didn’t panic and revert. I gave the error back to the AI and we fixed it together.
My Learnings: Handling the “Deep” Gradle Issues
The most interesting part was when the basic migration was done, but the build was still “bleeding” warnings. I saw errors like: API 'unitTestVariants' is obsolete and has been replaced with 'AndroidComponentsExtension'.
In the past, I would have spent hours on StackOverflow. With Gemini, I learned about the Built-in Kotlin support introduced in Android Gradle Plugin 9.0.
The Technical Shift:
- Removing Legacy Plugins: I had to remove the
org.jetbrains.kotlin.androidplugin entirely. AGP 9.0+ now handles Kotlin natively. - Cleaning
gradle.properties: Modern AGP versions are much more opinionated. I had to strip away a lot of legacy flags likeandroid.newDsl=falseandandroid.builtInKotlin=falsewhich were actually hindering the migration. - Kotlin DSL Updates: Migrating from
kotlinOptionsto the newerkotlin.compilerOptionsor simply letting AGP handle it viacompileOptionswas a key step in silencing those stubborn warnings.
This experience reinforced how staying attuned to platform evolution is crucial for senior engineers.
The Results
In a single afternoon, the transformation was complete:
- Kotlin Everywhere: The legacy Java code is gone. Everything from
MainActivityto theWebViewClientis now clean, modern Kotlin. - Modern UI: Switched to Jetpack Compose and Material 3.
- Build Success: All warnings resolved,
compileSdkupdated to 35, and the build is finally “green.” - Release Ready: I’ve successfully generated signed
.aaband.apkfiles for version 17.0, ready for the Play Store.
This not only revived the app but also highlighted scalable approaches for modernizing enterprise legacy systems.
Code Snippets: Then vs. Now
Old Java WebView Setup:
siteWebview = (WebView) findViewById(R.id.siteWebview); siteWebviewSettings.setJavaScriptEnabled(true); siteWebview.loadUrl("https://vishwarajanand.com/blog/");
New Kotlin + Compose Setup:
AndroidView(
factory = { context ->
WebView(context).apply {
settings.javaScriptEnabled = true
webViewClient = CotWebviewClient()
loadUrl("https://vishwarajanand.com/blog/")
}
},
modifier = Modifier.fillMaxSize()
)
Final Thoughts
You don’t need to be an expert in the latest framework to keep your old projects running. With tools like Gemini, the barrier to “modernization” has never been lower. It’s not about the AI doing everything for you; it’s about having a partner that knows the syntax so you can focus on the architecture and the “why.”
The app is back. It’s fast, it’s modern, and most importantly, it’s alive. If you have an old project gathering dust, maybe it’s time to let an AI agent help you blow the dust off. And for fellow engineers, consider how AI can empower your modernization efforts—share your stories below!
TECH
android kotlin genai gemini