I like taking apart Android apps that catch my attention to see how they work. It’s something I explored in my M.Sc. thesis, too.
With so much attention on Anthropic and Claude Code, I was curious about how their Android app was built. Anthropic has written a lot about Claude and Claude Code, but I couldn’t find much on their blog about the engineering behind the Android app. So I decided to unpack it and take a look myself.
One of the first things that stood out to me was a SharedPreferences key called is_ant.
Following that trail, I found a hidden internal settings menu used by Anthropic engineers to test, debug, and configure the app during development.
This post follows that trail and explores what was inside.
What survives optimization
Android release builds often use R8 or similar tools to shrink, optimize, and obfuscate code. R8 can remove unreachable code and shorten class, field, and method names. Those shorter names reduce the size of the app and also make decompiled code harder to follow. But even after that, a ton of useful information can survive this step.
SharedPreferences keys are one of my favorite places to start. Unlike class and method names, these strings often remain readable. Tracing where a key is used often tells you what it controls.
Following the Ant 🐜
I found about 100 preference keys in the app. Several were interesting, but one immediately stood out: is_ant.
The code that set is_ant to true checked whether the account’s email ended in @anthropic.com. This strongly suggested is_ant was a local indicator for Anthropic employee accounts.
Here’s a Kotlin reconstruction of the logic:
fun updateAccount(account: Account) {
if (account.email.endsWith("@anthropic.com")) {
sharedPreferences.edit()
.putBoolean("is_ant", true)
.apply()
}
// Remaining account setup...
}
Next, I followed where is_ant was read. One of those reads controlled whether an entry appeared in the Settings screen. In the same simplified form:
if (sharedPreferences.getBoolean("is_ant", false)) {
showInternalSettingsEntry()
}
Seems like we’ve found the internal settings entry point! The next question was whether the screen behind that entry had survived optimization too.
Opening the internal settings
On a rooted test device, I could have changed the stored preference directly. I was exploring other parts of the app at the same time, though, so I decided to repackage it instead: unpack the APK, modify it, rebuild it, and sign the rebuilt APK so I could install it on my test device.
I like to start by changing something obvious. It confirms that the rebuilt app actually contains my changes and makes it easy to tell apart from the original. In this case, I changed the “New chat” label. It worked!

Next, I changed the condition guarding the internal settings entry so it always passed. After rebuilding the app and opening Settings, the entry point appeared.

Seeing the entry point didn’t necessarily mean the screen behind it had survived optimization. Luckily, in this case, it had!
A tour of the Internal Settings
The menu had several tools for developing and testing the app. Here’s a slow scroll through the top level.
Internal Settings bubble
Enabling “Internal Settings bubble” added a floating, draggable Ant button that opened the internal settings. It snapped to the edges of the screen and was fun to move around.
It’s a small convenience, but it saves a trip through Settings when you are repeatedly changing a debug option.
Jank overlay
“Show jank overlay” displayed UI performance statistics. This was connected to JankStats and Android’s FrameMetrics API. Having those measurements available inside the app makes it easier to investigate a slow interaction while reproducing it.
Age signals
“Age Signal override” exposed controls for simulating different age signals.

Push notifications
There was also a dedicated screen for testing push notifications.

Feature flag overrides
I found GrowthBook integration in the app, along with an “Override feature flag” screen for changing local flag values and configurations.
This was one of the more interesting screens to explore. The feature flags can provide clues about experiments and possible future features. The list deserves a closer look in a separate post.
Simulating unreliable networks
The “Network Simulation” screen offered controls for adding latency and forcing request, upload, and timeout failures.

Switching the API endpoint
“Select API Endpoint” offered Production, Staging, Localhost, and a custom backend option.

The staging hostname was publicly reachable, but Cloudflare Access sat in front of it and required authentication.

There’s more here than I could fit into one post, especially in the feature flag overrides. I’ll save that rabbit hole for another time. For now, these screens offer a small glimpse into some of the tooling used by Anthropic’s Android team.
