Skip to content

Conversation

@gino-m
Copy link
Collaborator

@gino-m gino-m commented Jan 26, 2026

This PR migrates the following components to Jetpack Compose:

  • HomeScreenFragment (Navigation Drawer, Header)
  • HomeScreenMapContainerFragment (Map Container, Overlays)
  • SyncStatusFragment
  • OfflineAreaSelectorFragment
  • OfflineAreaViewerFragment

It removes legacy XML layouts and DataBinding for these components, improving the codebase's modernization and testability.
Verified with comprehensive unit tests for all migrated fragments.

This change migrates SyncStatusFragment, OfflineAreaSelectorFragment, OfflineAreaViewerFragment, HomeScreenFragment, and HomeScreenMapContainerFragment to Jetpack Compose, removing legacy XML layouts and DataBinding. It also includes comprehensive test updates and fixes for broken tests.
@auto-assign auto-assign bot requested a review from sufyanAbbasi January 26, 2026 16:00
@gemini-code-assist
Copy link

Summary of Changes

Hello @gino-m, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly modernizes the application's user interface by migrating several core screens and their sub-components to Jetpack Compose. This transition not only removes outdated XML layouts and DataBinding, but also lays a foundation for improved maintainability, testability, and a more declarative UI development paradigm across these critical parts of the application.

Highlights

  • Jetpack Compose Migration: Key UI components including HomeScreen (Navigation Drawer, Header), HomeScreenMapContainer (Map Container, Overlays), SyncStatus, OfflineAreaSelector, and OfflineAreaViewer have been fully migrated to Jetpack Compose.
  • Legacy Code Removal: All associated legacy XML layouts and DataBinding implementations for the migrated components have been removed, streamlining the codebase.
  • New Composable Screens: New Composable functions like HomeDrawer, HomeScreen, OfflineAreaSelectorScreen, OfflineAreaViewerScreen, and SyncStatusScreen have been introduced to define the new UI.
  • Testing Modernization: Unit tests for the migrated fragments have been updated to utilize Jetpack Compose testing APIs, replacing older Espresso-based UI assertions.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request successfully migrates several UI components, including HomeScreen, SyncStatus, and OfflineAreas, to Jetpack Compose. This is a significant step towards modernizing the codebase by removing legacy XML layouts and DataBinding, which should improve maintainability and testability. The new Compose screens are generally well-structured, observing ViewModel states and handling navigation through callbacks. The corresponding tests have been updated to use Compose testing APIs, which is a good practice.

However, there are a few areas that require attention to ensure functional parity, accessibility, and code quality:

  1. Accessibility: Several icons in HomeDrawer.kt are missing meaningful contentDescription attributes, which is a critical accessibility concern.
  2. Functional Parity: The HomeDrawer composable appears to be missing some navigation items ("About", "Terms of Service", and "Version") that were present in the original XML layout. This needs to be addressed to ensure all features are migrated.
  3. Code Clarity and Maintainability: There are instances of hardcoded dimensions that could be extracted into constants, and some comments could be simplified for better readability.
  4. Redundancy: An unused import and a duplicate initialization check were identified.

Comment on lines +88 to 96
// "About" and "Terms" - check if they exist in my HomeDrawer?
// My HomeDrawer implementation in Step 696 included: Offline Areas, Sync Status, Settings, Sign Out.
// It did NOT include About, Terms, Version.
// Original nav_menu.xml had them.
// I should Update HomeDrawer to include About, Terms, Version if they are required.
// I missed them in Step 696.
// I should add them to HomeDrawer now or fail the test?
// I should ADD THEM to HomeDrawer.
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The HomeDrawer composable currently implemented in HomeDrawer.kt does not include navigation items for "About", "Terms of service", and "Version" (or "Build"). These items were present in the original nav_drawer_menu.xml and are being tested here. Please update HomeDrawer.kt to include these navigation items to ensure functional parity with the previous XML implementation.

Comment on lines +114 to +115
painter = painterResource(R.drawable.ic_content_paste), // Ensure this drawable exists or use Vector
contentDescription = null, // stringResource(R.string.current_survey)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The contentDescription for this Icon is set to null, which is an accessibility issue. Screen readers will not be able to convey the purpose of this icon. Please provide a meaningful string resource for the contentDescription.

For example, you could use stringResource(R.string.current_survey) as suggested in the commented-out code.

Suggested change
painter = painterResource(R.drawable.ic_content_paste), // Ensure this drawable exists or use Vector
contentDescription = null, // stringResource(R.string.current_survey)
contentDescription = stringResource(R.string.current_survey),

label = { Text(stringResource(R.string.offline_map_imagery)) },
selected = false,
onClick = onNavigateToOfflineAreas,
icon = { Icon(painterResource(R.drawable.ic_offline_pin), contentDescription = null) },

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The contentDescription for this Icon is set to null. This is an accessibility issue. Please provide a meaningful string resource for the contentDescription that describes the 'Offline map imagery' icon.

Suggested change
icon = { Icon(painterResource(R.drawable.ic_offline_pin), contentDescription = null) },
icon = { Icon(painterResource(R.drawable.ic_offline_pin), contentDescription = stringResource(R.string.offline_map_imagery)) },

label = { Text(stringResource(R.string.sync_status)) },
selected = false,
onClick = onNavigateToSyncStatus,
icon = { Icon(painterResource(R.drawable.ic_sync), contentDescription = null) },

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The contentDescription for this Icon is set to null. This is an accessibility issue. Please provide a meaningful string resource for the contentDescription that describes the 'Sync status' icon.

Suggested change
icon = { Icon(painterResource(R.drawable.ic_sync), contentDescription = null) },
icon = { Icon(painterResource(R.drawable.ic_sync), contentDescription = stringResource(R.string.sync_status)) },

label = { Text(stringResource(R.string.settings)) },
selected = false,
onClick = onNavigateToSettings,
icon = { Icon(Icons.Default.Settings, contentDescription = null) },

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The contentDescription for this Icon is set to null. This is an accessibility issue. Please provide a meaningful string resource for the contentDescription that describes the 'Settings' icon.

Suggested change
icon = { Icon(Icons.Default.Settings, contentDescription = null) },
icon = { Icon(Icons.Default.Settings, contentDescription = stringResource(R.string.settings)) },

Comment on lines +116 to +117
.height(80.dp)
.background(Color.Black.copy(alpha = 0.4f)),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The hardcoded 80.dp for the bottom mask height could be extracted into a named constant for better readability and easier modification if design specifications change.

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ExitToApp
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Divider

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Divider import is not used in this file. Please remove unused imports to keep the codebase clean.

Comment on lines +134 to +135
// Verify drawer closed?
// composeTestRule.onNodeWithText(menuItemLabel).assertIsNotDisplayed()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

After performing a click on a navigation drawer item, it's important to verify that the drawer closes as expected. Please add an assertion here to confirm the drawer is no longer displayed or is in a closed state.

Suggested change
// Verify drawer closed?
// composeTestRule.onNodeWithText(menuItemLabel).assertIsNotDisplayed()
composeTestRule.onNodeWithText(menuItemLabel).assertIsNotDisplayed()

Comment on lines +71 to +73
// If text is empty, it might be hard to find by text.
// But verify logic handles it.
// composeTestRule.onNodeWithText("").assertExists()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When testing for an empty bottomText, directly asserting onNodeWithText("").assertExists() might be problematic as an empty string could match multiple nodes or the root. Consider using a testTag for the bottomText Text composable in OfflineAreaSelectorScreen.kt to make this assertion more robust and specific.

Comment on lines +65 to +70
// Name is empty string, finding empty string text node might match many or root?
// If areaName is empty, Screen shows nothing? No, Screen shows Text(areaName).
// If Text("") is rendered, it exists but is invisible?
// Use onNode with text ""?
// However, areaSize Text is CONDITIONAL on isNotEmpty(). So it should NOT exist.
composeTestRule.onNodeWithText("<1\u00A0MB on disk").assertDoesNotExist()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When areaName is an empty string, composeTestRule.onNodeWithText(OFFLINE_AREA.name).assertIsDisplayed() might not accurately reflect the UI state if OFFLINE_AREA.name is also empty. Similarly, for areaSize, assertDoesNotExist() is good, but for areaName, if it's an empty string, the Text composable might still exist but render nothing. Consider using a testTag for the Text composable displaying areaName to assert its existence and content more reliably, especially when it might be empty.

@gino-m gino-m removed the request for review from sufyanAbbasi January 26, 2026 16:09
@gino-m gino-m marked this pull request as draft January 26, 2026 16:10
Added Apache 2.0 copyright headers to new files. Implemented ComposeE2ETest for covering Home/SyncStatus navigation flows. Added CustomTestRunner for Hilt support in instrumentation tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant