Skip to content

Conversation

@OrangeAndGreen
Copy link
Contributor

@OrangeAndGreen OrangeAndGreen commented Aug 14, 2025

https://dimagi.atlassian.net/browse/CCCT-1614

Product Description

Showing the side navigation drawer in the App Home page when PersonalID is configured on the device.
Seated app highlighted in side nav menu on Login and App Home pages.
Navigating to Opportunities/Messages from App Home only requires unlock if it wasn't performed to access the current app.

Feature Flag

PersonalID, Side Navigation Drawer

Safety Assurance

Safety story

Dev tested

Automated test coverage

None

QA Plan

  • Verify the side navigation drawer appears on App Home whenever PersonalID is configured (otherwise does not appear)
  • Verify clicking Opportunities/Messages from App Home requires unlock when app was logged in via username/password
  • Verify clicking Opportunities/Messages from App Home does not require unlock when app was logged in via PersonalID unlock (including access to Connect apps from opportunity list)
  • Verify the seated app is indicated with a Chevron in the side menu when on Login and App Home pages
  • Verify clicking an app other than the seated one when in App Home logs the user out and goes to the Login page with the newly selected app seated

…ng if user hasn't already unlocked to login to app.

Logging out of app when user selects a new app from side navigation menu, and going to Login for the selected app.
Moved a couple constants to decouple activities.
…n menu.

Added activity-level control for when to highlight the seated app.
Removed some unused imports.
@OrangeAndGreen OrangeAndGreen added this to the 2.59 milestone Aug 14, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 14, 2025

📝 Walkthrough

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dv/ap_home_sidenav

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (6)
app/res/layout/nav_drawer_sublist_item.xml (1)

12-19: Align initial visibility with adapter logic and mark decorative icon as not important for accessibility.

The adapter toggles the highlight between VISIBLE and INVISIBLE. Keeping the initial state as GONE can cause one-time layout shifts on first bind. Also, declare the icon as decorative for a11y.

Apply:

-        android:visibility="gone"/>
+        android:visibility="invisible"
+        android:importantForAccessibility="no"/>
app/src/org/commcare/navdrawer/NavDrawerAdapter.kt (2)

110-121: Highlight visibility handling is correct; ensure XML default matches INVISIBLE.

Reserving space via INVISIBLE is the right call to keep titles aligned. Make sure the layout’s sublist_highlight_icon defaults to INVISIBLE (instead of GONE) to avoid first-bind jumps. See my layout comment for a small XML tweak.


127-131: Consider DiffUtil/ListAdapter to avoid full refreshes.

notifyDataSetChanged() will redraw the entire drawer, which can cause unnecessary jank. Optional: adopt ListAdapter with DiffUtil to compute minimal updates when expanding/collapsing parents or toggling highlights.

app/src/org/commcare/activities/LoginActivity.java (1)

1024-1033: Minor UX tweak: close the drawer before starting SeatAppActivity.

Closing the drawer immediately provides a snappier transition and avoids any transient overlap while the activity starts.

-                seatAppIfNeeded(recordId);
-                closeDrawer();
+                closeDrawer();
+                seatAppIfNeeded(recordId);
app/src/org/commcare/navdrawer/BaseDrawerController.kt (1)

119-121: Drawer visibility gating and sign-in UI states: confirm intent vs. implementation

  • The drawer content currently appears only when PersonalIdManager.getInstance().isloggedIn() is true. The PR objectives mention “when PersonalID is configured” (not necessarily signed in). If configuration and login are distinct states, this might not match product behavior.
  • setSignedInState toggles signoutView to VISIBLE when not signed in, which reads inverted. Also, signInText is never hidden when signed in.

Please confirm:

  • Whether the gating should be “configured” rather than “logged in”.
  • Whether signoutView is actually a “sign-in” affordance (naming mismatch), or the visibility should be inverted.
  • Whether signInText should be hidden when signed in.

If needed, adjust visibility handling:

// Inside setSignedInState(isSignedIn)
binding.signInText.visibility = if (isSignedIn) View.GONE else View.VISIBLE
binding.signoutView.visibility = if (isSignedIn) View.VISIBLE else View.GONE // if this is truly a sign-out view

Also applies to: 182-186

app/src/org/commcare/activities/DispatchActivity.java (1)

500-509: Double-check RESULT_OK handling from HOME_SCREEN sets userTriggeredLogout intentionally

When returning RESULT_OK from StandardHomeActivity after selecting another app, userTriggeredLogout is set to true. If this flag controls “user chose to log out” UX (toasts, telemetry, etc.), confirm this is desired for the “switch app” flow as well. If not, consider a separate reason code or flag to avoid conflating intents.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e5b3468 and 7913491.

📒 Files selected for processing (11)
  • app/res/layout/nav_drawer_sublist_item.xml (1 hunks)
  • app/src/org/commcare/activities/CommCareActivity.java (0 hunks)
  • app/src/org/commcare/activities/CommCareSetupActivity.java (0 hunks)
  • app/src/org/commcare/activities/DispatchActivity.java (7 hunks)
  • app/src/org/commcare/activities/LoginActivity.java (2 hunks)
  • app/src/org/commcare/activities/StandardHomeActivity.java (4 hunks)
  • app/src/org/commcare/connect/ConnectConstants.java (1 hunks)
  • app/src/org/commcare/navdrawer/BaseDrawerActivity.kt (4 hunks)
  • app/src/org/commcare/navdrawer/BaseDrawerController.kt (3 hunks)
  • app/src/org/commcare/navdrawer/NavDrawerAdapter.kt (1 hunks)
  • app/src/org/commcare/navdrawer/NavDrawerParentItem.kt (1 hunks)
💤 Files with no reviewable changes (2)
  • app/src/org/commcare/activities/CommCareSetupActivity.java
  • app/src/org/commcare/activities/CommCareActivity.java
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-04-21T18:48:08.330Z
Learnt from: OrangeAndGreen
PR: dimagi/commcare-android#3037
File: app/src/org/commcare/connect/ConnectConstants.java:11-15
Timestamp: 2025-04-21T18:48:08.330Z
Learning: Request codes used for startActivityForResult should be unique throughout the application, even if they're used in different activities. COMMCARE_SETUP_CONNECT_LAUNCH_REQUEST_CODE and STANDARD_HOME_CONNECT_LAUNCH_REQUEST_CODE should have different values.

Applied to files:

  • app/src/org/commcare/connect/ConnectConstants.java
📚 Learning: 2025-05-08T11:08:18.530Z
Learnt from: shubham1g5
PR: dimagi/commcare-android#0
File: :0-0
Timestamp: 2025-05-08T11:08:18.530Z
Learning: PR #3048 "Phase 4 Connect PR" introduces a substantial feature called "Connect" to the CommCare Android app, which includes messaging, job management, delivery tracking, payment processing, authentication flows, and learning modules. It follows a modern architecture using Navigation Components with three navigation graphs, segregated business logic in Manager classes, and proper database persistence.

Applied to files:

  • app/src/org/commcare/activities/StandardHomeActivity.java
  • app/src/org/commcare/activities/DispatchActivity.java
🧬 Code Graph Analysis (3)
app/src/org/commcare/activities/StandardHomeActivity.java (5)
app/src/org/commcare/connect/ConnectConstants.java (1)
  • ConnectConstants (8-48)
commcare-support-library/src/main/java/org/commcare/commcaresupportlibrary/CommCareLauncher.java (1)
  • CommCareLauncher (11-45)
app/src/org/commcare/navdrawer/BaseDrawerActivity.kt (5)
  • handleDrawerItemClick (43-51)
  • closeDrawer (81-83)
  • navigateToConnectMenu (61-69)
  • navigateToMessaging (71-79)
  • shouldHighlightSeatedApp (26-28)
app/src/org/commcare/navdrawer/BaseDrawerController.kt (2)
  • OPPORTUNITIES (36-42)
  • closeDrawer (188-190)
app/src/org/commcare/connect/ConnectNavHelper.kt (2)
  • goToConnectJobsList (45-48)
  • goToMessaging (27-30)
app/src/org/commcare/activities/LoginActivity.java (3)
app/src/org/commcare/connect/ConnectConstants.java (1)
  • ConnectConstants (8-48)
app/src/org/commcare/navdrawer/BaseDrawerActivity.kt (3)
  • shouldHighlightSeatedApp (26-28)
  • handleDrawerItemClick (43-51)
  • closeDrawer (81-83)
app/src/org/commcare/navdrawer/BaseDrawerController.kt (1)
  • closeDrawer (188-190)
app/src/org/commcare/activities/DispatchActivity.java (2)
app/src/org/commcare/connect/ConnectConstants.java (1)
  • ConnectConstants (8-48)
app/src/org/commcare/activities/LoginActivity.java (1)
  • LoginActivity (93-1038)
🔇 Additional comments (18)
app/src/org/commcare/connect/ConnectConstants.java (1)

15-16: No stale definitions/usages found — constants are centralized

Ran the repo-wide search you suggested. The only occurrences are the two definitions in ConnectConstants; no other definitions, raw string usages, or LoginActivity references were found.

  • app/src/org/commcare/connect/ConnectConstants.java — lines 15–16:
    • PERSONALID_MANAGED_LOGIN = "personalid-managed-login"
    • CONNECT_MANAGED_LOGIN = "connect-managed-login"
app/res/layout/nav_drawer_sublist_item.xml (1)

6-6: Padding reduction is consistent with introducing a leading highlight glyph.

Changing start padding from 48dp to 24dp makes sense given the new 24dp highlight icon before the existing icon; net left offset remains consistent.

app/src/org/commcare/navdrawer/NavDrawerParentItem.kt (1)

15-20: Adding isHighlighted is fine; double-check equality semantics of ChildItem.

Making ChildItem a data class means equals/hashCode now include isHighlighted. If any code treats ChildItem identity as (childTitle, recordId, parentType), toggling highlight will change identity semantics and may affect set membership, caching, or DiffUtil behavior.

If identity should ignore UI state, consider one of:

  • Switch to a regular class with custom equals/hashCode that exclude isHighlighted; or
  • Keep data class, but ensure any DiffUtil or collections use a stable ID separate from equality.
app/src/org/commcare/activities/LoginActivity.java (3)

5-6: Good: use shared ConnectConstants for intent extras.

Replacing local keys with static imports keeps intent contract centralized and reduces drift.


1015-1018: Correct: always highlight seated app on Login.

This matches the PR objective of showing the seated app selection on Login.


537-546: Confirmed: constants centralized and no raw-string usages remain

ConnectConstants defines the keys at app/src/org/commcare/connect/ConnectConstants.java:15–16. A repo-wide search found no other definitions, no raw string occurrences of "personalid-managed-login" or "connect-managed-login", and no usages of LoginActivity.PERSONALID_MANAGED_LOGIN / CONNECT_MANAGED_LOGIN.

No changes required here — resolved.

app/src/org/commcare/navdrawer/BaseDrawerController.kt (1)

28-29: Constructor change verified — no remaining old-arity callsites found

Repo-wide search for "BaseDrawerController(" shows only the class declaration and the single instantiation in BaseDrawerActivity, which already supplies shouldHighlightSeatedApp().

  • app/src/org/commcare/navdrawer/BaseDrawerController.kt — class declaration (around line 25)
  • app/src/org/commcare/navdrawer/BaseDrawerActivity.kt — instantiation (around lines 33–36) passing shouldHighlightSeatedApp()
app/src/org/commcare/activities/StandardHomeActivity.java (3)

63-65: Wiring PERSONALID_MANAGED_LOGIN looks good

Reading the flag at session creation time is correct and aligns with the navigation logic further below.


318-321: Enabling seated-app highlighting: LGTM

Overriding shouldHighlightSeatedApp() to true for StandardHomeActivity aligns with the PR goals to highlight the seated app.


249-282: Use of Java “switch arrow” may break Android builds; prefer classic switch-case

Android projects commonly target language levels where “switch (x) { case A -> { … } }” isn’t supported by the toolchain/desugaring across all AGP/JDK combos. To avoid build/runtime compatibility issues, switch to classic switch-case with breaks.

Apply:

-    protected void handleDrawerItemClick(@NonNull BaseDrawerController.NavItemType itemType, String recordId) {
-        switch (itemType) {
-            case COMMCARE_APPS -> {
-                if(recordId != null) {
-                    String currentSeatedId = CommCareApplication.instance().getCurrentApp().getUniqueId();
-                    if(!recordId.equals(currentSeatedId)) {
-                        //Navigate to LoginActivity for selected app
-                        CommCareApplication.instance().closeUserSession();
-                        Intent i = new Intent();
-                        i.putExtra(CommCareLauncher.SESSION_ENDPOINT_APP_ID, recordId);
-                        setResult(RESULT_OK, i);
-                        finish();
-                    }
-                }
-            }
-            case OPPORTUNITIES -> {
-                if(personalIdManagedLogin) {
-                    ConnectNavHelper.INSTANCE.goToConnectJobsList(this);
-                    closeDrawer();
-                } else {
-                    navigateToConnectMenu();
-                }
-            }
-            case MESSAGING -> {
-                if(personalIdManagedLogin) {
-                    ConnectNavHelper.INSTANCE.goToMessaging(this);
-                    closeDrawer();
-                } else {
-                    navigateToMessaging();
-                }
-            }
-        }
-    }
+    protected void handleDrawerItemClick(@NonNull BaseDrawerController.NavItemType itemType, String recordId) {
+        switch (itemType) {
+            case COMMCARE_APPS: {
+                if (recordId != null) {
+                    String currentSeatedId = CommCareApplication.instance().getCurrentApp().getUniqueId();
+                    if (!recordId.equals(currentSeatedId)) {
+                        // Navigate to LoginActivity for selected app
+                        CommCareApplication.instance().closeUserSession();
+                        Intent i = new Intent();
+                        i.putExtra(CommCareLauncher.SESSION_ENDPOINT_APP_ID, recordId);
+                        setResult(RESULT_OK, i);
+                        finish();
+                    }
+                }
+                break;
+            }
+            case OPPORTUNITIES: {
+                if (personalIdManagedLogin) {
+                    ConnectNavHelper.INSTANCE.goToConnectJobsList(this);
+                    closeDrawer();
+                } else {
+                    navigateToConnectMenu();
+                }
+                break;
+            }
+            case MESSAGING: {
+                if (personalIdManagedLogin) {
+                    ConnectNavHelper.INSTANCE.goToMessaging(this);
+                    closeDrawer();
+                } else {
+                    navigateToMessaging();
+                }
+                break;
+            }
+            default:
+                break;
+        }
+    }
⛔ Skipped due to learnings
Learnt from: OrangeAndGreen
PR: dimagi/commcare-android#3108
File: app/src/org/commcare/fragments/connect/ConnectJobsListsFragment.java:313-320
Timestamp: 2025-06-06T20:41:41.740Z
Learning: The dimagi/commcare-android project uses Java 17, which supports modern Java syntax features including comma-separated case labels in switch statements.
app/src/org/commcare/navdrawer/BaseDrawerActivity.kt (3)

26-29: Extensibility hook for highlighting: LGTM

Providing a protected open shouldHighlightSeatedApp() with a default false is a clean extension point. Callers can opt-in (as StandardHomeActivity does).


35-37: Plumbing highlight flag through to the controller: LGTM

Passing shouldHighlightSeatedApp() into BaseDrawerController keeps the controller stateless with respect to Activity type.


61-83: Protected navigation helpers: LGTM

Changing navigateToConnectMenu, navigateToMessaging, and closeDrawer to protected enables subclasses to tailor behavior and is consistent with StandardHomeActivity’s override-driven logic.

app/src/org/commcare/activities/DispatchActivity.java (5)

90-91: Transport for cross-app login redirection: LGTM

redirectToLoginAppId is a clear addition to carry app-id across flows; good naming and scope.


302-309: Robust app-id selection for LoginActivity: LGTM

Fallback from SESSION_ENDPOINT_APP_ID to redirectToLoginAppId and passing via LoginActivity.EXTRA_APP_ID looks correct. Clearing redirectToLoginAppId after consumption prevents stale state.


345-346: Passing PERSONALID_MANAGED_LOGIN to home: LGTM

This propagates the login mode so UI can skip further unlock prompts where appropriate.


463-467: Capturing redirectToLoginAppId from results: LGTM

This enables the “select another app” flow from App Home to feed back into login correctly.


491-496: Using ConnectConstants for managed-login flags: LGTM

Switching to centralized constants reduces coupling to LoginActivity and improves consistency.

Comment on lines +132 to 138
val seatedApp = if(highlightSeatedApp)
CommCareApplication.instance().currentApp.uniqueId else null

val commcareApps = MultipleAppsUtil.getUsableAppRecords().map {
NavDrawerItem.ChildItem(it.displayName, it.uniqueId, NavItemType.COMMCARE_APPS)
NavDrawerItem.ChildItem(it.displayName, it.uniqueId, NavItemType.COMMCARE_APPS,
it.uniqueId == seatedApp)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Possible NPE when resolving current app; make lookup null-safe

CommCareApplication.instance().currentApp can be null in edge cases (e.g., before an app is seated). Use a null-safe access to avoid crashes and keep highlighting off when no app is seated.

Apply:

-            val seatedApp = if(highlightSeatedApp)
-                CommCareApplication.instance().currentApp.uniqueId else null
+            val seatedApp = if (highlightSeatedApp) {
+                CommCareApplication.instance().currentApp?.uniqueId
+            } else {
+                null
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val seatedApp = if(highlightSeatedApp)
CommCareApplication.instance().currentApp.uniqueId else null
val commcareApps = MultipleAppsUtil.getUsableAppRecords().map {
NavDrawerItem.ChildItem(it.displayName, it.uniqueId, NavItemType.COMMCARE_APPS)
NavDrawerItem.ChildItem(it.displayName, it.uniqueId, NavItemType.COMMCARE_APPS,
it.uniqueId == seatedApp)
}
val seatedApp = if (highlightSeatedApp) {
CommCareApplication.instance().currentApp?.uniqueId
} else {
null
}
val commcareApps = MultipleAppsUtil.getUsableAppRecords().map {
NavDrawerItem.ChildItem(it.displayName, it.uniqueId, NavItemType.COMMCARE_APPS,
it.uniqueId == seatedApp)
}
🤖 Prompt for AI Agents
In app/src/org/commcare/navdrawer/BaseDrawerController.kt around lines 132 to
138, computing seatedApp uses CommCareApplication.instance().currentApp which
can be null; change the lookup to a null-safe access (e.g.,
CommCareApplication.instance().currentApp?.uniqueId) so seatedApp becomes null
when no app is seated, and ensure the highlight condition uses that nullable
value (keep highlighting off when seatedApp is null) to avoid an NPE.

Copy link
Contributor

@shubham1g5 shubham1g5 left a comment

Choose a reason for hiding this comment

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

Left a nit but looks good in general

//Navigate to LoginActivity for selected app
CommCareApplication.instance().closeUserSession();
Intent i = new Intent();
i.putExtra(CommCareLauncher.SESSION_ENDPOINT_APP_ID, recordId);
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems a bit confusing to me that we are using 2 different constants in different places to denote the app id - LoginActivity.EXTRA_APP_ID and CommCareLauncher.SESSION_ENDPOINT_APP_ID. I would prefer we can instead use LoginActivity.EXTRA_APP_ID itself here and not tie it to SESSION_ENDPOINT_APP_ID

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@OrangeAndGreen OrangeAndGreen merged commit e9d6741 into commcare_2.59 Aug 14, 2025
1 of 2 checks passed
@OrangeAndGreen OrangeAndGreen deleted the dv/ap_home_sidenav branch August 14, 2025 18:01
@coderabbitai coderabbitai bot mentioned this pull request Sep 12, 2025
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants