Modularization + Jetpack Compose in Commons Android App
Contributor Name: Owm Dubey
Contributor Email: owmdubey163@gmail.com
Contributor Github: Roniscend
Location : India (UTC +5:30)
Expected Project Size: 350 hours
Mentors:
1. About Me
I am a sophomore pursuing a B.E. in Computer Science Engineering in Bangalore, India. I have a strong interest in Android Development and Open Source Contribution. My focus is on building scalable and well-architected mobile applications using Kotlin/Java and the latest Android Development Technologies.
I have a strong reason to be a part of the Modularization and Jetpack Compose Initiative because the application's legacy MVP architecture is becoming increasingly difficult to scale and maintain. The application needs a modernization effort in its architecture from the legacy MVP architecture to the latest and greatest MVVM architecture and, more specifically, converting the application's XML-based view hierarchy to the latest Jetpack Compose UI.
The Modularization and Jetpack Compose Initiative is a great move in the direction of making the Wikimedia Commons application more scalable and efficient. I would love the opportunity to contribute to this project and work on making the application's architecture more scalable and efficient by identifying the architectural challenges in the application's legacy MVP architecture and migrating the application's XML-based UI to the latest Jetpack Compose UI.
2. Past Experiences
Over the past few months I have been actively contributing the Commons Application Project. At the time of writing this proposal, I have a total of 30 PR's with 14 merged and 16 currently open and a total of 25 Issues Created.
Beyond open-source, I've also worked as an Android developer Intern at LettrBlack, where I have architected an Android application using MVVM and Clean Architecture. I implemented the entire data layer including Repository patterns and Room persistence while ensuring a reactive UI layer with StateFlow.
After the submission of this proposal, I plan on continuing to contribute to the Commons
Android application. An updated list of all my pull requests, issues, and discussions can be
found in the following GitHub Gist, which I will keep on updating.
Link : https://gist.github.com/Roniscend/b080c4ef0632d9fa25f6a47fb6cccd11
2.1 Notable Projects
- SafarLink : A unified platform that compares ride and delivery services in real time and simplifies booking through intelligent deep linking and automated cross-app navigation
- Atkit : Atkit is Kotlin based Android camera app built with MVVM and Jetpack Compose featuring CameraX integration,smart search , adaptive theming,and efficient Session- based photo management for oral health imaging
- Survey : A modern Android survey application developed using Kotlin and Jetpack Compose,implementing MVVM architecture for efficient collection management and persistence of user responses through a scalable and intuitive UI.
- Moment.ly : A feature rich Android social media app using Firebase Authentication and Firestore with a dynamic scrolling feed and declarative UI in Jetpack Compose
3. Prior contributions
| Repository | Pull Request | Issue Solved | Description | Status |
|---|---|---|---|---|
| commons-app/apps-android-commons | #6663 | #6662 | Fix password toggle reset on rotation. | Merged |
| commons-app/apps-android-commons | #6681 | #6678 | Persist 2FA visibility across configuration changes | Merged |
| commons-app/apps-android-commons | #6665 | #6650 | Fix SVG decoding in upload flow | Open |
| commons-app/apps-android-commons | #6669 | #6666 | Resolve login password toggle desync issue | Open |
| commons-app/apps-android-commons | #6679 | #6674 | Enhance character rendering in Nearby list | Open |
| commons-app/apps-android-commons | #6685 | #6676 | Handle configuration changes in Place selector | Open |
| commons-app/apps-android-commons | #6682 | #6677 | Improve login network error user feedback | Open |
| commons-app/apps-android-commons | #6737 | #6699 | Fixed FAB layout and distortion issues | Merged |
| commons-app/apps-android-commons | #6753 | #6700 | Validated fragment behavior on configuration changes | Open |
| commons-app/apps-android-commons | #6753 | #6700 | Fixed BookmarkFragment UI on rotation | Merged |
| commons-app/apps-android-commons | #6796 | #6788 | Removed overlapping header in report screen | Merged |
| commons-app/apps-android-commons | #6794 | #6790 | Fixed OpenStreetMap link handling in Explore screen | Merged |
| commons-app/apps-android-commons | #6800 | #6784 | Fixed tab layout by making it scrollable to prevent text wrapping | Merged |
| commons-app/apps-android-commons | #6793 | #6782 | Made tabs scrollable to fix text wrapping issue | Open |
| commons-app/apps-android-commons | #6795 | #6789 | Restored bottom sheet expand behavior in landscape mode | Open |
| commons-app/apps-android-commons | #6799 | #6783 | Prevented dialog from reappearing after dismissal on rotation | Open |
| commons-app/apps-android-commons | #6801 | #6786 | Fixed Nearby screen layout issues in landscape mode | Open |
| commons-app/apps-android-commons | #6802 | #6787 | Fixed missing info icon in peer review screen | Open |
| commons-app/apps-android-commons | #6807 | #6629 | Migrated WelcomeActivity to Compose with responsive UI and dark theme | Open |
| commons-app/apps-android-commons | #6740 | #6701 | Verified keyboard visibility issue fixed in landscape | Open |
| organicmaps/organicmaps | #11983 | #11938 | Fix SplashActivity navigation race condition crash | Merged |
| organicmaps/organicmaps | #12008 | #10835 | Preserve native debug symbols for readable crashes | Merged |
| organicmaps/organicmaps | #12032 | Pass specific login error codes to Android layer | Merged | |
| organicmaps/organicmaps | #12079 | #11945 | Optimize layout updates during window inset changes | Merged |
| organicmaps/organicmaps | #12098 | #12091 | Ensure legacy Android 5 icon compatibility | Merged |
| organicmaps/organicmaps | #12000 | #11424 | Migrate navbar to AndroidX Edge-to-Edge API | Merged |
| organicmaps/organicmaps | #12006 | #11668 | Persist login error dialog across configuration changes | Approved |
| organicmaps/organicmaps | #12140 | #12132 | Fix OpenStreetMap search URL parsing | Open |
| organicmaps/organicmaps | #12077 | #12044 | Refactor opening hours layout for readability | Open |
| commons-app/apps-android-commons | #6803 | #6629 | Migrated About screen to Compose for better UI consistency | Open |
4. Commitments during GSoC
I have no prior commitments during the GSoC period and I'll be able to work 35-45 hours per week solely on completing this project.
Project Idea
5. Abstract
This project proposes a comprehensive architectural revamp of the Android application to resolve technical debt and mitigate the risks associated with its outdated, tightly coupled codebase. Currently, the legacy architecture bottlenecks feature development, complicates testing and creates a high barrier to entry for new contributors.
The core objective of this proposal is twofold: achieving complete modularization of the app and migrating at least 40% of the legacy XML UI to Jetpack Compose.
By dismantling the monolithic structure into highly cohesive, feature based modules following strict Clean Architecture principles, the project will drastically improve separation of concerns, gradle build times, and isolated testability. Concurrently, migrating key user flows to Jetpack Compose integrated with the industry standard Navigation 3 API will establish a Unidirectional Data Flow(UDF) using a modern MVVM pattern and reactive state observation.
The modernization effort will directly benefit end-users by delivering a highly stable, crash resilient app with a refreshed interface. Furthermore, it ensures long term scalability by providing volunteer contributors with a clean, standard compliant codebase that significantly accelerates onboarding and future development.
6. Implementation Details
6.1 Dependency Injection
| Current State | Target State |
|---|---|
| legacy Dagger 2 with AndroidInjectionModule and dagger-android | Hilt modern DI |
Problem Areas:
- Uses dagger android deprecated with AndroidInjector, DispatchingAndroidInjector, HasSupportFragmentInjector
- Complex Dagger setup with ApplicationlessInjection, CommonsDaggerAppCompatActivity, CommonDaggerSupportFragment
- Manual component builders ActivityBuilderModule, FragmentBuilderModule requiring every Activity/Fragment to be registered.
Files to update(eg.): CommonsApplicationComponent.kt
@Component( modules = [ CommonsApplicationModule::class, NetworkingModule::class, AndroidInjectionModule::class, AndroidSupportInjectionModule::class, ActivityBuilderModule::class, FragmentBuilderModule::class, // ... ] )
Migration Path: The migration from legacy Dagger 2 to Hilt focuses on replacing manual component management with Hilt's standard annotations. By annotating the Application class with @HiltAndroidApp and UI components with @AndroidEntryPoint, you eliminate the need for ActivityBuilderModule, FragmentBuilderModule, and custom base classes like CommonsDaggerAppCompatActivity. This transition simplifies the dependency graph, removes significant boilerplate code, and enables the use of @HiltViewModel for modern architecture.
Dagger The Tangled Web
Hilt The Magic Annotation
| Feature | Legacy State | Target State |
|---|---|---|
| App Setup | Manual @Component & AndroidInjectionModule | @HiltAndroidApp |
| UI Injection | @ContributesAndroidInjector in modules | @AndroidEntryPoint |
| Base Classes | DaggerAppCompatActivity/ Fragment | Standard AppCompatActivity/Fragment |
| ViewModels | Manual Factory/Provider injection | @HiltViewModel |
6.2 Architecture Pattern
| Current State | Target State |
|---|---|
| MVP(Model View Presenter) | MVVM(Model View ViewModel + Stateflow) |
The migration to MVVM focuses on the complete elimination of the legacy MVP infrastructure, specifically removing the BasePresenter.kt interface and all associated Contract.kt files such as ContributionsContract.kt, ContributionsListContract.kt, and PagingContract.kt.
By removing these contracts and over 30+ Presenter files, we are transitioning from an imperative, interface-driven architecture to a modern reactive approach. This allows us to move logic into lifecycle-aware ViewModels that utilize StateFlow to emit updates. This shift effectively eliminates the high boilerplate of the contract based pattern and ensures the application handles configuration changes natively via viewModel.
When a configuration change triggers the recreation of the UI, The new Activity requests ViewModel via ViewModelProvider. Because Android framework preserves the ViewModelStore across these lifecycle events, ViewModelProvider retrieves the exact same ViewModel instance rather than instantiating a new one. ViewModelScope remains active and StateFlow retains its latest state. The newly initialized UI reconnects to the existing flow, ensuring a seamless transition without data loss.
| Feature | MVP | MVVM |
|---|---|---|
| State handling | Manual updates via View Interfaces | Reactive streams using State Flow |
| Lifecycle | Presenters must be manually cleared | Viewmodels are lifecycle aware |
| Logic | Tight coupling(Presenter & View) | Decoupled (Viewmodel emits View observes) |
| Boilerplate | More(Contract Interfaces for every screen) | Less |
Example:
Legacy Pattern PagingContract.kt
interface PagingContract { interface View<T> { fun showSnackbar() fun observePagingResults (searchResults: LiveData<...>) } interface Presenter<T>: BasePresenter<View<T>> { // } }
Updated Code:
- ViewModel
@HiltViewModel class PagingViewModel @Inject constructor( private val repository: Paging Repository ): ViewModel() { private val _uiState = MutableStateFlow<PagingState> (PagingState.Idle) val uiState: StateFlow<PagingState> = _uiState.asStateFlow() val pagingResults: Flow<PagingData<T>> = repository.getResults() .cachedIn(viewModelScope) fun handleAction() { // Implementation } }
- UI State
sealed class PagingState { object Idle: PagingState() object Loading: PagingState() data class Error (val message: String): PagingState() }
- View
@AndroidEntryPoint class PagingFragment: Fragment (R.layout.fragment_paging) { private val viewModel: PagingViewModel by viewModels() override fun onViewCreated (view: View, savedInstanceState: Bundle?) { super.onViewCreated (view, savedInstanceState) viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle (Lifecycle.State.STARTED) { viewModel.pagingResults.collectLatest { pagedData -> adapter.submitData (pagedData) } } } viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle (Lifecycle.State.STARTED) { viewModel.uiState.collect { state -> when (state) { is PagingState. Error -> { Snackbar.make(view, state.message, Snackbar.LENGTH_LONG).show() } PagingState. Loading -> { // Handle loading state } else -> { } } } } } } }
6.3 Reactive Programming
| Current Status | Target Status |
|---|---|
| RxJava2(Observable, Single, Competable, CompositeDisposable) | Kotlin Coroutines + Flow |
A critical priority of this modernization is overhauling the asynchronous execution model across 40+ legacy files, transitioning from RxJava2 (Observable, Single, Completable) to Kotlin Coroutines and Flow. The codebase currently relies on manual thread scheduling (Schedulers.io(), AndroidSchedulers.mainThread()) and error-prone lifecycle management via CompositeDisposable.
By adopting structured concurrency, we will replace complex RxJava chains and outdated callback interfaces (e.g., LoginCallback in LoginClient.kt) with sequential, readable suspend functions. This shift eliminates the need for manual disposal management; instead, viewModelScope and lifecycleScope will automatically handle coroutine cancellation when the UI is destroyed, drastically reducing boilerplate and the risk of memory leaks. Finally, background processing will be unified by migrating any remaining RxWorker implementations to native CoroutineWorker classes.
| Migration Area | Current | Target | Affected files |
|---|---|---|---|
| Lifecycle Management | Composite Disposable | ViewModelScope | 40+ Presenter files(eg. ContributionsListPresenter.kt) |
| Thread Switching | Schedulers.io(), AndroidSchedulers.mainThread() | Dispatchers.IO, Dispatchers.Main ,withContext() | Presenters, ViewModels, Repository layers |
| Network return Types | Observable<T>,Single<T>, Completable | Suspend functions or Flow<T>(streams) | All Retrofit API interface |
| Data Operators | subscribeOn(), observeOn(), flatMap() | Standard Kotlin Flow operators(map, flatMapLatest) | Repository and Data layers |
Example: ContributionsListPresenter.kt
RxJava2
@Inject @Named (CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler private val composite Disposable = Composite Disposable() fun loadContributions() { composite Disposable.add( repository.getContributions() .subscribeOn(ioThreadScheduler) .observeOn (Android Schedulers.mainThread()) .subscribe({ result -> // Handle result }, { error -> // Handle error }) ) }
Coroutines
@Inject @IoDispatcher private val ioDispatcher: CoroutineDispatcher fun loadContributions() { viewModelScope.launch(ioDispatcher) { try { val result = repository.getContributions() } catch (e: Exception) { // Handle error } } }
Currently, the codebase hardcodes Dispatchers.IO across multiple files such as UploadWorker.kt, DepictsDao.kt, NearbyParentFragmentPresenter.kt, OkHttpJsonApiClient.kt, ZoomableActivity.kt, CustomSelectorActivity.kt, and ImageAdapter.kt. This leads to three key issues:
a)Untestable coroutines: Hardcoding Dispatchers.IO prevents tests from controlling execution. Coroutines run on real IO threads, making tests nondeterministic, flaky, and slow. Injecting @IoDispatcher allows replacing it with TestCoroutineDispatcher or StandardTestDispatcher for deterministic testing.
b)Violation of Dependency Inversion Principle: Classes directly depend on a concrete dispatcher instead of an abstraction. With @IoDispatcher, the dependency is inverted and provided by Hilt, keeping classes decoupled from implementation details.
c)Lack of architectural consistency: In a modular setup like :core:domain and :core:data, dispatchers should be injected via constructors. This centralizes concurrency control and avoids widespread changes if strategies evolve.
6.4 Navigation
To modernize the application's routing we will migrate from manual fragment transactions to the industry standard Android Navigation 3 API. Currently the MainActivity relies heavily on boilerplate code, using FragmentTransaction.replace() and custom enums(NavTab, NavTabLoggedOut) to manage bottom tab states. By adopting this modern approach, we will define all application destinations and routing logic within a centralized Navigation Graph.
A NavController will then be utilized within a NavHostFragment to trigger transitions and manage the back stack automatically. This eliminates the need for manual intent - based Activity routing, ensures type safe argument passing, and significantly reduces the complexity of handling fragment lifecycles.
| Migration | Current | Target |
|---|---|---|
| Routing Mechanism | Manual (FragmentTransactions.replace()) | Centralized Navigation Graph & NavController |
| Tab Management | Custom NavTab & NavTabLoggedOut enums | BottomNavigationView.setupWithNavController() |
| Data Passing | Manual Bundle creation and Intent extras | Type safe Kotlin DSL routing |
6.5 UI Layer
The UI layer is currently in a transitional state , relying on over 100+ XML layout files and ViewBinding , with only partial Jetpack Compose adoption in specific areas like SingleWebViewActivity and MediaDetailFragment. The primary objective for this phase is to achieve a minimum of 40% Jetpack Compose coverage across the application's UI.
We will systematically replace high priority legacy XML layouts and ViewBinding implementations with modern @Composable functions. Since the application will utilize the standard Android Navigation 3 API , the migrated Compose screens will be integrated as Fragment destinations using ComposeView. This approach guarantees smooth interoperability between the newly built Compose UI.
To guarantee smooth performance for the UI after migrating to Compose, Baseline Profiles will be used to pre-compile the new UI components and prevent frame drops on the first launch.
| Migration Area | Current State | Target State |
|---|---|---|
| UI Framework | 100+ XML layouts + viewbinding | Minimum 40% Jetpack Compose coverage |
| Custom Views | Legacy views like NavtabLayout | Compose Material 3 Adaptive library |
| Screen Hosting | Standard XML inflated Fragments | @Composable functions hosted via ComposeView |
Priority 1: Screens That Already Use Compose
The codebase already has partial Compose adoption. These screens should be completed first since the infrastructure is already in place:
| Screen | Current State | Rationale |
|---|---|---|
| WelcomeScreen.kt | Already fully Compose (@Composable pages: WelcomeDoUploadPage, WelcomeDontUploadPage, etc.) | Already done. Only needs cleanup and theme token alignment with :core:ui. |
| BookmarkCategoriesFragment.kt | Already uses ComposeView | Already hybrid. Extend to cover the remaining bookmark tabs. |
| MediaDetailFragment.kt | Partially Compose (fileUsagesComposeView) | Already hosts a ComposeView. Incrementally migrate remaining XML sections of this fragment. |
| SingleWebViewActivity.kt | Already @Composable | Almost done |
Priority 2: High-Value, Low-Complexity Migrations
These screens are state-heavy but structurally simple, making them ideal Compose candidates:
| Screen | Files | Why Migrate? |
|---|---|---|
| Auth Flow (LoginActivity.kt, SignupActivity.kt) | 6 files | Highly state-dependent (Loading -> Error -> 2FA -> Success). Compose's declarative when(state) pattern is a massive upgrade over the current imperative XML toggle logic. Low user traffic = low regression risk. |
| Profile / Achievements (ProfileActivity.kt) | 1 file + 2 subdirs (achievements/, leaderboard/) | Simple data-display screens. LazyColumn for leaderboard lists is a direct, clean replacement for RecyclerView. |
| Notifications (NotificationActivity.kt) | 8 files | A straightforward list screen. Perfect candidate for LazyColumn + StateFlow. |
| Settings (SettingsActivity.kt, SettingsFragment.kt) | 3 files | SettingsFragment is large but is mostly a PreferenceFragmentCompat. Can be incrementally wrapped in ComposeView for custom preference items. |
| About (AboutActivity.kt) | 1 file | Static content screen. Trivial to port. |
Priority 3: High-Impact, Medium-Complexity Migrations
These are the core user-facing screens where Compose delivers the biggest performance and UX gains:
| Screen | Files | Why Migrate? |
|---|---|---|
| Contributions List (ContributionsListFragment.kt, ContributionsFragment.kt) | 22 files total | The most visible screen in the app. Currently uses legacy RecyclerView + Fresco SimpleDraweeView for image loading. Migrating to Compose LazyColumn + Paging 3 collectAsLazyPagingItems() + Coil AsyncImage will dramatically improve scroll performance and drop legacy layout inflation entirely. |
| Explore / Search (ExploreFragment.kt, SearchActivity.kt) | 6 files + 7 subdirs | Paging-heavy search results. Same LazyColumn + Paging 3 benefits as Contributions. |
Which stable screens should be kept as XML (Hybrid Approach)?
These screens are massive, deeply intertwined, or Java-heavy. Rewriting them in Compose during GSoC introduces unacceptable regression risk. They will be modularized to MVVM but will strictly retain their XML ViewBinding:
| Screen | Files | Why Keep as XML? |
|---|---|---|
| Upload Wizard (UploadActivity.kt) | 43 files + 6 subdirs | This is the single most critical feature in the app it's how users contribute media to Wikimedia Commons. With deeply coupled MVP presenters (UploadPresenter.kt, UploadContract.kt), FragmentStatePagerAdapter, multi-step wizard logic, and complex Camera/Location/EXIF integrations, rewriting this to Compose is extremely high risk. It will be extracted to :feature:upload and migrated to MVVM, but the XML stays. |
| Nearby Map (NearbyController.java, NearbyParentFragment) | 19 files + 4 subdirs | Still primarily written in Java (not even Kotlin). Tightly coupled to the native Osmdroid C++ map rendering engine. Compose has no mature Osmdroid wrapper. Rewriting this means rewriting the entire map interaction layer. Extract to :feature:nearby, convert Java -> Kotlin, migrate to MVVM, but keep XML. |
| Review (ReviewActivity.kt, ReviewImageFragment.kt) | 9 files | A mature, stable peer-review flow with custom ViewPager swiping logic. Low user complaints, no architectural benefit from Compose migration. Keep as XML. |
| Custom Selector (CustomSelectorActivity.kt) | - | Already uses @Composable partially Hybrid state. Keep the existing XML shell and expand Compose incrementally. |
| Location Picker (LocationPickerActivity.kt) | - | Tightly bound to Mapbox/Osmdroid native views. Cannot be ported to Compose without a compatible map composable. Keep as XML. |
6.6 State Management
| Current State | Target State |
|---|---|
| LiveData+RxJava+Callbacks | StateFlow + Shared Flow Kotlin Flow |
The application's current state management is fragmented , relying on a mixture of LiveData, RxJava observables and manual callback interfaces to push updates to the UI via the standard observer pattern. To unify the architecture and fully support the planned Jetpack Compose migration, we will transition entirely to Kotlin's StateFlow and SharedFlow. This modernization replaces lifecycle bound LiveData with reactive Flowstreams which are inherently more flexible and integrate seamlessly with Compose via collectAsState().
Furthermore , tightly coupled callback interfaces will be eliminated in favor of SharedFlow to handle one time UI events such as Snackbars,toast messages , or navigation triggers safely and efficiently without holding duplicate state.
| Migration | Current | Target |
|---|---|---|
| UI State Holding | LiveData & Mutable Live Data | StateFlow & MutableState Flow |
| One Time Events | Manual Callback interface | Shared Flow |
| Compose integration | observeAsState() or manual wrappers | Native collectAsState() |
6.7 Background Work & Threading
The application's background work and threading are currently managed through a mix of legacy systems, including RxJava Schedulers, a custom ThreadPoolService.kt for thread pool management, and remnants of deprecated AsyncTask implementations. To standardize asynchronous operations and ensure resource safety, we will migrate entirely to Kotlin Coroutines.
By adopting Coroutines, we will leverage structured concurrency to tie background work directly to the lifecycle of the UI components via viewModelScope and lifecycleScope. This eliminates the need for manual thread management and custom services, guaranteeing that ongoing tasks are automatically canceled when the associated screen is destroyed thereby preventing memory leaks and application crashes.
| Migration | Current State | Target State |
|---|---|---|
| Concurrency Framework | RxJava + Custom Thread Pools + AsyncTask | Kotlin Coroutines |
| Thread Switching | Schedulers.io(),AndroidSchedulers.mainThread() | Dispatchers.IO, Dispatchers.Main |
| Lifecycle Safety | Manual cancellation | Structured Concurrency (viewmodelScope) |
| Custom Services | ThreadPoolService.kt | Native Coroutine Dispatchers |
6.8 Image Loading
The application currently relies on Facebook's Fresco library for image loading, which requires heavy, explicit initialization in CommonsApplication.kt and the use of custom SimpleaDraweeView components within XML layouts. To align with our Kotlin first architecture and the ongoing Jetpack Compose migration, we will replace Fresco with Coil (Coroutine Image Loader).
The migration process involves completely removing Com.facebook.drawee.backends.pipeline. Fresco and its Image PipelineConfig from the application class, taking advantage of Coil's lightweight, automatic initialization. In the UI layer, legacy SimpleDraweeView XML tags will be replaced with standard ImageView components utilizing Coil's load() extension function, while newly built Compose screens will natively adopt Coil's AsyncImage composable. Finally all Fresco artifacts will be stripped from the build.gradle dependencies, which will significantly reduce the final APK size as Coil is a very lightweight library.
6.9 Pagination
The application currently utilizes the legacy Paging 2 library, relying on DataSource, PagedList, and LivePagedListBuilder to handle data pagination in classes such as ContributionsListPresenter.kt. To modernize the data layer and ensure seamless compatibility with Jetpack Compose, we will migrate to the Paging 3 library.
This transition involves replacing the legacy DataSource components with PagingSource and swapping the LivePagedListBuilder for the modern Pager API, which emits paginated data as a continuous Kotlin Flow<PagingData>. This upgrade not only provides robust error handling and loading state management natively but also enables direct integration with Compose lists such as LazyColumn and LazyVerticalGrid using the paging compose artifact.
To modernize the data layer and fully support Jetpack Compose, we will migrate from Paging 2 to Paging 3 targeting legacy files like Contributions List Presenter.kt. The outdated DataSource and LivePagedListBuilder components will be replaced with the modern PagingSource and Pager APIs. Instead of exposing a LiveData stream of PagedList objects, the new architecture will emit a continuous Kotlin Flow<PagingData>. This Coroutine native approach integrates seamlessly with the updated architecture and allows Compose UI components such as LazyColumn and LazyVerticalGrid to efficiently consume and display paginated data using the paging compose library.
6.10 Network Layer
To completely modernize the network layer targeting legacy files like LoginInterface.kt, we will transition from Retrofit with RxJava callbacks to Coroutine based suspend functions, eliminating callback hell and ensuring type-safe and efficient data parsing.
To guarantee robust network execution, inconsistent try-catch blocks will be standardized globally using a custom Retrofit CallAdapter, and manual retry logic will be centralized into an OkHttp RetryInterceptor, alongside new certificate pinning for enhanced security.
Finally, we will establish a true offline first architecture by abstracting raw Retrofit calls behind a Repository pattern emitting Kotlin Flow, utilize a Flow-based NetworkBoundResource rather than relying solely on the basic OkHttp cache.
Legacy API Pattern:
LoginInterface.kt
interface LoginInterface { @GET("?format=json&formatversion=2&errorformat=plaintext") @Headers("Cache-Control: no-cache") fun getLogin Token(): Call<MwQueryResponse?> }
Legacy RxJava Network Pattern:
SessionManager.kt
fun logout(): Completable = Completable.fromObservable( Observable.empty<Any>() .doOnComplete { removeAccount() currentAccount = null } )
Modern Network Architecture:
interface LoginApi { @GET("login") suspend fun login (@Body request: LoginRequest): Result<LoginResponse> } class LoginRepository @Inject constructor( private val api: LoginApi, private val db: UserDao ) { // The CallAdapter makes api.login return a Result automatically fun login(request: LoginRequest): Flow<Result<User>> = flow { emit(Result.Loading) val result = api.login(request) if (result is Result. Success) { db.saveUser(result.data.toEntity()) } emit(result) } }
6.11 Data Storage
To completely overhaul the storage layer, we will replace legacy SharedPreferences (BasicKvStore.kt) and the Gson based JsonKvStore with Jetpack DataStore (Preferences and Proto) for type safe key value management.
For structured data, we will finalize a migration to Room, fully deprecating both the legacy SQLiteOpenHelper (DBOpenHelper.java) and the custom ContentProviders used for bookmarks in favor of a standard Room and Repository pattern.
| Migration | Current | Target |
|---|---|---|
| Relational Database | Room | Room(Retained, updated for Coroutines/Flow) |
| Key-Value Preferences | Custom JsonKvStore | Jetpack DataStore |
| Execution Context | Synchronous / Custom Threads | Asynchronous (Coroutines & Flow) |
6.12 Testing
To ensure long term stability, the legacy testing infrastructure will undergo a comprehensive modernization. We will migrate the unit test stack from JUnit 4 and PowerMock to a modern setup utilizing JUnit 5, MockK, and either Turbine or kotlinx-coroutines-test, as Turbine is third party library and older but still google recommends it for precise Coroutine Flow validation.
For UI and integration testing, the custom Dagger mock setup will be replaced by HiltTestApplication, transitioning to a hybrid of Compose Testing and Espresso. Finally, to prevent future UI and edge case regressions, we will introduce Compose Screenshot Testing tool for automated screenshot testing alongside Kotlin Test for property based coverage.
Legacy Test Pattern:
TestCommonsApplication.kt
class TestCommonsApplication: Application() { private var mockApplicationComponent: CommonsApplication Component? = null // Complex manual mock setup } class MockCommonsApplicationModule (appContext: Context): CommonsApplicationModule (appContext) { // mock } val defaultSharedPreferences: JsonKvStore = mock() // Mockito
Legacy UI Test Pattern:
MainActivityTest.kt
@RunWith(AndroidJUnit4::class) // OLD API class MainActivityTest { @get: Rule var activityRule: ActivityTestRule<LoginActivity> = ActivityTestRule (LoginActivity::class.java) // Deprecated // Manual login helpers private lateinit var defaultKvStore: JsonKvStore }
Compose UI Test
@HiltAndroidTest class MainScreenTest { @get: Rule val hiltRule = HiltAndroidRule(this) @get: Rule val composeTestRule = createAndroidComposeRule<MainActivity>() @Test fun bottomNavigation_switches Tabs() { composeTestRule .onNodeWithText("Contributions") .performClick() composeTestRule .onNodeWithTag("contributions_list") .assertIsDisplayed() } }
Modern Test Architecture
@ExtendWith(MockKExtension::class) class LoginViewModelTest { @MockK private lateinit var repository: LoginRepository private lateinit var viewModel: LoginViewModel private val testDispatcher = UnconfinedTestDispatcher() @BeforeEach fun setup() { Dispatchers.setMain (testDispatcher) viewModel = LoginViewModel (repository) } @Test fun `login emits loading then success` () = runTest { coEvery { repository.login(any()) } returns flow { emit(Result.Success (User("test"))) } viewModel.login("user", "pass").test { assertEquals (LoginUiState. Loading, awaitItem()) assertEquals (LoginUiState. Success, awaitItem()) awaitComplete() } } }
6.13 Uncoupling the Monolith
The current architecture of commons-app heavily centralizes logic into the :app module, which drastically impairs scalability and increases build times. For example, UploadActivity.kt currently operates as a massive class that directly @Injects sweeping, cross-domain dependencies like UserClient (Network), JsonKvStore (Database), and LocationServiceManager (Hardware) straight into the Activity layer. Furthermore, ActivityBuilderModule.kt binds over 24 different Activities into one massive global Dagger graph. This architecture exponentially inflates incremental Gradle build times, prevents compilation avoidance, and creates tight coupling across unrelated feature boundaries.
My Implementation Strategy: To permanently resolve this technical debt and modernize the compilation pipeline, I will architect a strict Domain driven multi module directed acyclic graph (DAG) based on the following pillars:
Strict Encapsulation (Vertical Layering): Feature modules (e.g., :feature:upload, :feature:nearby) will enforce Kotlin's internal visibility modifiers for their localized MVVM infrastructure. *UploadActivity will be stripped of its direct Network and Hardware injections. It will instead rely purely on an UploadViewModel** that communicates exclusively through abstract domain interfaces (e.g., UserRepository), entirely blinding the UI to how data is retrieved.
Module-Scoped DI & Horizontal Compilation Avoidance: By replacing the bloated multiple activities ActivityBuilderModule with strictly scoped @HiltAndroidApp component hierarchies, and extracting :core:network (MediaWiki APIs) and :core:database (Room DAOs) into isolated horizontal layer modules, we clear the compilation bottleneck.
Decoupling Transitive Dependencies : Currently, the :app monolith globally bundles massive libraries like Fresco, Glide, RxJava, and Osmdroid. This forces lightweight text-only features to compile heavy C++ map and image rendering libraries. In the new DAG architecture, dependencies will be strictly hidden. For instance, libs.osmdroid.android will exist only inside :feature:nearby using Gradle's implementation keyword (rather than api), physically preventing it from leaking into the broader application. This eliminates class-path collisions and drastically reduces the compiled APK footprint.
Before (The Monolithic Dagger Tangle)
After (True Clean Architecture via Dependency Inversion)
6.14 Proposed Module Structure
To execute this architecture, the monolithic :app folder will be dismantled into the following cohesive Gradle modules:
1. Core Modules (Horizontal Infrastructure): These modules form the backbone of the application and do not depend on any specific UI feature.
- :core:domain (Pure Kotlin. Holds Entities, Result wrappers, and Repository Interfaces).
- :core:network (Retrofit, OkHttp, MediaWiki API endpoints).
- :core:database (Room DAOs, Jetpack DataStore preferences).
- :core:ui (Jetpack Compose Material 3 tokens, theme, and reusable common components).
- :core:testing (Hilt test rules, MockK extensions for shared module testing).
2. Feature Modules (Vertical Flows): These modules encapsulate distinct user flows. By using Gradle's implementation keyword, their massive 3rd-party dependencies (like Fresco or Osmdroid) remain physically isolated and cannot leak into the broader application.
- :feature:auth (Login, 2FA, Signup UI flows).
- :feature:contributions (The MVVM paged list of user contributions).
- :feature:explore (Search and categories).
- :feature:nearby (The osmdroid map rendering UI).
- :feature:upload (The Upload Wizard flow).
How will multi-modules NOT increase maintenance overhead?
1. Gradle Convention Plugins (build-logic)
The #1 cause of multi-module nightmare is duplicated build.gradle.kts boilerplate. I will create a build-logic/ included build with Kotlin DSL convention plugins:
build-logic/
├── convention/
│ ├── src/main/kotlin/
│ │ ├── commons.android.application.gradle.kts
│ │ ├── commons.android.feature.gradle.kts -> For feature modules
│ │ ├── commons.android.library.gradle.kts -> For core modules
│ │ └── commons.android.compose.gradle.kts -> Compose compiler config
Result: Creating a new module requires exactly one line in its build.gradle.kts:
plugins { id("commons.android.feature") }
All compileSdk, minSdk, Hilt, Compose compiler, and test dependency configurations are inherited automatically. Zero duplication, zero version conflicts.
2. Version Catalog (libs.versions.toml)
The project already uses a Version Catalog. All library versions will be centralized here. Upgrading a library (e.g., Retrofit) means changing one line in libs.versions.toml, and it propagates to every module automatically.
3. :core:domain as the Single Source of Truth
All inter-module communication goes through pure Kotlin interfaces in :core:domain. A feature module cannot directly depend on another feature module. This means:
- Changing :feature:upload internals cannot break :feature:contributions.
- A small bug fix in one feature module only recompiles that module, not the entire app.
4. Strict Dependency Rules in settings.gradle.kts
I will enforce dependency direction using Gradle's api vs implementation:
- Feature modules use implementation for :core:network, :core:database -> changes in data layer do NOT trigger feature recompilation.
- Only :core:domain is exposed via api -> the public contract never leaks implementation details.
5. CI Enforcement
A simple Gradle task (or a lint rule) can be added to CI that fails the build if any :feature:* module directly depends on another :feature:* module, preventing developers from accidentally introducing circular dependencies.
What reusable components should :core:ui include as subtasks?
A. Design System & Theme Foundation
| Component | Status / Current Implementation | Rationale |
|---|---|---|
| CommonsTheme | styles.xml, colors.xml | Unified Material 3 theme; replaces BaseActivity theme logic. |
| CommonsColorScheme | Scattered color resources | Centralized light/dark palette; removes SystemThemeUtils dependency. |
| CommonsTypography | Inconsistent hardcoded sizes | Standardized type scale using Google Fonts (Inter/Outfit). |
| EdgeToEdgeShell | EdgeToEdgeUtils.kt (273 lines) | Migrates complex window inset and IME (keyboard) handling to Compose WindowInsets. |
B. Navigation & Layout Shells
| Component | Migrates From | Notes |
|---|---|---|
| CommonsTopAppBar | toolbar_binding.xml | Supports dynamic menus found in 15+ Activities. |
| CommonsBottomNavBar | NavTabLayout.kt | Replaces custom NavTab enum based fragment switching. |
| CommonsScaffold | Repeated XML patterns | Standardizes layout for primary navigation destinations. |
| LayoutHelpers | LayoutUtils.kt | Migrates aspect ratio and dimen helpers to Compose Modifiers. |
C. Media & Content Primitives
| Component | Migrates From | Notes |
|---|---|---|
| MediaThumbnail | ContributionViewHolder, GridViewAdapter | Replaces Fresco SimpleDraweeView with Coil/Glide. |
| ZoomableImage | ZoomableActivity.kt | Migrates custom touch/zoom logic to Compose transformable. |
| HtmlText | HtmlTextView.kt | Native Compose implementation using AnnotatedString. |
| NearbySwipeCard | NearbyNotificationCardView.java | Migrates complex SwipeableCardView gesture + compass rotation logic. |
D. List & State Management
| Component | Migrates From | Notes |
|---|---|---|
| PaginatedList | FooterAdapter.kt, BaseViewHolder | Consolidates LoadMore/Retry/Loading states for list UIs. |
| EmptyStateView | Scattered View.GONE logic | Standardized "No Data" UI found in 20+ fragments. |
| CommonProgressBar | progressBar in 25+ files | Unified loading indicator for long running operations. |
E. Input & Interaction
| Component | Migrates From | Notes |
|---|---|---|
| PasteSensitiveField | PasteSensitiveTextInputEditText.kt | Retains plain text paste logic required for security/formatting. |
| CommonsSearchBar | SearchActivity / ExploreMapFragment | Standardized search behavior with animation. |
| CategoryChipGroup | CategoryChip.java | Efficient chip based selection for tags and depicts. |
F. Dialogs & Utilities
| Component | Migrates From | Notes |
|---|---|---|
| CommonsAlertDialog | DialogUtil.kt (7 overloads) | Migrates showSafely and custom view dialog wrappers. |
| MoreBottomSheet | MoreBottomSheetFragment.kt | Replaces BottomSheetDialogFragment with native ModalBottomSheet. |
| FeedbackSheet | FeedbackDialog.kt | Refactors user reporting flow into a modern sheet. |
| UIEffectHandlers | ViewUtil.kt | Migrates Snackbar/Toast/Keyboard helpers to Compose Scaffold / LocalSoftwareKeyboardController. |
Result Wrapper is a sealed interface that safely and exhaustively encapsulates the result of a domain operation without relying on exception based flow control.
sealed interface Result<out T> {
data class Success<T>(val data: T) : Result<T>
data class Error(val exception: Throwable, val message: String? = null) : Result<Nothing>
data object Loading : Result<Nothing>
}This standardizes error handling by ensuring:
- Repositories return Result<T> instead of raw values
- ViewModels handle states exhaustively using when
- UI receives predictable states for rendering (loading, success, error)
Yes, DDD Use Cases can also be introduced in :core:domain.
Benefits of Use Cases in :core:domain:
- Single Responsibility: each Use Case does one thing
- Reusable: multiple ViewModels across feature modules can share the same Use Case
- Testable: pure Kotlin, no Android dependencies
- Decoupled: ViewModels don't need to know about repositories directly
- Domain logic centralization: business rules live in the domain layer, not scattered across ViewModels and Activities
7. Timeline
GSoC Timeline
7.1 Week-Wise Distribution
Community Bonding Period
Phase1: Foundation & Infrastructure
| Timeframe | Description | Est. Hours |
|---|---|---|
| Week 1 | Gradle Hilt Configuration & Core DI Migration | 45 Hrs |
| Subtask 1.1 | Initialize @HiltAndroidApp in CommonsHiltApplication and completely strip legacy Dagger AppComponent initialization and HasSupportFragmentInjector implementations. | 10 Hrs |
| Subtask 1.2 | Refactor ActivityBuilderModule & FragmentBuilderModule. Replace manual AndroidInjection.inject(this) calls with @AndroidEntryPoint and validate legacy ViewModel injections. | 15 Hrs |
| Subtask 1.3 | Resolve DI-related test regressions. Implement @HiltAndroidTest via a newly created TestCommonsApplication and configure a CustomTestRunner for robust Hilt UI testing. | 20 Hrs |
| Week 2 | DataStore & Async Foundations | 45 Hrs |
| Subtask 2.1 | Configure Hilt DataStoreModule and Proto schemas to globally provide DataStore<Preferences>. | 15 Hrs |
| Subtask 2.2 | Migrate standard key-value preferences from legacy BasicKvStore to Jetpack DataStore. | 15 Hrs |
| Subtask 2.3 | Deprecate JsonKvStore and refactor classes like SessionManager to emit Flow<T>, simultaneously establishing and injecting centralized Coroutine infrastructure. | 15 Hrs |
| Week 3 | Core Modularization & RxJava to Coroutines | 45 Hrs |
| Subtask 3.1 | Set up a build-logic module utilizing Gradle Convention Plugins (Version Catalogs) to centralize dependency management across all future modules. | 15 Hrs |
| Subtask 3.2 | Establish foundational core modules (:core:network, :core:database, :core:utils, :core:ui) and migrate the modernized components into these strict boundaries. | 15 Hrs |
| Subtask 3.3 | Implement CommonsCoroutineScope for centralized, lifecycle-aware coroutine management. Migrate ContributionsRepository from RxJava to Kotlin Flow with zero UI layer disruption. | 15 Hrs |
Phase 2: Architecture & Navigation
| Timeframe | Description | Est. Hours |
|---|---|---|
| Week 4 | Hybrid UI Migration: MVP -> MVVM | 45 Hrs |
| Subtask 4.1 | Refactor ContributionsFragment to MVVM. Embed @Composable ContributionsScreen via ComposeView(DisposeOnViewTreeLifecycleDestroyed) and observe UI states using collectAsStateWithLifecycle(). | 15 Hrs |
| Subtask 4.2 | Migrate ExploreFragment to MVVM and implement Jetpack Compose interoperability for the UI layer. | 15 Hrs |
| Subtask 4.3 | Execute the complex NearbyParentFragment MVVM migration. Integrate all newly refactored hybrid Fragments into MainActivity. | 15 Hrs |
| Week 5 | Navigation 3 API, Paging 3 Migration & Core UI | 45 Hrs |
| Subtask 5.1 | Migrate ContributionsListPresenter to ContributionsViewModel. Define nav_graph.xml to centralize all UI destinations (ContributionsFragment, NearbyParentFragment, UploadActivity) using the industry standard Navigation 3 API. | 15 Hrs |
| Subtask 5.2 | Migrate legacy Paging 2 DataSource to Paging 3 PagingSource returning isolated Flow<PagingData>. Configure NavHostFragment and initialize the NavController within MainActivity to manage the application’s hybrid backstack. | 20 Hrs |
| Subtask 5.3 | Create the :core:ui module establishing foundational Material 3 Compose tokens (CommonsTypography, CommonsColors). Replace legacy NavTab routing by binding BottomNavigationView directly to the NavController (setupWithNavController). Concurrently implement explicit deep link routing for external media and notifications. | 10 Hrs |
| Week 6 | Network Modernization & Core Feature Extraction | 45 Hrs |
| Subtask 6.1 | Initialize Kotlin Serialization and establish a NetworkResult sealed interface to standardize API response states. Extract the :feature:auth module and verify Gradle Single Responsibility constraints (it cannot access anything but :core:domain). | 15 Hrs |
| Subtask 6.2 | Refactor Retrofit interfaces, replacing RxJava Observable returns with Coroutine suspend functions mapped to Flow in repositories. Extract the :feature:contributions module. | 15 Hrs |
| Subtask 6.3 | Extract the highly stable UploadActivity logic into the :feature:upload module while strictly keeping its XML layout intact, ensuring all modernized network responses route correctly through the new module boundaries. | 15 Hrs |
Phase 3: UI Modernization & Feature Modules
| Week 7 | Core UI Module, MD3 System & Compose Integration | 45 Hrs |
| Subtask 7.1 | Establish an independent :core:ui module to centralize the Jetpack Compose design system. Implement a complete Material Design 3 theme (responsive light/dark schemes, CommonsTypography, CommonsShapes), and embed ComposeView into the ContributionsFragment layout. | 10 Hrs |
| Subtask 7.2 | Build a highly reusable Compose component library utilizing the new MD3 tokens. Rewrite the main contribution list rendering (RecyclerView equivalent) purely in Compose utilizing LazyColumn and collectAsLazyPagingItems(). | 20 Hrs |
| Subtask 7.3 | Concurrently migrate legacy image loading to Coil. Strip Fresco XML definitions and implement lightweight Coil AsyncImage composables to deeply optimize the image rendering pipeline. | 15 Hrs |
| Week 8 | Advanced Feature Extraction & Dependency Resolution | 45 Hrs |
| Subtask 8.1 | Extract the Explore screen into a standalone :feature:explore module, leveraging Convention Plugins to enforce a strict dependency graph that relies only on :core:domain (for business logic) and :core:ui (for design system tokens), ensuring the feature remains completely decoupled from data implementation details. | 15 Hrs |
| Subtask 8.2 | Refactor the Nearby map functionality into a standalone :feature:nearby module, establishing a decoupled dependency graph that only uses :core:domain to avoid circular dependencies. | 15 Hrs |
| Subtask 8.3 | Extract the logic into a standalone :feature:contributions module, utilizing Convention Plugins to segregate the legacy logic from the core data layer. Concurrently establish strict inter-module navigation graphs and dependency rules within settings.gradle.kts to prevent circular dependencies. | 15 Hrs |
| Week 9 | Compose UI Migrations: Upload, Media & Nearby | 45 Hrs |
| Subtask 9.1 | Refactor UploadActivity into a Compose-based multi-step wizard. Route the sub-flows strictly utilizing the Android Navigation Component. | 15 Hrs |
| Subtask 9.2 | Migrate the complex, data-dense UI of MediaDetailFragment entirely to Jetpack Compose. | 15 Hrs |
| Subtask 9.3 | Refactor NearbyParentFragment, replacing legacy map implementations with Compose-compatible rendering. | 15 Hrs |
| Week 10 | Remaining Screens & Polish | 45 Hrs |
| Subtask 10.1 | Refactor ProfileActivity to Compose, fully migrating the interactive achievements and leaderboard UI components. | 25 Hrs |
| Subtask 10.2 | Migrate SettingsActivity and AboutActivity to Compose-based screens. | 10 Hrs |
| Subtask 10.3 | Refactor the final remaining screens ReviewActivity and NotificationActivity. Execute a final codebase sweep to systematically delete most of the deprecated XML layouts. | 10 Hrs |
| Week 11 | Modern Testing Stack & Coroutine Unit Testing | 45 Hrs |
| Subtask 11.1 | Setup the modern testing stack: JUnit 5, MockK, and either Turbine or kotlinx-coroutines-test, fully replacing legacy JUnit 4 and Mockito dependencies across the project. | 20 Hrs |
| Subtask 11.2 | Implement ViewModel unit tests utilizing runTest, StandardTestDispatcher, and kotlin-coroutines-test to validate sequential StateFlow emissions. | 25 Hrs |
| Week 12 | End-to-End Testing, Community Feedback & Final Documentation | 45 Hrs |
| Subtask 12.1 | Conduct final end-to-end testing across the entire application to resolve all remaining bugs and ensure absolute stability before the final submission. | 20 Hrs |
| Subtask 12.2 | Conduct a feedback round from early adopters and community members; work on follow-up issues or PRs based on that feedback to ensure production readiness. | 15 Hrs |
| Subtask 12.3 | Prepare the final blog post summarizing the project outcomes, architectural shifts, and future directions for the Wikimedia Commons app. Submit final work product. | 10 Hrs |
Extended Timeline
This section outlines additional work that may be carried out if time permits or remains after
the official GSoC timeline.
To fortify the application's security posture, several critical vulnerabilities within the legacy architecture will be systematically addressed. First, we will eliminate the potentially insecure practice of storing account passwords in AccountManager, transitioning to a token only authentication model secured by Jetpack EncryptedSharedPreferences.
At the network level, OkHttp CertificatePinner will be implemented to prevent Man in the middle (MITM) attacks. To protect the application from reverse engineering, we will remove legacy Don't obfuscate ProGuard rules and fully enable R8 obfuscation. Finally, runtime security will be enhanced by integrating the RootBeer library to detect compromised device environments, and Flag Secure will be applied to the window flags of sensitive screens.
8. What motivates me to work with Wikimedia for GSOC
The Wikimedia Commons Android app is a cornerstone of the open knowledge ecosystem, empowering millions to enrich one of the world's largest freely licensed media repositories. Contributing to this mission through Google Summer of Code is a unique privilege to build software that democratizes information globally.
Beyond its impact, the Commons app is backed by a vibrant engineering community.Collaborating in this environment presents an ideal opportunity to master large scale open source development,adhere to rigorous code quality standards, and collectively maintain software that operates at a massive scale.
The proposed initiative Modularization and Jetpack Compose migration is critical overcoming the technical friction inherent in the app's legacy architecture. Currently, the heavily coupled monolithic structure complicates dependency management, hinders isolated testing, and inflates Gradle build times.
By decoupling the codebase into high cohesive,feature based modules,we can enforce strict architectural boundaries.. This restructuring will cleanly separate the presentation,domain, and data layers,optimizing parallel build execution and paving the way for a more robust Clean Architecture implementation.
Simultaneously, migrating the presentation layer to Jetpack Compose will fundamentally modernize the application's UI paradigm. Transitioning away from imperative XML layouts and state-heavy view controllers to a declarative framework allows us to enforce a Unidirectional Data Flow(UDF).
Crucially,this migration will overhaul the app's routing architecture. By phasing out manual Fragment transactions, we will integrate the modern Android Navigation API.Adopting this industry standard moves the app toward a highly stable single -activity architecture with a centralized NavHost.
This provides robust, type safe argument passing and completely eliminates the lifecycle related crashes and state loss exceptions notoriously associated with complex Fragment backstacks. Furthermore, Compose will allow us to build a reusable, modular design system that guarantees visual consistency across the app.
The downstream impact of these architectural shifts is profound. For the developer community, feature isolation and a standardized navigation component dramatically lower the cognitive load for new contributors and enable highly targeted, reliable unit and UI testing without bringing up the entire application graph.
For the end users, a modernized UI ensures a more performant fluid, and crash resilient experience. I am eager to drive this modernization forward, delivering scalable, maintainable and contributor-friendly platform that strengthens Wikimedia's mission of global collaboration.
9. Career Goals
As an Android developer, my primary goal is to master the Android SDK, multithreading, and related technologies while building a strong portfolio of projects that includes personal applications, open-source contributions, and projects developed through academic or professional work.
I have also explored AOSP to understand the deeper internals of the Android platform and continue to actively contribute to open-source projects as part of my growth as a developer.
As I gain more experience, I aim to strengthen my expertise in UI/UX design alongside enterprise-scale mobile application development and gradually move into leadership roles. Being largely self taught, open source has played a crucial role in my learning journey, and I hope to give back to the community by sharing knowledge through technical blogs, tutorials, speaking engagements, and continued open-source contributions.
10. Previous Participation in GSoC
No, this is my first participation in Google Summer of Code
11. Application to Multiple Orgs
I will be applying only to Wikimedia for this year's GSoC

![Hilt].png (1,612×790 px, 217 KB)](https://phab.wmfusercontent.org/file/data/tsr5oauqfdtcpzcdh6io/PHID-FILE-qsai6naz3dx3jdlykztp/Hilt%5D.png)



