Skip to content

Conversation

@OrangeAndGreen
Copy link
Contributor

@OrangeAndGreen OrangeAndGreen commented Apr 18, 2025

https://dimagi.atlassian.net/browse/CCCT-707
cross-request: dimagi/commcare-core#1455

Product Description

ConnectID Messaging is now accessible from the app home page when signed in to an app via ConnectID.

Technical Summary

Converted menu in app home to an XML resource.
Added messaging to app home menu (as a toolbar action).
Only showing messaging when user is signed in to an app via ConnectID.
Retrieving messages when app home resumes and updating messaging icon.

Feature Flag

ConnectID Messaging

Safety Assurance

Safety story

Tested locally, see specific tests cases in QA Plan

Automated test coverage

None for ConnectID yet

QA Plan

-Verify that the existing menu functionality on the app home page still works
-Log into a traditional app via user/pass and verify that the messaging icon does not appear
-Log into a traditional app managed by ConnectID and verify that the messaging icon appears
-Log into a Connect app and verify that the messaging icon appears
-Send a ConnectID message to the user, then login to an app via ConnectID on the device and verify that the red dot appears on the messaging icon

@coderabbitai
Copy link

coderabbitai bot commented Apr 18, 2025

📝 Walkthrough

Walkthrough

This set of changes introduces a new XML menu resource (menu_app_home.xml) that defines the structure and items of the main application menu in an Android app. The menu handling in StandardHomeActivity is refactored to use this XML resource for menu inflation, replacing previous programmatic menu creation and static integer constants with resource IDs. A new messaging menu item (action_messaging) is added, with its icon dynamically updated based on unread messages using a new static method updateMessagingIcon in the MessageManager class. The visibility of this item is controlled by the shouldShowMessaging() method. Lifecycle methods (onResume and onPause) are introduced to register and unregister a BroadcastReceiver that listens for messaging update broadcasts, ensuring the messaging icon is updated in response to relevant events. Related changes in ConnectActivity delegate messaging icon updates to MessageManager, and menu item IDs are updated for consistency. Unused or redundant code is removed from ConnectMessagingActivity, and unit tests are updated to reference resource IDs.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant StandardHomeActivity
    participant MessageManager
    participant BroadcastReceiver
    participant ConnectManager

    User->>StandardHomeActivity: Launches activity
    StandardHomeActivity->>StandardHomeActivity: onCreateOptionsMenu inflates menu_app_home.xml
    StandardHomeActivity->>MessageManager: updateMessagingIcon(context, messagingMenuItem)
    MessageManager-->>StandardHomeActivity: Sets icon based on unread messages

    Note over StandardHomeActivity: Registers BroadcastReceiver in onResume

    BroadcastReceiver->>StandardHomeActivity: Receives MESSAGING_UPDATE_BROADCAST
    StandardHomeActivity->>MessageManager: updateMessagingIcon(context, messagingMenuItem)

    User->>StandardHomeActivity: Selects action_messaging menu item
    StandardHomeActivity->>ConnectManager: goToMessaging(activity)
Loading

Possibly related PRs

Suggested reviewers

  • pm-dimagi
  • shubham1g5

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.
✨ Finishing Touches
  • 📝 Generate Docstrings

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.
    • Generate unit testing code for this file.
    • 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. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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

Documentation and Community

  • 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

🔭 Outside diff range comments (1)
app/src/org/commcare/connect/ConnectManager.java (1)

429-433: 💡 Verification agent

🧩 Analysis chain

Method looks correct for launching the messaging activity

The goToMessaging method provides proper navigation to the ConnectMessagingActivity.

Verify that the calling conditions for this method check whether the user is signed in via ConnectID, as per PR requirements that messaging should only be visible to users signed in via ConnectID.


🏁 Script executed:

#!/bin/bash
# Search for calls to goToMessaging and check if they're properly guarded
rg -p "goToMessaging" --glob "*.java"

Length of output: 370


🏁 Script executed:

#!/bin/bash
# Show context around calls to goToMessaging to verify ConnectID guard
rg -C5 "ConnectManager.goToMessaging" app/src/org/commcare/activities/connect/ConnectActivity.java
rg -C5 "ConnectManager.goToMessaging" app/src/org/commcare/activities/StandardHomeActivity.java

Length of output: 840


Ensure messaging is only accessible to authenticated ConnectID users

The goToMessaging method itself correctly launches ConnectMessagingActivity, but its invocations in both activities aren’t guarded by a ConnectID sign‐in check. As a result, users who aren’t signed in can still tap “Messaging.” You should either:

  • Wrap the calls in onOptionsItemSelected with a check against your ConnectID auth state (e.g., if (!ConnectManager.isSignedIn()) { /* show prompt */ return true; }) before calling goToMessaging(this);, or
  • Conditionally inflate or hide the action_messaging menu item in onCreateOptionsMenu unless ConnectManager.isSignedIn() is true.

Locations needing fixes:

  • app/src/org/commcare/activities/connect/ConnectActivity.java → onOptionsItemSelected handling of R.id.action_messaging (≈line 169)
  • app/src/org/commcare/activities/StandardHomeActivity.java → onOptionsItemSelected handling of R.id.action_messaging (≈line 254)
🧹 Nitpick comments (6)
app/res/menu/menu_app_home.xml (1)

1-49: Menu resource structure looks good

This new XML menu resource for the app home provides a clean structure with proper organization of menu items. The messaging item is set to always show in the action bar, while other items are in the overflow menu.

A couple of minor suggestions:

  1. Consider using string resources for all menu item titles for better localization support
  2. Some menu items are missing icons (like action_set_pin)

For improved localization support, replace hardcoded titles with string resources:

-        android:title="Update"
+        android:title="@string/update"

-        android:title="Saved Forms"
+        android:title="@string/saved_forms"

-        android:title="Change Language"
+        android:title="@string/change_language"

-        android:title="About"
+        android:title="@string/about"

-        android:title="Advanced"
+        android:title="@string/advanced"

-        android:title="Preferences"
+        android:title="@string/preferences"

-        android:title="Set PIN"
+        android:title="@string/set_pin"

-        android:title="Update CommCare"
+        android:title="@string/update_commcare"
app/src/org/commcare/activities/connect/ConnectActivity.java (2)

115-132: Minor: guard icon refresh until menu is inflated

messagingMenuItem is null on the very first onResume() (executed before onCreateOptionsMenu() during initial launch).
MessageManager.updateMessagingIcon() handles the null, so there is no crash, but the broadcast receiver may fire before the menu exists, losing the update.

A tiny improvement is to early‑return when messagingMenuItem == null instead of hitting the DB:

protected void onResume() {
    super.onResume();
-    MessageManager.updateMessagingIcon(this, messagingMenuItem);
+    if (messagingMenuItem != null) {
+        MessageManager.updateMessagingIcon(this, messagingMenuItem);
+    }

Purely an optimisation—feel free to skip if you prefer to keep the code compact.


153-156: Analytics mapping missing for new menu id

action_messaging is handled in onOptionsItemSelected, but it is not recorded in the analytics map in createMenuItemToAnalyticsParamMapping() (still only lives in StandardHomeActivity).
If you want to track taps consistently, add an entry such as:

menuIdToAnalyticsEvent.put(R.id.action_messaging,
        AnalyticsParamValue.ITEM_MESSAGING);

(assuming a corresponding AnalyticsParamValue exists).

app/src/org/commcare/activities/StandardHomeActivity.java (3)

133-147: Avoid triggering a full network sync on every onResume()

MessageManager.retrieveMessages() performs a REST call, DB writes, and potentially crypto, every time the home activity comes to the foreground.
Typical user behaviour (quick app switching or permission dialogs) can make this run dozens of times in a short period.

Consider throttling:

  1. Cache the last‑sync timestamp and skip if called within N minutes.
  2. Kick off a WorkManager periodic job instead of coupling it to UI lifecycle.
  3. Perform an exponential back‑off if the last request failed.

This will save battery and bandwidth without sacrificing freshness.


169-181: Centralise menu‑title localisation to reduce duplication

Seven consecutive lines set titles manually. That’s easy to forget when adding a new item. Extract to a helper:

private static void localise(Menu menu, int itemId, @StringRes int stringId) {
    menu.findItem(itemId).setTitle(Localization.get(getString(stringId)));
}

Call it in a small loop. Pure readability but keeps this method short.


228-256: Switch‑case body is ripe for refactor

The long else if ladder duplicates the pattern used elsewhere. A switch or a Map<Integer,Runnable> would trim ~40 lines. Not blocking, but pays back quickly as new actions are added.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between aba660f and 5dad478.

📒 Files selected for processing (9)
  • app/res/menu/menu_app_home.xml (1 hunks)
  • app/res/menu/menu_connect.xml (1 hunks)
  • app/res/menu/menu_connect_messaging.xml (0 hunks)
  • app/src/org/commcare/activities/StandardHomeActivity.java (5 hunks)
  • app/src/org/commcare/activities/connect/ConnectActivity.java (4 hunks)
  • app/src/org/commcare/activities/connect/ConnectMessagingActivity.java (0 hunks)
  • app/src/org/commcare/connect/ConnectManager.java (3 hunks)
  • app/src/org/commcare/connect/MessageManager.java (2 hunks)
  • app/unit-tests/src/org/commcare/android/tests/DemoUserRestoreTest.java (2 hunks)
💤 Files with no reviewable changes (2)
  • app/res/menu/menu_connect_messaging.xml
  • app/src/org/commcare/activities/connect/ConnectMessagingActivity.java
🧰 Additional context used
🧬 Code Graph Analysis (3)
app/src/org/commcare/connect/ConnectManager.java (1)
app/src/org/commcare/connect/database/ConnectMessagingDatabaseHelper.java (1)
  • ConnectMessagingDatabaseHelper (20-193)
app/src/org/commcare/connect/MessageManager.java (1)
app/src/org/commcare/connect/database/ConnectMessagingDatabaseHelper.java (1)
  • ConnectMessagingDatabaseHelper (20-193)
app/src/org/commcare/activities/connect/ConnectActivity.java (1)
app/src/org/commcare/connect/MessageManager.java (1)
  • MessageManager (33-378)
🔇 Additional comments (6)
app/src/org/commcare/connect/ConnectManager.java (1)

6-6: Added imports for the messaging feature support

The added imports will support the new messaging functionality. These imports are necessary for:

  1. MenuItem - To update the messaging menu item
  2. ResourcesCompat - For resource handling likely related to icon updates
  3. ConnectMessagingDatabaseHelper - To access messaging data for the feature

Also applies to: 15-15, 43-43

app/res/menu/menu_connect.xml (2)

5-5: Improved menu item identification and localization

Good changes:

  1. Standardizing on action_messaging ID across the app
  2. Using string resources instead of hardcoded titles for proper localization

Also applies to: 7-7


13-13: Proper use of string resource for sync title

Using @string/sync improves localization support for the sync menu item.

app/unit-tests/src/org/commcare/android/tests/DemoUserRestoreTest.java (2)

18-18: Added import for resource access

Adding the import for org.commcare.dalvik.R is necessary to reference the menu resource IDs.


152-159: Updated test to use resource IDs instead of constants

This is a good update to match the code changes. The test now properly references menu items by their resource IDs rather than static constants.

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

263-277: Add analytics entry for the new Messaging action

action_messaging is absent here, so taps on the icon won’t be logged. Mirror the pattern used for other items:

menuIdToAnalyticsEvent.put(R.id.action_messaging,
        AnalyticsParamValue.ITEM_MESSAGING);

(Define ITEM_MESSAGING if missing.)

Comment on lines +70 to +78
public static void updateMessagingIcon(Context context, MenuItem messagingMenuItem) {
if(messagingMenuItem != null) {
int icon = R.drawable.ic_connect_messaging_base;
if(ConnectMessagingDatabaseHelper.getUnviewedMessages(context).size() > 0) {
icon = R.drawable.ic_connect_messaging_unread;
}
messagingMenuItem.setIcon(ResourcesCompat.getDrawable(context.getResources(), icon, null));
}
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Avoid loading full message objects just to test the “unread” state

getUnviewedMessages(context) deserialises every unread message into a Java object and returns it in a List, only for us to call .size(). On even a modest message backlog this allocates many objects and hits the DB on the UI thread every time the icon is refreshed (on onResume, in a BroadcastReceiver, in onCreateOptionsMenu, etc.).

Introduce a lightweight query that only returns a count/boolean and move the DB‑work off the UI thread:

-if(ConnectMessagingDatabaseHelper.getUnviewedMessages(context).size() > 0) {
+if (ConnectMessagingDatabaseHelper.hasUnviewedMessages(context)) {

Where hasUnviewedMessages could be implemented in the helper with:

public static boolean hasUnviewedMessages(Context c) {
    SQLiteDatabase db = getDb(c);
    try (Cursor cur = db.rawQuery(
            "SELECT 1 FROM messaging WHERE " + META_MESSAGE_USER_VIEWED + " = 0 LIMIT 1", null)) {
        return cur.moveToFirst();
    }
}

Besides the performance win, doing the DB call inside a coroutine/AsyncTask (or at least wrapping it in AsyncTask.THREAD_POOL_EXECUTOR) would prevent a Strict‑Mode violation on newer Android versions.


🏁 Script executed:

#!/bin/bash
# Locate all references to getUnviewedMessages
rg --color=never "getUnviewedMessages" -n

# Show the definition of getUnviewedMessages with surrounding context
rg --color=never "public static .*getUnviewedMessages" -n -A5 -B2

# Find where updateMessagingIcon is invoked in the codebase
rg --color=never "updateMessagingIcon" -n

Length of output: 2623


Implement a lightweight “unread” check and move DB calls off the UI thread

Loading full message records just to test for any unread messages blocks the UI and allocates many objects. Instead:

• In app/src/org/commcare/connect/database/ConnectMessagingDatabaseHelper.java (add after line 145):

public static boolean hasUnviewedMessages(Context c) {
    SQLiteDatabase db = getDb(c);
    try (Cursor cur = db.rawQuery(
            "SELECT 1 FROM messaging WHERE " + META_MESSAGE_USER_VIEWED + " = 0 LIMIT 1", 
            null)) {
        return cur.moveToFirst();
    }
}

• In app/src/org/commcare/connect/MessageManager.java (replace at line 73):

-    if (ConnectMessagingDatabaseHelper.getUnviewedMessages(context).size() > 0) {
+    if (ConnectMessagingDatabaseHelper.hasUnviewedMessages(context)) {

• Wrap calls to updateMessagingIcon(…) in a background thread (e.g. AsyncTask.THREAD_POOL_EXECUTOR or a coroutine on Dispatchers.IO), then post the icon update back to the UI thread to avoid Strict‑Mode violations.

@shubham1g5
Copy link
Contributor

is it possible to PR the menu refactor to master first ?

@OrangeAndGreen
Copy link
Contributor Author

Created a PR to master here

@shubham1g5
Copy link
Contributor

thanks @OrangeAndGreen , think we should rebase connect_qa with master now to minimize diff here.

Base automatically changed from connect_qa to dv/connect_initial April 23, 2025 09:19
@shubham1g5 shubham1g5 deleted the branch dv/connect_initial May 14, 2025 17:08
@shubham1g5 shubham1g5 closed this May 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants