Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Android News — Two-App Architecture Exercise

A news experience split across two independently installable Android apps that communicate over IPC:

App Role Key tech
BackendApp Owns the data. Bundles the article dataset as JSON, seeds it into Room, and serves filtered results over a ContentProvider. Filtering runs here. Room, kotlinx.serialization, Hilt, ContentProvider
UIApp Owns the experience. Renders the news list and filter controls, and requests filtered articles from BackendApp via ContentResolver. Jetpack Compose, MVVM + StateFlow, Hilt, Coil

The two apps have different applicationIds and no compile-time or runtime dependency on each other. Their only contract is the ContentProvider URI + query-parameter shape, mirrored in each app's ArticleFilter.

Architecture at a glance

UIApp                                   BackendApp
─────                                   ──────────
ArticleListScreen (Compose)
   ↕  StateFlow<ArticleUiState>
ArticleViewModel
   ↓  ArticleFilter
ContentResolverDataSource ──query()──▶  ArticleContentProvider
   ▲                                       ↓  ArticleFilter.fromUri()
   └──────────── Cursor ◀───────────────  ArticlesRepository ─▶ Room (seeded from assets JSON)
  • IPC mechanism: a ContentProvider returning a Cursor — the idiomatic Android surface for tabular cross-process data. Filters travel as URI query parameters.
  • Security: the provider is exported but guarded by a signature-level custom permission, so only apps signed with the same key (our UI app, and any future first-party caller) can read it.
  • Extensibility: filters are a single ArticleFilter value object on each side. Adding a new filter type is a localized change — no interface, data-source, or IPC-shape rewrite.

See each app's README for the deeper rationale.

Build & run (out of the box)

Requires JDK 17+ and the Android SDK; open either app in Android Studio (Ladybug or newer), or build from the command line. The two apps are separate Gradle builds.

# 1. Build both APKs
(cd BackendApp && ./gradlew assembleDebug)
(cd UIApp      && ./gradlew assembleDebug)

# 2. Install BackendApp FIRST (UIApp resolves its provider at startup)
adb install BackendApp/app/build/outputs/apk/debug/app-debug.apk
adb install UIApp/app/build/outputs/apk/debug/app-debug.apk

Same signing key required. UIApp can only hold BackendApp's signature-level permission if both APKs are signed with the same key. Debug builds from the same machine satisfy this automatically.

Verify the apps are talking

# Query the provider directly — should print ~129 rows
adb shell content query --uri content://com.unity.backendapp.provider/articles

# With filters
adb shell content query --uri "content://com.unity.backendapp.provider/articles?titleQuery=sport&ratingMin=4"

Then launch UIApp: the list populates from BackendApp, and the search field + rating slider + Apply Filters button round-trip through IPC. Launch BackendApp on its own: its dashboard reports the stored article count, confirming the dataset seeded independently of the UI app.

Tests

(cd BackendApp && ./gradlew testDebugUnitTest)          # filter SQL, JSON parsing
(cd BackendApp && ./gradlew connectedDebugAndroidTest)  # provider IPC, Room DAO   (device/emulator)
(cd UIApp      && ./gradlew testDebugUnitTest)          # ViewModel state machine
(cd UIApp      && ./gradlew connectedDebugAndroidTest)  # cursor mapping, URI encoding, Compose card

AI tooling

See AI_TOOLING_WRITEUP.md for an honest account of how AI tools were used, where they accelerated the work, and where they were wrong.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages