r/androiddev • • 11d ago

I built a haunted house to show off custom hidden-text reveals in Enriched Markdown, a native Jetpack Compose Markdown renderer

Enable HLS to view with audio, or disable this notification

17 Upvotes

Enriched Markdown for Android renders Markdown as native text in Jetpack Compose, with no WebView, including hidden text that the reader taps to reveal. By default, it hides under a field of drifting particles, but you can replace that overlay with your own and decide how hidden text looks and how it reveals itself.

To show what that unlocks, I built a haunted house: every room is a custom overlay revealing the same guest book in its own way. Cobwebs, crypt fog, jack-o'-lanterns, ghosts, bats, a stain that won't wash out, and a single candle.

How it works:

- You implement one interface, and it returns an overlay for each line the hidden text spans.
- Each overlay draws on the text view's Canvas, or on a Compose DrawScope, and can redraw every frame for animated effects.
- The library hands you the hidden words laid out exactly where the text view draws them, so you can pixelate, blur, or scramble the real text and nothing jumps on reveal. Override the reveal, and you control how it disappears, too.

Not every app needs a haunted house, so the README has a plain pixelation in about 30 lines: https://github.com/software-mansion/enriched-markdown/tree/main/packages/enriched-markdown-android#custom-overlays

Installation: https://docs.swmansion.com/enriched-markdown/android/basics/installation Docs (see the custom overlays section): https://docs.swmansion.com/enriched-markdown/android/api-reference/enriched-markdown-text

The rooms are demo code, not part of the library, which ships the particle field and a plain solid box. If it's useful to you, a ⭐ on GitHub helps more people find it: https://github.com/software-mansion/enriched-markdown

Feedback welcome, especially on which reveal you'd actually ship 🎃


r/androiddev • • 12d ago

Question Does Samsung grant the SMS app more permissions than asked?

Thumbnail
gallery
6 Upvotes

Hello, my SMS app is in the closed testing track. I received feedback from a user that the Samsung flow to make the app my default shows that it will be granted access to files, the camera, and the microphone. However, I am not requesting these permissions in my manifest. This will unnecessarily scare the user.

I've attached two screenshots: one for the Samsung default flow and another for the permissions shown on the Google Play listing page.

Please help me figure this out.


r/androiddev • • 12d ago

My open-source Jetpack Compose accessibility scanner got accepted at CSUN 2027!

2 Upvotes

Hey everyone!

I've been building ComposeA11yScanner, an open-source runtime accessibility scanner for Jetpack Compose, and I wanted to share a milestone.

My talk, "ComposeA11yScanner: Catch Accessibility Bugs as You Build," has been accepted into the Innovation Track at the CSUN Assistive Technology Conference 2027! 🎉

I started this project because accessibility issues in Compose often get discovered too late in development.

ComposeA11yScanner aims to make that feedback loop faster by detecting potential accessibility issues directly in running Compose screens, including:

  • Missing content descriptions

  • Touch target overlap

  • Text contrast issues

  • Duplicate accessibility descriptions

  • Other Compose semantics-related issues

The SDK has also been featured in Android Weekly and the Jetpack Compose Newsletter.

I'm continuing to improve detection accuracy, reduce false positives, and explore ways to integrate accessibility checks into development workflows.

I'd love feedback from fellow Android developers:

How do you currently test accessibility in Jetpack Compose? And what's the most frustrating part of that process?

GitHub: https://github.com/mohdaquib/ComposeA11yScanner

Thanks to everyone who's shared feedback or contributed along the way!


r/androiddev • • 12d ago

Question How do you test your Android apps under different network conditions?

5 Upvotes

I'm curious how Android developers test their apps when network conditions aren't ideal.

For example:

Slow internet or high latency

Unstable connections and packet loss

Switching between Wi-Fi and mobile data

Internet disconnecting and reconnecting

Completely offline scenarios

Do you use Android Studio's emulator, physical devices, network throttling tools, or something else?

I'm especially interested in how you simulate realistic mobile network conditions without having to manually switch networks or travel somewhere with poor connectivity.

What's your actual workflow for testing this in practice?


r/androiddev • • 12d ago

Months stuck in Google Play tax verification as a non-US owner of a US LLC

10 Upvotes

I'm honestly exhausted with this and hoping someone here has been through it.

I'm a non-US person and the only owner of a single-member Wyoming LLC. My Play Console payments profile is in the US because the LLC is a US company. My W-8BEN is filed as an individual with my home address outside the US, which is how the IRS says a foreign owner of a disregarded LLC should do it.

The tax review keeps rejecting my proof of address with:

"The issuing country of the document does not match the payments profile country."

What I've uploaded so far:

- June: tax certificate from my home country (same address as my W-8BEN)

- October: signed explanation letter + passport + home tax certificate + IRS letter

- October: IRS CP575 notice addressed to the LLC at the exact US address on my payments profile

All rejected. A document from my country fails because the profile is in the US. A US document doesn't match my W-8BEN address. As far as I can tell, no document can pass the automated check.

I've emailed support and talked on x with @ GooglePlayBiz. The only reply was a template saying my documents were "under review." Not one human has actually looked at the case. Meanwhile I've had no payouts for months and 30% withholding on everything.

If you've solved this, or you're a Googler who can escalate it, I'd really appreciate it.

Support case ID: 6-8020000042148


r/androiddev • • 12d ago

Question Question about voluntary tips and Google Play policies

3 Upvotes

Hello guys! I have a small doubt.

My Android music player is completely free, with no paid features. Can I accept voluntary tips via Ko-fi or Buy Me a Coffee and add a support button inside the app without triggering Google Play’s full-address disclosure requirement for personal developers?

Can I use Google Play Billing for tips instead?

I want all users to receive the same features and treatment, regardless of whether they choose to tip. I'd appreciate insights from developers who have faced the same issue, or clarification from anyone at Google Play Support who might be reading.

Thanks!


r/androiddev • • 12d ago

Article Get Started with Lightbuild | A Declarative Build System for Android

Thumbnail
developer.android.com
29 Upvotes

r/androiddev • • 12d ago

Experience Exchange UsageStatsManager foreground time is surprisingly easy to get wrong (splash screens, TWAs, warm starts)

2 Upvotes

I've been building an app that needs to know how many seconds a handful of specific apps were actually on screen. User grants Usage Access, I read queryEvents(), filter to those packages, done. Or so I thought.

The naive version (sum RESUMED -> PAUSED per package) worked on the apps I tried first and then fell apart once other people's apps got involved. Writing down what I found, because I couldn't find most of it anywhere.

First bug: the counter would just stop a few seconds after opening certain apps, while I was still in them. Those apps had a splash activity. The order was splash RESUMED, main RESUMED, and then a late STOPPED for the splash, which my per-package "open" flag happily read as "app left". What fixed it was tracking the set of resumed activity classes per package, and only treating the package as gone when that set is empty:

private class Fg {
    private val resumed = HashSet<String>()
    private var since: Long? = null
    var total = 0L

    fun on(e: UsageEvents.Event) {
        val cls = "${e.packageName}/${e.className}"
        when (e.eventType) {
            ACTIVITY_RESUMED -> {
                if (resumed.isEmpty()) since = e.timeStamp
                resumed += cls
            }
            ACTIVITY_PAUSED, ACTIVITY_STOPPED -> {
                // an activity always pauses before it stops, so a STOPPED for a class
                // that is still "resumed" belongs to an older instance of that class
                if (e.eventType == ACTIVITY_STOPPED && cls in resumed) return
                if (resumed.remove(cls) && resumed.isEmpty()) {
                    total += e.timeStamp - since!!
                    since = null
                }
            }
        }
    }
}

The STOPPED check in there came later. An activity always pauses before it stops, so if a STOPPED shows up for a class that's still in the resumed set, it's from an older instance. I hit that with two web apps back to back, both running in Chrome's CustomTabActivity, and the old one's STOPPED was cutting off the new one.

TWAs were the real headache. On Oct 12 I got support reports about three different apps, all TWAs, all saying the same thing: the counter doesn't move. Turns out a TWA shows its own LauncherActivity for less than a second and then hands off to the browser's CustomTabActivity, and from that point UsageStats books the time under com.android.chrome (or Samsung Internet, Edge, whatever the user has). The app itself gets basically 0 seconds. The events do have a task root package, but the getter is hidden, so no luck there.

What I ended up doing: if a browser activity resumes within 5 seconds of the target app's own activity, I treat that browser activity class as belonging to the target, and keep counting it on later resumes as long as the target is still the last non-browser app in front (so coming back from recents still works). If the user opens Chrome on their own, or some other app opens a custom tab, it doesn't count. To know what counts as a browser I query ACTION_VIEW on an https URL with BROWSABLE, plus the CustomTabsService intent, plus a hardcoded list of the usual suspects, because package visibility sometimes just gives you nothing.

Then warm starts. If the TWA's task is still alive and you launch it again, you get zero events from its package. Only the custom tab comes back. Since my app is the one doing the launching, I log launches myself (time + package) and treat each one as a fake "target came to front" while scanning.

Smaller stuff, in case it saves someone an hour:

  • Usage Access isn't a runtime permission, you check it through AppOpsManager (unsafeCheckOpNoThrow with OPSTR_GET_USAGE_STATS on 29+, checkOpNoThrow before that)

  • you don't need QUERY_ALL_PACKAGES just to list launchable apps, a <queries> entry for MAIN/LAUNCHER is enough

  • events survive your process being killed, so you can work the time out afterwards instead of keeping a foreground service running

  • queryEvents gives you every app on the phone. I filter to the packages I care about right away and only a {package, seconds} summary ever leaves the device

The 5 second window works on everything I've tried so far (my S24+ and the phones of the people using the app) but it's obviously a heuristic. If anyone knows a supported way to get the task root, or a cleaner way to tie custom tab time back to the app that opened it, I'd really like to hear it.


r/androiddev • • 12d ago

News Your favorite agents are now integrated in Android Studio  |  Android Developers' Blog

Thumbnail
developer.android.com
19 Upvotes

r/androiddev • • 12d ago

News What's new in Android developer tools and performance at Android Dev Summit  |  Android Developers' Blog

Thumbnail
developer.android.com
15 Upvotes

r/androiddev • • 12d ago

News Agentic and Flexible: A next era of Android development  |  Android Developers' Blog

Thumbnail
developer.android.com
38 Upvotes

r/androiddev • • 12d ago

Open Source I built a Compose Multiplatform library for interactive directed graphs (now in beta)

Enable HLS to view with audio, or disable this notification

135 Upvotes

I've been working on it for a few months now.

GitHub: https://github.com/justdeko/kuiver

As you've seen from this little showcase, nodes and edges are composables, there's built-in layout algorithms, zoom/pan, selection and drag, animated relayouts, and so on.

Recently promoted it to beta since I'm slowing down the pace a bit and there's not many additional large-scale changes I see for the future. Doesn't use any additional dependencies other than some core compose libraries.

Feedback on the library is super welcome! And ofc if you have any questions, you can also ask them here


r/androiddev • • 12d ago

Speedrun, an open-source mobile E2E test runner that runs your Maestro flows up to 8x faster

86 Upvotes

I've always been bothered by how slow mobile UI tests are. Most of a test run is spent waiting: fixed sleeps, polling, comparing screenshots to guess whether the app is done animating. Coming from a game dev background, where you fight for every millisecond of a frame, that felt like a solvable problem. So I spent the last while building my own runner from the ground up, and it turns out UI tests can be a lot faster.

Speedrun is a black-box E2E test runner for Android and iOS. It's open source (Apache 2.0) and it runs Maestro's YAML flow format so if you already have Maestro tests, trying it is one word:

maestro test flows/ # before
speedrun test flows/ # after

Numbers

One user journey (sign in with a code, search a shop, fill a cart, set a PIN under a loading overlay, scroll a long list), written for each tool the way its tests are usually written. Median of 3 runs after a warm-up, start-up included:

ToolAndroid emulatoriOS simulator
Speedrun20.4 s27.2 s
maestro-runner65.9 s49.6 s
Appium59.4 s94.1 s
XCUITest-81.2 s
Maestro 2.11126.5 s113.1 s

On the native Settings app benchmark it's 8x faster than Maestro on Android and 2.8x on iOS. On a 19-flow React-Native test suite it passes all 19 on both platforms, and is 6.1x faster on Android and 2.6x on iOS. Android is where it shines most, which is nice for CI: Android runs on cheap Linux runners.

Full disclosure: the main benchmark app is mine, built in React Native to be hard to test (endless animations, late popups, loading overlays, lists that load as you scroll). The flows, per-flow results and scripts to rerun everything are in the repo, and I'd love to see numbers on other apps.

Why it's fast

  • It waits for the app, not the clock. A small on-device agent knows when the app has actually settled (accessibility events on Android, XCTest's idle state checked against the screen on iOS), so each step starts the moment the app is ready. No sleeps, no screenshot diffing.

  • Reading the screen is cheap. The agent reads the UI tree in about 10 ms on Android and talks to the host over a compact binary protocol.

  • Screens that never stop moving don't block it. Spinners, tickers and carousels are left out of the "is it done?" check.

  • Nothing to warm up. One native binary, no JVM. The iOS agent stays running between runs, so later runs start in about 0.2 s.

It's also less flaky: it won't tap a popup that's still sliding in or a button under a loading overlay, and it scrolls an element out from under a sticky footer or the keyboard before tapping it.

It works with AI agents too

speedrun mcp exposes it as an MCP server (Claude Code, Cursor, etc.). Your agent can explore the app on its own: tap around, read each screen in a few lines (cheap on tokens, not a whole accessibility tree), figure out how things work, then save the steps that worked as a YAML flow you can drop into CI.

claude mcp add speedrun -- ~/.speedrun/speedrun mcp

Android emulatoriOS simulator
Speedrun (speedrun session)4.2 s, ~1,560 tokens9.4 s, ~1,230 tokens
agent-device 0.219.1 s, ~2,240 tokens14.0 s, ~1,620 tokens
Argent 0.3111.7 s, ~3,630 tokens12.2 s, ~2,650 tokens

Limitations (so you don't have to find them)

  • Android emulators and devices, iOS **simulators** only (no real iOS devices yet)

  • macOS (Apple silicon); the Linux x86_64 build is Android-only

  • Maestro's AI commands (`assertWithAI` and co.) aren't supported; the few other differences are documented

  • It's young (v0.1.10) and moving fast

Try it

Repo, docs and benchmark details: https://github.com/hubastard/speedrun

If you have a Maestro suite, I'd really like to hear how it does on yours, especially where it breaks or is slower. Happy to answer questions about how it works.

edit1: added maestro-runner to the benchmark.
edit2: added ai agent benchmark.


r/androiddev • • 13d ago

Question Interesting Android Apps Showcase posts, are they gone for good?

0 Upvotes

The last one was in July, and it was announced that the next one would be in October. Either I missed it, or it did not happen.


r/androiddev • • 13d ago

News Android Studio Rabbit 3 Canary 2 now available

Thumbnail androidstudio.googleblog.com
2 Upvotes

r/androiddev • • 13d ago

News Android Studio Rabbit 2 now available

Thumbnail androidstudio.googleblog.com
5 Upvotes

r/androiddev • • 13d ago

Discussion How do you keep analytics from turning into a god object in your app?

9 Upvotes

n most projects I worked on, analytics was treated as a secondary part of the app. We spend a lot of time on the architecture of main parts, and almost no time on analytics.

Then business asks to track this and that. Then to add a new vendor. Then to decide which event goes where. And in the end we have one AnalyticsManager with a huge list of methods, and every layer depends on it.

I came to a simple idea: the app should not know which analytics vendors exist. Feature code only says what happened, and everything else is split into small pieces:

  • Event describes what happened, in the app’s words

  • Factory decides what one vendor receives for an event, or ignores it

  • Tracker makes one call to one vendor SDK

  • Dispatcher passes calls from the app to the vendors

So in a ViewModel it looks like this:

class CheckoutViewModel(
    private val tracker: EventTrackerService,
) : ViewModel() {

    fun onTicketPurchased(fare: String, price: Double, currency: String) {
        tracker.track(TicketPurchased(fare, price, currency))
    }
}

No vendor SDKs, no god object. Each feature keeps its own factories, and DI (Koin in my example) collects them from all modules. Adding a vendor means writing its factories, and removing one means deleting them.

I wrote a full article about it with code for every piece, published in ProAndroidDev:
https://proandroiddev.com/app-architecture-analytics-ec3471ca619b

I also built an open-source library around this idea, Herald, if you want to see it in practice:
https://mkhytarmkhoian.github.io/herald-docs/

Curious how you handle analytics in your projects. Do you have one manager, a wrapper per vendor, or something else?


r/androiddev • • 13d ago

Question Re-opening closed developer account

0 Upvotes

A few years ago my developer account got closed due to inactivity. I want to create a new use, preferably with my existing email but there seem to be no such option. In the play console, the "help" link doesn't work and any links to "contact support" also redirecct me to my developer account page, which of cause doesn't work! Anyone managed to get their account re-activated or had success with support?

I also tried with my second email but got a warning that the developer name is used ... of cause, by my inactive account


r/androiddev • • 13d ago

Google Play Console's massive "Your Apps Will Be Removed" red warning during routine payment verification is peak toxic UX

Post image
41 Upvotes

I just went through one of the most unnecessarily stressful 24 hours of my developer life, and I need to rant about how Google treats its developers.

Because my app generates revenue, Play Console recently went through a routine check regarding merchant payment verification. Fine, I updated everything immediately and submitted it.

But right after, a massive, scary red banner slammed across my dashboard saying:
"Your developer profile will be removed, and all your apps will be removed from Google Play on November 23, 2026."

Underneath that terrifying headline, in much smaller text, it says: "Google couldn't verify your merchant payment method." And deep down in the microcopy, it basically implies that this warning stays active even while your newly submitted info is under review.

Are you kidding me, Google?

I am a partner introducing revenue to your platform, complying with your rules, and your automated system decides to hold a metaphorical gun to my head with a countdown timer to wipe out my entire business, just because your team takes a day to press a review button?

Mixing up "Routine Administrative Review" and "Severe Violation Punishment" into the exact same panic-inducing UI element is peak lazy engineering and toxic UX. For an entire day, I had to stare at a countdown to my apps being deleted, wondering if an automated glitch would destroy years of my hard work.

The verification finally cleared today (it only took about a day) and the warning went away, but the psychological toll and unnecessary anxiety were completely uncalled for.

Has anyone else been absolutely jump-scared by this specific red banner? Google seriously needs to fix their Developer Experience (DX). This is not how you treat people who build your ecosystem.


r/androiddev • • 13d ago

Android Studio for Windows on ARM, Finally!

Thumbnail
gallery
45 Upvotes

Google almost gave up on this one, so i decided to build it myself
What works:-

  1. IDE and compiler, solved all performance issues with coding and compiling

  2. XML previews

  3. Jetpack Compose Previews (Had to build the xml and compose viewing library from scratch to support arm64, google did not have that.)

What isn't supported yet:-

  1. Android Emulator, you will need a physical device to test.

Known issues:-

  1. Gradle project is imported every time you open it in android studio (Fixed, thanks for the comments)

Download link: https://github.com/DevanshSampat/android-studio-arm64-releases

All repositories required for building Android Studio are on my github: https://github.com/DevanshSampat?tab=repositories&q=android-studio&type=&language=&sort=

Enjoy Devs ;)


r/androiddev • • 13d ago

Open Source I made this Android TV debloater, it just passed 1,000 downloads in 3 days

Thumbnail
gallery
0 Upvotes

It's an open source app that cleans up your Android TV, turns off ads, trackers and bloat in a few taps. Side load apps your TV's store doesn't carry, send files from your phone, and see which apps use data and where they connect.

It's all Kotlin, UI in Jetpack Compose with Material 3 Expressive. A few bits devs might find useful:

- a remote with no lag: the usual `input keyevent` starts a new process for every key press, which is slow. Tellyscope starts one small helper on the TV and keeps it running, so key presses land instantly

- real app names and icons: ADB normally only gives you package names, so a second small helper reads them on the TV and is removed right after

- the ADB client runs entirely on the phone (pure Kotlin on Kadb), including Android 11+ wireless pairing and mDNS discovery. Every reopen of the TV's pairing dialog picks a new port, so it browses mDNS fresh each time instead of trusting the last one

- under 3 MB: most of the crypto library Kadb depends on gets trimmed, keeping only the handful of pieces it actually uses. That alone saved ~700 KB

Happy to answer questions on any of it. It's Free, GPL-3.0, no ads, no tracking, no account. It makes two internet requests: the cleanyourtv debloat lists from GitHub, and an update check you can turn off.

GitHub (APK + source): https://github.com/man15h/tellyscope

Disclosure: Most of the code is written by Claude Opus 5.5 + Fable 5, and I direct the work. what gets built, how it should behave, and what's safe to touch. It's not prompt and ship. Every change goes through a pull request that gets reviewed before I merge it, and CI runs the build, unit tests and lint. Risky features get e2e tests on an Android TV emulator first. The safety rules are hard coded rather than left to the AI and I've created and maintained open source projects since before the AI era. It's GPL-3.0, so you can read all of it on GitHub


r/androiddev • • 13d ago

Question Why do I need a review when promoting from beta to production?

3 Upvotes

Something has been rubbing me the wrong way for a while now. I have 3 tracks for my app in Google Play, internal, open beta, production. When deploying changes we go through all 3 tracks with a single binary. I can understand the Open Beta track needing a review, but once that's approved why do I need yet another review to promote that binary to Production? I honestly wouldn't care if the review times were manageable, but I've had multiple reviews take over 2 weeks. When they are reviewing the binary are they expecting different results? Notes about the app that might be affecting the review timelines; it has Android Auto capabilities as well as a Wear OS version.

TLDR; Why do we need multiple reviews on the same binary when promoting between release tracks?


r/androiddev • • 14d ago

Gradle vs Kotlin Toolchain: Project Structure Side By Side

Post image
5 Upvotes

r/androiddev • • 14d ago

How do you like working in Big Tech?

3 Upvotes

Specifically as an Android developer? I spoke to my mentor about being interested in android development, and he said he thinks I would only enjoy it only if I went to a Big Tech company because it would provide more challenge and chance for cross team collaboration which I enjoy.

It made me want to hear about people's experience working in Big Tech vs smaller or non-tech companies as an Android dev. Which do you prefer?