The client

With Joyful Lips is a hymn app for Christian congregations, worship leaders, and individuals. The client wanted a catalogue of timeless hymns — lyrics plus professional-quality audio recordings — available on Android with offline access and a playlist engine that worship leaders could build their own service orders with. The audience is deliberately broad: Sunday-service leaders at congregations across India, individuals who want to carry the hymns of their childhood onto their phone, and choir directors who want a fast way to share a song list with singers.

The challenge

Three problems dominated the scope. First, the media service. Modern Android tightly restricts background and foreground services, and a music app without properly configured foreground-service media playback either gets rejected at Play review or gets throttled by the OS during playback. Google's FOREGROUND_SERVICE_MEDIA_PLAYBACK permission requires an explicit declaration and a credible policy justification; many apps get this review rejected on first submission. We designed the architecture to make the justification airtight from day one.

Second, offline playback. A worship leader may be using the app in a rural chapel with no connectivity. The app needs to download the audio of any hymn the user has bookmarked, cache it with a sensible storage policy, and stream only when the user actively requests a hymn that has not been downloaded.

Third, playlists and liturgical context. Worship leaders do not pick hymns at random — they pick based on the liturgical season, the Sunday of the year, or a thematic focus. The app surfaces suggested hymns by liturgical context and lets leaders save service orders as reusable playlists.

What we built

With Joyful Lips is a native Android app published on the Play Store. The home surface shows seasonal hymn recommendations. The catalogue browser supports search by title, first-line, theme, and author. A dedicated playlist surface lets users build, save, and reorder sets. The audio player runs in a foreground media-playback service with MediaSession, allowing lock-screen controls, Bluetooth-headset resume, and background playback without the OS killing the process.

For the Play Store submission, we wrote the FOREGROUND_SERVICE_MEDIA_PLAYBACK justification against the actual code path and declared it in the manifest with a specific foregroundServiceType. The app cleared Google Play review on the first submission — a concrete outcome we often cite when Android clients ask whether their own media app can clear the same bar.

Lyrics are rendered with a large-print mode for use during services, and a scrolling mode that advances in time with the audio. The audio is ExoPlayer-backed, with a local cache that persists bookmarked tracks across reinstalls via Scoped Storage.

Technology stack

  • Language: Kotlin.
  • Architecture: MVVM with Android Architecture Components, Hilt for DI.
  • UI: Material 3, View system primary, Compose for the playlist builder.
  • Media: ExoPlayer, foreground MediaPlayback service with MediaSession, MediaBrowserService for Android Auto compatibility.
  • Data: Room (catalogue), Firestore (remote sync of curated collections), Scoped Storage (audio cache).
  • Background work: WorkManager for download scheduling over Wi-Fi preference.
  • Backend services: Firebase Auth, Firestore, Cloud Storage, Analytics, Crashlytics.
  • Build: Gradle, GitHub Actions, Fastlane.

Outcome

With Joyful Lips shipped on time and cleared the FOREGROUND_SERVICE_MEDIA_PLAYBACK Play review on first submission. Post-launch production stability is strong and incremental updates ship quarterly. For Anoint Tech, it is the canonical example of how we handle Android media apps: tight scoping of the media service, offline-first audio caching, and a Play-review submission document that we write alongside the code so review is predictable.

Related

Shipping a media app on Android?

Media-playback apps are where Android policy is strictest. We have shipped several through Play review on first pass — we can help you do the same.

Start a conversation