# Compose Multiplatform Migration

**URL:** https://community.metabrainz.org/t/compose-multiplatform-migration/814456
**Category:** ListenBrainz
**Tags:** listenbrainz-android, gsoc
**Created:** [March 17, 2026, 5:43pm UTC](https://community.metabrainz.org/t/compose-multiplatform-migration/814456 "2026-03-17T17:43:13Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![anuj990](https://community.metabrainz.org/letter_avatar_proxy/v4/letter/a/9fc29f/32.png) [@anuj990](https://community.metabrainz.org/u/anuj990)
#### Post date: [March 17, 2026, 5:43pm UTC](https://community.metabrainz.org/t/compose-multiplatform-migration/814456/1 "2026-03-17T17:43:14Z")

</div>

# GSoC 2026 Proposal: Compose Multiplatform Migration — Anuj

**Applicant:** Anuj (IRC: anuj\_)  
**Email:** [aanuj8619@gmail.com](mailto:aanuj8619@gmail.com)  
**GitHub:** [anuj990](https://github.com/anuj990)  
**LinkedIn:** [anuj990](https://linkedin.com/in/anuj990)

* * *

## About Me

I am a sophomore at the Indian Institute of Information Technology Jabalpur  
(IIITDM Jabalpur) pursuing B.Tech in Electronics and Communication Engineering.  
I started my coding journey with C++ for competitive programming, and through  
my college’s open source programme BSOC (BitByte Summer of Code) I discovered  
Android development. Since then my primary stack has been Kotlin, Jetpack  
Compose, and MVVM and over the past few months I have been working directly  
with Kotlin Multiplatform and Compose Multiplatform.

I am particularly drawn to this project because it is not about adding new  
features it is about making the existing codebase structurally so that  
a completely new group of users (iOS users) can access ListenBrainz for the  
first time. That kind of foundational work is what excites me most.

I have been contributing to ListenBrainz Android since December 2025 across  
both the `main` and `cmp` branches. I have read the code deeply, received  
review feedback from jasje, acted on it, and gotten PRs merged.

* * *

## Project Summary

ListenBrainz Android is currently available only on Android which means iOS  
users cannot access this service at all. This project proposes  
completing the migration of the existing codebase to Kotlin Multiplatform (KMP)  
and Compose Multiplatform (CMP), so that both Android and iOS users can run  
ListenBrainz from a single shared codebase.  
The migration is already actively in progress on the `cmp` branch, tracked  
under [Epic Issue #614](https://github.com/metabrainz/listenbrainz-android/issues/614)  
some migrations is already done. My GSoC work focuses on completing the remaining migration in a structured phase-by-phase approach, resulting in a fully working Android and iOS app by the end of the summer.

The final architecture will look like this:

```auto
commonMain 
├── Models, Repositories, ViewModels
├── Network (Ktor), DataStore KMP, Room KMP
├── Compose UI (JetBrains CMP), Navigation (Voyager)
│
├── androidMain iosMain
│ ├── ExoPlayer ├── AVPlayer
│ ├── WorkManager ├── BGTaskScheduler
│ ├── Permissions ├── Native Permissions
│ └── SplashScreen └── LaunchScreen
│
├── androidApp iosApp

```

* * *

## Current State of Migration

### Already Done

- KMP project structure + shared module
- Gradle KMP plugin configuration
- Source sets (commonMain, androidMain, iosMain)
- Dagger/Hilt → Koin
- Gson → Kotlin Serialization
- Retrofit → Ktor
- coil/glide → coil3
- androidsvg → coil SVG decoder
- JUnit → kotlin-test
- turbine (already KMP compatible)
- androidx.datastore → DataStore KMP
  - `AppPreferencesImpl` in commonMain
  - `PreferenceKeys` in commonMain
  - `DataStorePreference<T>` interface in commonMain
  - `PlatformContext` expect/actual in place
  - `createDataStore` expect/actual in place

- compose ratingbar → custom CMP component
- threetenabp → kotlinx-datetime

### Partial

- **[socket.io](http://socket.io) → ktor-websockets** — PR in review
- **Room → Room KMP** — infrastructure done but DAOs, entities, and database classes still  
in androidMain
- **androidx.preference → DataStore KMP** shared module done, minor  
cleanup remaining in app module
- **androidx.lifecycle → KMP ViewModel** PR #732 attempted SocialViewModel  
migration but used plain `CoroutineScope` instead of proper KMP `ViewModel()`  
with JetBrains `lifecycle-kmp="2.8.0"` i will it update soon

### Remaining for GSoC

- Logger-Android → Kermit
- jsoup → Ksoup
- ktor-client-okhttp → platform engines (darwin/okhttp)
- Remove chucker
- Androidx.paging → paging-common
- Koin platform-specific setup:  
Move koin-core to commonMain
  - koin-androidx-compose → koin-compose
  - Remove koin-androidx-workmanager (WorkManager is Android-only)

- lottie/lottie-compose → Compottie
- androidx.lifecycle → KMP ViewModel (full migration)
- androidx.compose.\* → JetBrains CMP
- androidx.navigation → Voyager
- compose-shimmer → KMP shimmer
- ExoPlayer → interface (AVPlayer for iOS)
- spotify-app-remote → stub for iOS
- WorkManager → BGTaskScheduler (expect/actual)
- accompanist-permissions → platform-specific
- accompanist-systemuicontroller → platform-specific
- share-android → platform-specific
- androidx.browser → platform-specific
- androidx.core.splashscreen → platform-specific
- app-update → Android-only
- androidx.palette → Custom implementation

* * *

## Migration Plan

### Phase 1 — Final Build Infrastructure

Update `libs.versions.toml` to the final target state adding all KMP/CMP  
plugins (`jetbrains-compose`, `lifecycle-kmp`, `voyager`, `kermit`,  
`compottie`, `ksoup`, `shimmer-kmp`) and dependency versions. Set up the  
final `shared/build.gradle.kts` with proper `commonMain`, `androidMain`,  
and `iosMain` source sets so that approximately 80% of all code lives in  
`commonMain` by the end of GSoC.

### Phase 2 — Domain Models

Move all model classes from `androidMain` to `commonMain`. Replace Gson’s  
`@SerializedName` with kotlinx.serialization’s `@SerialName`, annotate any  
missing `@Serializable`, and replace all Java date types (`java.util.Date`,  
`Calendar`, `SimpleDateFormat`) with `kotlinx-datetime` equivalents  
(`Clock.System.now()`, `Instant`, `LocalDateTime`).

### Phase 3 — Network Layer

This is one of the most impactful phases. Migrate `createBaseHttpClient`  
to `commonMain` via `expect/actual` platform engines (OkHttp for Android,  
Darwin for iOS). Remove `ChuckerInterceptor` (Android-only debug tool) and  
replace it with Ktor’s built-in `Logging` plugin using Kermit as the  
underlying logger. Replace `Logger-Android` with Kermit throughout.  
Replace `socket.io-client` with `ktor-client-websockets`, handling the  
[Engine.IO](http://Engine.IO) handshake manually (0 → 40 → subscribe, ping/pong 2 → 3) with  
automatic reconnect logic. Replace `jsoup` with `Ksoup` for HTML parsing.  
Migrate all Ktorfit service interfaces and Koin `networkModule` to shared.

### Phase 4 — Room KMP

Move all entities (`SongEntity`, `AlbumEntity`, `ArtistEntity`,  
`PlaylistEntity`, `ListenSubmitBody.Payload`), all DAOs, and both database  
classes (`BrainzPlayerDatabase`, `ListensSubmissionDatabase`) from  
`androidMain` to `commonMain`. Add `expect/actual` database builders to  
handle platform-specific file paths — Android uses `Context.getDatabasePath()`  
while iOS uses `NSFileManager` + `NSDocumentDirectory`.

### Phase 5 — Repository Layer + Paging

Migrate all repository interfaces and implementations to `commonMain`.  
Replace all `java.util.Calendar` and `SimpleDateFormat` usages with  
`kotlinx-datetime`. Migrate all paging sources from `androidx.paging`  
to `paging-common` so they work on both platforms. Keep `paging-runtime`  
and `paging-compose` in `androidMain` only. Migrate Koin `repositoryModule`  
to commonMain.

### Phase 6 — ViewModel Migration

Add JetBrains `lifecycle-kmp="2.8.0"` to `commonMain` — this is critical.  
ViewModels must extend KMP `ViewModel()` and use `viewModelScope` which  
auto-cancels on both platforms. Plain `CoroutineScope` is not lifecycle-aware  
and must not be used (PR #732 needs updating for this reason). Keep thin  
Android wrappers in `androidMain` only for Android-specific dependencies  
like `RemotePlaybackHandler` and `android.net.Uri`. Migrate Koin  
`viewModelModule` to commonMain.

### Phase 7 — Compose UI Migration

Replace `androidx.compose.*` with JetBrains CMP. Migrate app theme, colors,  
and typography to `commonMain`. Move all resources from Android `res/` to  
shared `composeResources/` — updating all `painterResource(R.drawable.x)`  
calls to `painterResource(Res.drawable.x)`. Replace `lottie-compose` with  
Compottie for KMP animations. Replace `compose-shimmer` with KMP shimmer  
(same API, different artifact). Replace both `androidx.navigation.compose`  
and `androidx.navigation3` with Voyager — converting string-based routes to  
type-safe `Screen` data classes. Wire platform entry points  
(`MainActivity.setContent { App() }` on Android,  
`ComposeUIViewController { App() }` on iOS).

### Phase 8 — Platform-Specific Boundaries

Implement `expect/actual` for the four major platform boundaries:  
**ScrobblerManager** — `NotificationListenerService` on Android, permanent  
no-op stub on iOS (iOS fundamentally cannot read other apps’ notifications);  
**AudioPlayer** — ExoPlayer on Android wrapped with `MediaBrowserServiceCompat`  
for lock screen + Bluetooth integration, AVPlayer on iOS;  
**scheduleListenSubmission** — WorkManager on Android (existing  
`ListenSubmissionWorker`), BGTaskScheduler on iOS;  
**PermissionChecker** — Accompanist permissions on Android (notification  
listener + battery optimization), native iOS permission APIs.

* * *

## Timeline

| Period | Milestone |
| --- | --- |
| May 1–24 | Community bonding — study cmp branch deeply, confirm all decisions with mentor |
| May 25–Jun 7 | Phase 1 + Phase 2 — final build setup + all domain models to commonMain |
| Jun 8–Jun 21 | Phase 3 — complete network layer migration (both parts) |
| Jun 22–Jun 28 | Phase 4 — Room KMP complete |
| Jun 29–Jul 5 | Phase 5 — all repositories + paging to commonMain |
| **Jul 6–10** | **Midterm Evaluation** — models, network, Room, repositories all in commonMain |
| Jul 11–Jul 17 | Tests + midterm bug fixes, move tests to commonTest |
| Jul 18–Jul 24 | Phase 6 — full ViewModel migration with proper KMP ViewModel |
| Jul 25–Jul 31 | Phase 8 — all platform boundaries via expect/actual |
| Aug 1–Aug 7 | Phase 7 Part 1 — CMP core, theme, resources, Compottie |
| Aug 8–Aug 14 | Phase 7 Part 2 — Voyager navigation, shimmer, platform entry points |
| Aug 15–Aug 24 | Full Android + iOS testing, polish, final submission |

* * *

## My Contributions

| PR | Branch | Description |
| --- | --- | --- |
| [#639](https://github.com/metabrainz/listenbrainz-android/pull/639) | main | Fixed search state mismatch between user and BrainzPlayer search |
| [#648](https://github.com/metabrainz/listenbrainz-android/pull/648) | main | Fixed broken overflow menu on BrainzPlayer Albums screen |
| [#727](https://github.com/metabrainz/listenbrainz-android/pull/727) | main | Implemented Delete Listen feature |
| [#732](https://github.com/metabrainz/listenbrainz-android/pull/732) | cmp | Migrating SocialViewModel to shared/commonMain for KMP |

All PRs: [github.com/metabrainz/listenbrainz-android/pulls/anuj990](https://github.com/metabrainz/listenbrainz-android/pulls?q=is%3Apr+author%3Aanuj990)

* * *

## Full Proposal

Due to the community post character limit, the complete proposal including  
all technical details, actual ListenBrainz code examples showing exact  
before/after migration for every phase, full dependency configurations,  
`expect/actual` implementations, and the complete week-by-week timeline  
is available here:

**[Full Proposal on GitHub](https://github.com/anuj990/gsoc-2026-proposal)**

* * *

Looking forward to feedback from the community and mentors!

---

<div class="post-metadata">

### Author: ![harshal829](https://community.metabrainz.org/letter_avatar_proxy/v4/letter/h/f9ae1b/32.png) [@harshal829](https://community.metabrainz.org/u/harshal829)
#### Post date: [March 19, 2026, 10:11am UTC](https://community.metabrainz.org/t/compose-multiplatform-migration/814456/2 "2026-03-19T10:11:27Z")

</div>

@anuj990

I have read your proposal. It is very informative, and you have addressed every aspect of the project well.

---

<div class="post-metadata">

### Author: ![Jasjeet](https://community.metabrainz.org/user_avatar/community.metabrainz.org/jasjeet/32/64960_2.png) [@Jasjeet](https://community.metabrainz.org/u/Jasjeet)
#### Post date: [March 24, 2026, 4:19pm UTC](https://community.metabrainz.org/t/compose-multiplatform-migration/814456/3 "2026-03-24T16:19:56Z")

</div>

Hi @anuj990, thanks for writing up a contribution plan / GSoC proposal.

Great work on writing a detailed proposal, but it is way too verbose for me to review. I know that all proposals for GSoC are AI-assisted but you need to be subtle with it. Compacting the proposal is the first suggestion I would like give you before proceeding further.

Also, a lot of the coding part is already included in the proposal itself. The code should be short and concise. Coding should be done once coding period starts itself and not in the proposal period itself.

---

<div class="post-metadata">

### Author: ![anuj990](https://community.metabrainz.org/letter_avatar_proxy/v4/letter/a/9fc29f/32.png) [@anuj990](https://community.metabrainz.org/u/anuj990)
#### Post date: [March 24, 2026, 9:17pm UTC](https://community.metabrainz.org/t/compose-multiplatform-migration/814456/4 "2026-03-24T21:17:02Z")

</div>

@Jasjeet Thanks for your feedback. I will ensure alignment with the guidelines  
I will refine and make the proposal more concise, and update it asap
