Skip to content

Conversation

@Jignesh-dimagi
Copy link
Contributor

Technical Summary

Changes for https://dimagi.atlassian.net/browse/CCCT-1087 already merged in master, now rebasing master into dv/connect_initial

Labels and Review

  • Do we need to enhance the manual QA test coverage ? If yes, the "QA Note" label is set correctly
  • Does the PR introduce any major changes worth communicating ? If yes, the "Release Note" label is set and a "Release Note" is specified in PR description.
  • Risk label is set correctly
  • The set of people pinged as reviewers is appropriate for the level of risk of the change

rahulrajesh21 and others added 30 commits May 4, 2025 18:53
This is not valid for Android 14+, where the concept of dismissing notification is
not supported except for a few use cases:
https://developer.android.com/about/versions/14/behavior-changes-all#non-dismissable-notifications
Jignesh-dimagi and others added 20 commits May 13, 2025 12:15
Improve image downscaling using progressive steps to reduce artifacts.
-updated formStorageTest file with latest Xpath functions
Review for layouts, navigations, views, and some minor strings
@Jignesh-dimagi
Copy link
Contributor Author

Closing this PR as some mistake in merge

@coderabbitai
Copy link

coderabbitai bot commented May 14, 2025

📝 Walkthrough

Walkthrough

This set of changes introduces a new Android foreground service, AudioRecordingService, to manage audio recording in the application. The service is registered in the manifest with the required microphone foreground service permission. The RecordingFragment is refactored to delegate all recording operations to this service, replacing direct MediaRecorder usage. Supporting classes, such as AudioRecordingHelper, are added to handle recorder setup and codec detection. Numerous UI layout files are updated for accessibility, RTL support, and improved view identification, while multiple localization string files are corrected for consistency and accuracy. Several instrumentation tests are updated to reflect new view IDs and improved interaction reliability.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant RecordingFragment
    participant AudioRecordingService
    participant AudioRecordingHelper
    participant NotificationManager

    User->>RecordingFragment: Initiate audio recording
    RecordingFragment->>AudioRecordingService: Bind and start service (with filename)
    AudioRecordingService->>AudioRecordingHelper: setupRecorder(filename)
    AudioRecordingHelper-->>AudioRecordingService: MediaRecorder instance
    AudioRecordingService->>AudioRecordingService: Start recording
    AudioRecordingService->>NotificationManager: Show "Recording in progress" notification
    RecordingFragment-->>User: Update UI (recording started)

    User->>RecordingFragment: Pause/Resume/Stop recording
    RecordingFragment->>AudioRecordingService: pauseRecording()/resumeRecording()/stopRecording()
    AudioRecordingService->>NotificationManager: Update notification status
    AudioRecordingService-->>RecordingFragment: Confirm action
    RecordingFragment-->>User: Update UI (recording state)
Loading

Possibly related PRs

Suggested labels

skip-integration-tests

Suggested reviewers

  • shubham1g5
  • OrangeAndGreen

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.

✨ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

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

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 generate sequence diagram to generate a sequence diagram of the changes in 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: 17

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

191-202: 🛠️ Refactor suggestion

Drawable-end touch handling is still LTR-only and may NPE

You correctly switched the start drawable lookup to getCompoundDrawablesRelative()[0] with a null check, but the end path still uses getCompoundDrawables()[2] without a null check.

  1. RTL builds will not detect touches on the end icon.
  2. If the drawable is unexpectedly null an NPE is thrown.
- if (event.getX() >= (getWidth() - getPaddingRight() - getCompoundDrawables()[2].getBounds().width())) {
+ Drawable end = getCompoundDrawablesRelative()[2];
+ if (end != null &&
+     event.getX() >= (getWidth() - getPaddingRight() - end.getBounds().width())) {
🧹 Nitpick comments (14)
app/res/layout/fragment_connect_delivery_progress.xml (1)

79-79: Avoid hard-coded button text colors—use theme/style
Both MaterialButtons now set android:textColor="@color/blue". For consistency, prefer using your app’s themed color resource (e.g., @color/connect_blue_color) or a style attribute (?attr/colorPrimary) instead of a generic @color/blue.

Also applies to: 90-90

app/res/layout/fragment_connect_delivery_details.xml (1)

206-206: Leverage MaterialButton’s Icon API

You replaced android:drawableRight with android:drawableEnd on a MaterialButton, but the recommended approach is to use app:icon (and app:iconGravity="end") so that app:iconTint is applied correctly. For example:

-    android:drawableEnd="@drawable/ic_connect_arrow_forward_20px"
-    app:iconTint="@color/cc_brand_color"
+    app:icon="@drawable/ic_connect_arrow_forward_20px"
+    app:iconGravity="end"
+    app:iconTint="@color/cc_brand_color"
app/res/layout/view_job_card.xml (2)

73-75: Ensure RTL compatibility for tv_job_time alignment
Using android:gravity="right" works in LTR locales, but for full RTL support prefer android:gravity="end" so alignment flips automatically in RTL languages.


86-91: Ensure RTL compatibility for connect_job_end_date alignment
Similarly, replace android:gravity="right" with android:gravity="end" to respect RTL layout directions without hard-coding “right.”

app/assets/locales/android_translatable_strings.txt (2)

45-45: Consider updating the installation source mention
The message refers to the “Android market,” which is outdated. It’s preferable to reference the “Google Play Store” to reflect current terminology.


804-804: Refine Wi-Fi Direct no-forms message
The phrase “for Submitting; did you mean to Send forms?” uses inconsistent capitalization. Consider normalizing to sentence case and clarifying the action name.

app/instrumentation-tests/src/org/commcare/utils/InstrumentationUtility.kt (1)

29-32: repeatedlyUntil import is unused

ViewActions.repeatedlyUntil is imported but never referenced, leading to an IDE/Lint warning.
Remove the import or, even better, use it in logout() (see next comment).

app/src/org/commcare/views/widgets/AudioRecordingHelper.java (1)

56-81: Codec scanning could be cached

isHeAacEncoderSupported() iterates over all codecs every time setupRecorder() is called.
If multiple recordings are expected in one session, cache the result in a static field to avoid repeated expensive scans.

private static Boolean heAacSupportedCache = null;
...
boolean isHeAacSupported() {
    if (heAacSupportedCache != null) { return heAacSupportedCache; }
    heAacSupportedCache = computeSupport();
    return heAacSupportedCache;
}
app/src/org/commcare/views/widgets/RecordingFragment.java (1)

200-227: Potential memory leak: ServiceConnection kept as anonymous inner instance

audioRecordingServiceConnection holds an implicit reference to the fragment. Because it’s stored in the Service binder it can out-live the fragment if unbindService() is forgotten or fails.
Given you already unbind in onDestroyView(), consider:

  1. Making the connection a static inner class holding a WeakReference to the fragment, or
  2. Nulling the audioRecordingServiceConnection field after unbindAudioRecordingService().

This will guard against edge-cases such as configuration changes while the bind call is still in flight.

app/src/org/commcare/views/widgets/AudioRecordingService.java (5)

19-19: Fix typo in import statement

There is a typo in the import statement: CommCareNoficationManager should be CommCareNotificationManager.

-import org.commcare.CommCareNoficationManager;
+import org.commcare.CommCareNotificationManager;

74-76: Use Intent constants instead of hardcoded strings

Replace hardcoded strings with the corresponding Intent constants for better code maintainability and to avoid potential typos.

Intent activityToLaunch = new Intent(this, DispatchActivity.class);
-activityToLaunch.setAction("android.intent.action.MAIN");
-activityToLaunch.addCategory("android.intent.category.LAUNCHER");
+activityToLaunch.setAction(Intent.ACTION_MAIN);
+activityToLaunch.addCategory(Intent.CATEGORY_LAUNCHER);

84-84: Update notification channel reference after fixing import

After fixing the import for CommCareNotificationManager, make sure to update this reference as well.

-return new NotificationCompat.Builder(this, CommCareNoficationManager.NOTIFICATION_CHANNEL_USER_SESSION_ID)
+return new NotificationCompat.Builder(this, CommCareNotificationManager.NOTIFICATION_CHANNEL_USER_SESSION_ID)

31-36: Consider making fields final where appropriate

Consider making the binder and helper fields final for better code safety and to communicate your intent that these references shouldn't change.

public class AudioRecordingService extends Service {
    private MediaRecorder recorder;
-    private final IBinder binder = new AudioRecorderBinder();
+    private final IBinder binder = new AudioRecorderBinder();
    public static final String RECORDING_FILENAME_EXTRA_KEY = "recording-filename-extra-key";
    private NotificationManager notificationManager;
-    private AudioRecordingHelper audioRecordingHelper = new AudioRecordingHelper();
+    private final AudioRecordingHelper audioRecordingHelper = new AudioRecordingHelper();

24-30: Enhance documentation with exception handling details

The JavaDoc comment should mention that the service might throw exceptions during recording operations and how clients should handle them.

/**
 * A foreground service intended to be bound to the RecordingFragment for managing audio recording
 * operations. Due to its persistent notification, the system treats it with higher importance, reducing the
 * likelihood of interruptions during recordings.
 *
+ * Note: The service methods do not throw exceptions, but clients should handle potential recording
+ * failures by implementing appropriate error handling strategies.
+ *
 * @author avazirna
 **/
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 35a97d0 and 985560b.

📒 Files selected for processing (41)
  • app/AndroidManifest.xml (2 hunks)
  • app/assets/locales/android_translatable_strings.txt (8 hunks)
  • app/instrumentation-tests/src/org/commcare/androidTests/DemoUserTest.kt (2 hunks)
  • app/instrumentation-tests/src/org/commcare/androidTests/LogSubmissionTest.kt (1 hunks)
  • app/instrumentation-tests/src/org/commcare/androidTests/LoginTest.kt (1 hunks)
  • app/instrumentation-tests/src/org/commcare/androidTests/MenuTests.kt (1 hunks)
  • app/instrumentation-tests/src/org/commcare/utils/InstrumentationUtility.kt (4 hunks)
  • app/res/layout-land/home_screen.xml (4 hunks)
  • app/res/layout/activity_connect_messaging.xml (1 hunks)
  • app/res/layout/connect_delivery_item.xml (2 hunks)
  • app/res/layout/connect_delivery_progress_item.xml (3 hunks)
  • app/res/layout/dialog_payment_confirmation.xml (1 hunks)
  • app/res/layout/fragment_channel_consent_bottom_sheet.xml (1 hunks)
  • app/res/layout/fragment_connect_delivery_details.xml (6 hunks)
  • app/res/layout/fragment_connect_delivery_list.xml (3 hunks)
  • app/res/layout/fragment_connect_delivery_progress.xml (2 hunks)
  • app/res/layout/fragment_connect_downloading.xml (1 hunks)
  • app/res/layout/fragment_connect_jobs_list.xml (0 hunks)
  • app/res/layout/fragment_connect_message.xml (2 hunks)
  • app/res/layout/fragment_connect_results_summary_list.xml (2 hunks)
  • app/res/layout/home_screen.xml (4 hunks)
  • app/res/layout/item_channel.xml (2 hunks)
  • app/res/layout/item_chat_right_view.xml (0 hunks)
  • app/res/layout/item_login_connect_home_corrupt_apps.xml (0 hunks)
  • app/res/layout/view_job_card.xml (3 hunks)
  • app/res/layout/view_progress_job_card.xml (2 hunks)
  • app/res/navigation/nav_graph_connect.xml (1 hunks)
  • app/res/values-fr/strings.xml (2 hunks)
  • app/res/values-pt/strings.xml (2 hunks)
  • app/res/values-sw/strings.xml (3 hunks)
  • app/res/values-ti/strings.xml (1 hunks)
  • app/res/values/strings.xml (2 hunks)
  • app/src/org/commcare/utils/FileUtil.java (6 hunks)
  • app/src/org/commcare/views/ConnectPhoneInputView.java (0 hunks)
  • app/src/org/commcare/views/connect/CircleProgressBar.java (0 hunks)
  • app/src/org/commcare/views/connect/ConnectEditText.java (5 hunks)
  • app/src/org/commcare/views/connect/LinearProgressBar.java (4 hunks)
  • app/src/org/commcare/views/widgets/AudioRecordingHelper.java (1 hunks)
  • app/src/org/commcare/views/widgets/AudioRecordingService.java (1 hunks)
  • app/src/org/commcare/views/widgets/RecordingFragment.java (11 hunks)
  • app/unit-tests/src/org/commcare/android/tests/processing/FormStorageTest.java (1 hunks)
💤 Files with no reviewable changes (5)
  • app/res/layout/item_chat_right_view.xml
  • app/res/layout/item_login_connect_home_corrupt_apps.xml
  • app/res/layout/fragment_connect_jobs_list.xml
  • app/src/org/commcare/views/connect/CircleProgressBar.java
  • app/src/org/commcare/views/ConnectPhoneInputView.java
🧰 Additional context used
🧠 Learnings (2)
app/res/layout/activity_connect_messaging.xml (1)
Learnt from: Jignesh-dimagi
PR: dimagi/commcare-android#3093
File: app/res/layout/activity_connect_messaging.xml:0-0
Timestamp: 2025-05-09T13:45:18.661Z
Learning: In Android layouts, use `layout_constraintStart_toStartOf` and `layout_constraintEnd_toEndOf` instead of the deprecated `layout_constraintLeft_toLeftOf` and `layout_constraintRight_toRightOf` to ensure proper RTL (Right-to-Left) layout support.
app/res/navigation/nav_graph_connect.xml (1)
Learnt from: Jignesh-dimagi
PR: dimagi/commcare-android#3093
File: app/res/navigation/nav_graph_connect_messaging.xml:41-45
Timestamp: 2025-05-09T10:57:41.073Z
Learning: In the CommCare Android codebase, the navigation graph for Connect messaging (`nav_graph_connect_messaging.xml`) intentionally uses `channel_id` as the argument name in the connectMessageFragment, despite using `channelId` in other parts of the same navigation graph. This naming difference is by design in the refactored code.
🔇 Additional comments (64)
app/res/layout/fragment_channel_consent_bottom_sheet.xml (1)

27-27: Good accessibility improvement.

Adding android:importantForAccessibility="no" to this decorative icon ensures screen readers won't focus on it, improving the experience for users with accessibility needs. This complements the existing android:contentDescription="@null" attribute.

app/res/layout/dialog_payment_confirmation.xml (2)

12-12: Good layout optimization.

Changing the layout height from "match_parent" to "wrap_content" makes the dialog size appropriate to its content rather than filling the entire screen, which improves the user experience for this dialog.


20-21: Good accessibility improvement.

Adding both android:contentDescription="@null" and android:importantForAccessibility="no" to this status icon properly excludes it from screen reader focus, improving accessibility for users with visual impairments.

app/src/org/commcare/views/connect/LinearProgressBar.java (4)

6-7: LGTM - Required imports for gradient functionality.

These imports support the new gradient coloring feature being added to the progress bar.

Also applies to: 9-9


29-29: LGTM - Changed variable type for direct color access.

Modified from a resource ID to a direct color integer for more efficient rendering.


48-50: LGTM - Improved color initialization.

Using ContextCompat.getColor() is the recommended way to retrieve colors from resources while maintaining compatibility across API levels.


74-82: Well-implemented gradient support.

The gradient implementation correctly:

  1. Checks for gradient mode and sufficient colors
  2. Creates a linear gradient shader in the correct direction
  3. Applies the shader to the paint only when needed
  4. Clears the shader when not in gradient mode

This provides a nice visual enhancement option for progress bars.

app/res/layout/item_channel.xml (3)

37-39: Good accessibility improvement.

Adding android:contentDescription="@null" and android:importantForAccessibility="no" to this decorative icon ensures screen readers won't focus on it, improving accessibility.


44-45: Improved layout constraints for the channel name.

Changing from a fixed width to a constraint-based width (0dp) with proper end constraint and margin creates a more responsive layout that adapts to different screen sizes and text lengths.

Also applies to: 52-54


62-63: Refined alignment for channel description.

The adjusted margins and constraints properly position the description text relative to the channel name, creating a more consistent and visually pleasing layout.

Also applies to: 66-67, 69-70

app/instrumentation-tests/src/org/commcare/androidTests/MenuTests.kt (1)

40-40: Good naming consistency improvement

Changing "Wifi Direct" to "Wi-Fi Direct" properly follows standard capitalization and hyphenation conventions for Wi-Fi terminology, improving consistency throughout the application.

app/res/layout/fragment_connect_downloading.xml (1)

23-24: Good accessibility improvement

Adding android:contentDescription="@null" and android:importantForAccessibility="no" correctly marks this decorative image as non-relevant for screen readers, improving the accessibility experience for users with visual impairments.

app/instrumentation-tests/src/org/commcare/androidTests/LogSubmissionTest.kt (1)

64-65: Good UI test reliability improvement

Adding an explicit keyboard dismissal step immediately after clicking "Report Problem" helps prevent potential UI interference issues during test execution, improving test stability and reliability.

app/instrumentation-tests/src/org/commcare/androidTests/DemoUserTest.kt (4)

24-25: Good UI structure alignment

Updating from R.id.home_gridview_buttons to R.id.nsv_home_screen correctly targets the new nested scroll view component for swipe operations, aligning with UI layout changes.


29-30: Good UI structure alignment

Updating from R.id.home_gridview_buttons to R.id.nsv_home_screen correctly targets the new nested scroll view component for swipe operations, aligning with UI layout changes.


57-58: Good UI structure alignment

Updating from R.id.home_gridview_buttons to R.id.nsv_home_screen correctly targets the new nested scroll view component for swipe operations, aligning with UI layout changes.


62-63: Good UI structure alignment

Updating from R.id.home_gridview_buttons to R.id.nsv_home_screen correctly targets the new nested scroll view component for swipe operations, aligning with UI layout changes.

app/unit-tests/src/org/commcare/android/tests/processing/FormStorageTest.java (1)

393-395: Added new XPath function classes for form serialization compatibility.

These new classes for geospatial calculations (XPathClosestPointOnPolygonFunc and XPathIsPointInsidePolygonFunc) are properly added to the compatibility list with appropriate version annotation.

app/instrumentation-tests/src/org/commcare/androidTests/LoginTest.kt (1)

74-74: Updated swipe target for improved test reliability.

The test now correctly targets the NestedScrollView (nsv_home_screen) for the swipe action rather than the grid buttons. This change aligns with similar updates in other instrumentation tests.

app/res/values-ti/strings.xml (1)

30-30: Fixed extraneous quotation mark in Tigrinya string resource.

Removed the unnecessary trailing double quote from the back_to_manager string value, improving string resource consistency.

app/res/navigation/nav_graph_connect.xml (1)

62-62: Fixed typo in fragment label.

Corrected the spelling of "progress" in the fragment label attribute.

app/res/layout/view_progress_job_card.xml (2)

7-7: Change CardView height to wrap_content
Adjusting the root CardView’s layout_height to wrap_content ensures the card sizes dynamically to its content, preventing unnecessary empty space.


73-75: Align tv_job_time using proper constraints
Adding layout_marginStart="5dp", android:gravity="end", and app:layout_constraintStart_toEndOf="@+id/tv_view_more" right-aligns the time text between the “view more” label and parent. Verify that when tv_view_more is GONE the constraint still resolves as expected (it should anchor to the invisible view’s virtual bounds).

app/res/layout/connect_delivery_item.xml (2)

23-23: Exclude decorative icon from accessibility focus
Marking imgDeliveryStatus with android:importantForAccessibility="no" correctly removes a non-informative element from the accessibility traversal, improving screen-reader experience.


70-71: Add missing start/top constraints to status TextView
Introducing app:layout_constraintStart_toStartOf="parent" and app:layout_constraintTop_toTopOf="parent" for delivery_item_status resolves the previous missing-constraint warnings and guarantees consistent positioning.

app/res/layout/activity_connect_messaging.xml (1)

17-18: Use RTL-aware constraints instead of deprecated left/right
Replacing layout_constraintLeft_toLeftOf/Right_toRightOf with constraintStart_toStartOf and constraintEnd_toEndOf improves right-to-left language support and aligns with best practices.

app/res/layout/fragment_connect_results_summary_list.xml (2)

30-30: Enable weight-based sizing for first CardView
Switching layout_width to 0dp allows the layout_weight="1" setting in the horizontal LinearLayout to distribute space evenly.


84-84: Enable weight-based sizing for second CardView
Similarly, setting layout_width to 0dp ensures both CardViews share available horizontal space per their weights.

app/res/layout/fragment_connect_delivery_details.xml (2)

58-58: Consistent Accessibility Exclusion for Decorative Icons

You’ve correctly marked each decorative ImageView with android:importantForAccessibility="no" (and contentDescription="@null") to keep screen readers from announcing them. This is the right approach for purely decorative icons.

Also applies to: 82-82, 106-106, 128-128


170-170: Use RTL‐aware Margin for Spacing

Adding android:layout_marginEnd="2dp" ensures proper spacing before the download button in both LTR and RTL layouts. Good use of marginEnd over the deprecated marginRight.

app/res/layout/fragment_connect_message.xml (2)

44-44: Localize the Hint Text

Replacing the hardcoded hint with @string/connect_send_message_hint ensures this UI text is translatable. Great catch.


67-67: Add Content Description for Accessibility

Adding android:contentDescription="@string/connect_send_message_btn_desc" makes the send button accessible to screen readers. Perfect improvement.

app/res/values-pt/strings.xml (1)

42-42: Fix Unmatched Quotes in back_to_manager

The extraneous quotes have been removed, correcting the resource. This aligns with other localization fixes.

app/res/layout/connect_delivery_progress_item.xml (3)

46-46: Enable Weight Distribution by Setting Width to 0dp

Changing android:layout_width to 0dp is necessary when using layout_weight so both child LinearLayouts share space correctly.


102-103: Apply Weight and Wrap Content for Second Section

You’ve set android:layout_width="0dp" and android:layout_height="wrap_content" alongside layout_weight="1", ensuring proportional width and correct height. Well done.


122-122: Nested Layout Height Adjustment

Switching the nested vertical LinearLayout from match_parent to wrap_content prevents it from unnecessarily filling vertical space.

app/res/values-sw/strings.xml (1)

42-42: Corrected back_to_manager Translation

Removed extraneous quotes to match other locales. Good alignment with default and Portuguese files.

app/res/layout/view_job_card.xml (1)

42-42: Adjust tv_view_more width for correct content sizing
Changing the tv_view_more width from 0dp (match-constraints) to wrap_content ensures the label only occupies the space it needs, preventing it from stretching across the row and enabling adjacent views to be positioned relative to its end.

app/res/layout-land/home_screen.xml (4)

6-6: Expand root layout to full width
Updating the LinearLayout's layout_width to match_parent ensures the landscape layout spans the entire screen, improving consistency and using the available horizontal space.


85-85: Add ID for scrollable container
Assigning @+id/nsv_home_screen to the NestedScrollView standardizes the scrollable view reference, aligning with instrumentation tests and facilitating UI interactions.


99-99: Hide connect message CardView by default
Setting android:visibility="gone" on cvConnectMessage matches the portrait layout behavior and prevents this alert from appearing until explicitly shown.


134-134: Hide included job card layout by default
Applying android:visibility="gone" on the view_progress_job_card include ensures this card remains hidden on initial render, consistent with the updated UI flow.

app/res/values-fr/strings.xml (2)

203-203: New completion message is clear
The connect_learn_finished string is well-phrased, correctly escaped, and uses French typographic rules for colons.


205-205: New failure notification is clear
The connect_learn_failed string appropriately informs users of a failed assessment and includes score details.

app/assets/locales/android_translatable_strings.txt (6)

83-83: Add Wi-Fi Direct menu option
Introducing home.menu.wifi.direct aligns with the UI update to support Wi-Fi Direct functionality and matches the new nsv_home_screen interactions.


123-123: Handle missing sync URL gracefully
The sync.fail.empty.url string clearly communicates that the Sync URL isn’t set, guiding users to contact support.


413-413: Clarify no active connections action
The notification.sync.connections.action string gives actionable guidance on enabling network connectivity.


472-474: Add audio recording notification strings
The new keys (recording.notification.title, .in.progress, .paused) back the AudioRecordingService notifications, providing clear UX feedback during recording operations.


524-525: Handle empty login URL errors
Adding login.attempt.fail.empty.url.title and .detail improves error reporting when the login endpoint is not configured.


806-806: Clarify discovery failure guidance
The wifi.direct.discovery.failed.generic string succinctly instructs users to toggle Wi-Fi; this is clear and appropriate.

app/res/layout/home_screen.xml (4)

85-85: Assign ID to NestedScrollView
Adding @+id/nsv_home_screen to the NestedScrollView enables reliable UI interactions and aligns with updated instrumentation tests.


99-102: Default hide connect message and adjust positioning
Hiding cvConnectMessage by default, applying translationY instead of negative margins, and using marginStart/marginEnd makes the alert hidden initially and positions it correctly when shown.


134-134: Hide progress job card by default
Setting android:visibility="gone" on the included view_progress_job_card prevents it from appearing until explicitly made visible, matching the updated UI flow.


145-145: Limit RecyclerView height to content
Changing the RecyclerView's height from match_parent to wrap_content allows it to size to its content and improves nested scrolling behavior.

app/AndroidManifest.xml (2)

39-39: Appropriate permission added for audio recording foreground service

The addition of the FOREGROUND_SERVICE_MICROPHONE permission is correct and necessary when implementing a foreground service that uses the microphone.


339-345: Properly configured audio recording foreground service

The new AudioRecordingService is correctly declared with appropriate attributes:

  • enabled="true" ensures the service can be started
  • exported="false" prevents access from external applications
  • stopWithTask="true" ensures proper cleanup when the app is terminated
  • foregroundServiceType="microphone" correctly categorizes this as a microphone service, which aligns with Android's permission model

This follows best practices for implementing recording functionality using a foreground service.

app/res/values/strings.xml (2)

53-53: Fixed string resource by removing extraneous quotes

Corrected the back_to_manager string resource.


551-552: Added message input accessibility strings

Added appropriate strings for messaging input field hint and send button accessibility description, which improves the user experience and accessibility of the messaging interface.

app/res/layout/fragment_connect_delivery_list.xml (3)

32-32: Improved view ID naming to match view type

Renamed ID from llFilterAll to cvFilterAll to correctly reflect that it's a CardView and not a LinearLayout. All constraint references to this ID are also properly updated.

Also applies to: 77-77


36-37: Enhanced accessibility with proper interactive attributes

Added clickable and focusable attributes to ensure the CardView properly handles user interaction. The null content description helps screen readers skip decorative elements.

Also applies to: 40-40


51-51: Fixed incorrect padding unit

Changed padding from 16sp to 16dp. This is an important fix since sp should only be used for text sizes, while dp is the correct unit for layout dimensions.

app/src/org/commcare/utils/FileUtil.java (1)

772-782: Updated bitmap scaling to use progressive downscaling

The existing getBitmapScaledByMaxDimen method now uses the new step-wise approach instead of direct scaling, which will provide better quality results.

app/instrumentation-tests/src/org/commcare/utils/InstrumentationUtility.kt (1)

317-320: enterText no longer clears focus before replacing text

The new chain (click(), replaceText(text), closeSoftKeyboard()) is concise, but in crowded forms an EditText might already contain the cursor elsewhere. Consider keeping an explicit clearText() (or ensure replaceText suffices in all test scenarios) to avoid residual chars when the field already has content.

app/src/org/commcare/views/widgets/RecordingFragment.java (1)

253-257: resumeRecording() before stopRecording() may throw when service has already stopped

audioRecordingService.resumeRecording() is called unconditionally if inPausedState, but if the service has been killed (e.g. by system under memory pressure) this will NPE.
Add a null/active check:

if (inPausedState && audioRecordingService != null && audioRecordingService.isRecorderActive()) {
    audioRecordingService.resumeRecording();
}

<string name="save_all_answers">Salvar Formulário</string>
<string name="save_data_message">Por favor, salvar Formulário para manter as respostas.</string>
<string name="save_enter_data_description">Você está no fim de \%s\"."</string>
<string name="save_enter_data_description">Você está no fim de "\%s\".</string>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incorrect Escaping of Quotes and Placeholder

The string uses backslashes and an unindexed %s. In XML element text you don’t need backslashes, and best practice is to use indexed placeholders and &quot; for quotes. For example:

-    <string name="save_enter_data_description">Você está no fim de "\%s\".</string>
+    <string name="save_enter_data_description">Você está no fim de &quot;%1$s&quot;.</string>
📝 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
<string name="save_enter_data_description">Você está no fim de "\%s\".</string>
<string name="save_enter_data_description">Você está no fim de &quot;%1$s&quot;.</string>
🤖 Prompt for AI Agents (early access)
In app/res/values-pt/strings.xml at line 191, remove the backslashes before the
quotes and replace the quotes with &quot; entities. Also, change the %s
placeholder to an indexed format like %1$s for best practice. The corrected
string should use &quot; for quotes and %1$s for the placeholder without any
backslashes.

<string name="capture_video">Rekodi Video</string>
<string name="change_language">Badilisha Lugha</string>
<string name="clearanswer_confirm">Ondoa jibu kwa \%s\"?"</string>
<string name="clearanswer_confirm">Ondoa jibu kwa \"%s\"?</string>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Mis-escaped Quotes in clearanswer_confirm

Backslashes in the element text will appear literally. Use &quot; and an indexed placeholder instead. For example:

-    <string name="clearanswer_confirm">Ondoa jibu kwa \"%s\"?</string>
+    <string name="clearanswer_confirm">Ondoa jibu kwa &quot;%1$s&quot;?</string>
📝 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
<string name="clearanswer_confirm">Ondoa jibu kwa \"%s\"?</string>
<string name="clearanswer_confirm">Ondoa jibu kwa &quot;%1$s&quot;?</string>
🤖 Prompt for AI Agents (early access)
In app/res/values-sw/strings.xml at line 102, the string clearanswer_confirm
uses backslashes to escape quotes, which will render literally. Replace the
backslash-escaped quotes with &quot; entities and use an indexed placeholder for
the string substitution. For example, change the text to use &quot;%1$s&quot;
instead of \"%s\" to properly escape quotes and support formatting.

<string name="enter_data">Jaza Fomu tupu</string>
<string name="enter_data_button">Jaza Fomu tupu</string>
<string name="enter_data_description">Uko mwanzoni mwa \%s\". Telezesha kidole skrini kama inavyoonyeshwa hapa chini ili kuanza."</string>
<string name="enter_data_description">Uko mwanzoni mwa \"%s\". Telezesha kidole skrini kama inavyoonyeshwa hapa chini ili kuanza.</string>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Mis-escaped Quotes in enter_data_description

Same issue here with backslashes. Use entity references and indexed placeholders:

-    <string name="enter_data_description">Uko mwanzoni mwa \"%s\". Telezesha kidole skrini kama inavyoonyeshwa hapa chini ili kuanza.</string>
+    <string name="enter_data_description">Uko mwanzoni mwa &quot;%1$s&quot;. Telezesha kidole skrini kama inavyoonyeshwa hapa chini ili kuanza.</string>
🤖 Prompt for AI Agents (early access)
In app/res/values-sw/strings.xml at line 126, the string value for
enter_data_description incorrectly uses backslashes to escape quotes. Replace
the backslashes with proper XML entity references for quotes (e.g., &quot;) and
use indexed placeholders (e.g., %1$s) instead of unescaped %s to ensure correct
formatting and escaping in the string resource.

<string name="download">Obtenez celle sélectionnée</string>
<string name="download_forms_result">Resultat du téléchargement</string>
<string name="downloading_data">Connection au serveur…</string>
<string name="downloading_data">Connexion au serveur…</string>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Verify translation accuracy for downloading_data
The string Connexion au serveur… reads as “Connecting to the server…” rather than “Downloading data…”. Please confirm that this matches the intended English meaning; if it should indicate downloading, consider “Téléchargement des données…”.

🤖 Prompt for AI Agents (early access)
In app/res/values-fr/strings.xml at line 44, the French translation for
"downloading_data" currently reads "Connexion au serveur…", which means
"Connecting to the server…". Verify if the intended meaning is "Downloading
data…". If so, update the string to "Téléchargement des données…" to accurately
reflect the English source.

wifi.direct.change.state.title=Send, Receive, Submit
wifi.direct.change.state.text=Do you want to send, receive, or submit forms?
wifi.direct.wifi.direct.off=WiFi Direct is Off - Please turn it on to use this feature
wifi.direct.wifi.direct.off=Wi-Fi Direct is Off - Please turn it on to use this feature
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Review inconsistent key naming for Wi-Fi Direct
The key wifi.direct.wifi.direct.off redundantly repeats wifi.direct. Consider renaming to wifi.direct.off or similar to maintain a consistent key hierarchy.

🤖 Prompt for AI Agents (early access)
In app/assets/locales/android_translatable_strings.txt at line 792, the key name
wifi.direct.wifi.direct.off redundantly repeats wifi.direct. Rename the key to
wifi.direct.off to maintain a consistent and concise key hierarchy for Wi-Fi
Direct related strings.

Comment on lines +469 to +484
@Override
public void onDestroyView() {
super.onDestroyView();
unbindAudioRecordingService();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
unregisterAudioRecordingConfigurationChangeCallback();
}
}

private void unbindAudioRecordingService() {
if (audioRecordingServiceBounded) {
requireActivity().unbindService(audioRecordingServiceConnection);
requireActivity()
.stopService(new Intent(requireActivity(), AudioRecordingService.class));
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ensure service stopped even when recording never started

unbindAudioRecordingService() only executes when audioRecordingServiceBounded is true.
If the user cancels the dialog before onServiceConnected fires the service remains in the foreground.
Consider also calling requireActivity().stopService(...) outside the if to guarantee cleanup.

🤖 Prompt for AI Agents (early access)
In app/src/org/commcare/views/widgets/RecordingFragment.java around lines 469 to
484, the current unbindAudioRecordingService method only stops the
AudioRecordingService if audioRecordingServiceBounded is true, which can leave
the service running if the user cancels before onServiceConnected. To fix this,
move the requireActivity().stopService(...) call outside and after the if block
so the service is always stopped regardless of the binding state, ensuring
proper cleanup.

Comment on lines +129 to +134
@RequiresApi(api = Build.VERSION_CODES.N)
public void resumeRecording() {
recorder.resume();
notificationManager.notify(RecordingFragment.RECORDING_NOTIFICATION_ID,
createNotification(true));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check before resuming recording

Similar to pauseRecording, the resumeRecording method should check if the recorder is null before attempting to resume it.

@RequiresApi(api = Build.VERSION_CODES.N)
public void resumeRecording() {
+    if (recorder == null) {
+        return;
+    }
    recorder.resume();
    notificationManager.notify(RecordingFragment.RECORDING_NOTIFICATION_ID,
            createNotification(true));
}
📝 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
@RequiresApi(api = Build.VERSION_CODES.N)
public void resumeRecording() {
recorder.resume();
notificationManager.notify(RecordingFragment.RECORDING_NOTIFICATION_ID,
createNotification(true));
}
@RequiresApi(api = Build.VERSION_CODES.N)
public void resumeRecording() {
if (recorder == null) {
return;
}
recorder.resume();
notificationManager.notify(RecordingFragment.RECORDING_NOTIFICATION_ID,
createNotification(true));
}
🤖 Prompt for AI Agents (early access)
In app/src/org/commcare/views/widgets/AudioRecordingService.java around lines
129 to 134, the resumeRecording method calls recorder.resume() without checking
if recorder is null. Add a null check for recorder before calling resume() to
prevent potential NullPointerException, similar to the pauseRecording method.

Comment on lines +50 to +58
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String fileName = intent.getExtras().getString(RECORDING_FILENAME_EXTRA_KEY);
if (recorder == null) {
recorder = audioRecordingHelper.setupRecorder(fileName);
}
recorder.start();
return START_NOT_STICKY;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check for intent extras to prevent NullPointerException

The onStartCommand method doesn't check if intent.getExtras() is null before accessing it, which could lead to a NullPointerException if the service is started without extras.

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
-    String fileName = intent.getExtras().getString(RECORDING_FILENAME_EXTRA_KEY);
+    String fileName = null;
+    if (intent != null && intent.getExtras() != null) {
+        fileName = intent.getExtras().getString(RECORDING_FILENAME_EXTRA_KEY);
+    }
+    
+    if (fileName == null) {
+        stopSelf();
+        return START_NOT_STICKY;
+    }
+    
    if (recorder == null) {
        recorder = audioRecordingHelper.setupRecorder(fileName);
    }
    recorder.start();
    return START_NOT_STICKY;
}
🤖 Prompt for AI Agents (early access)
In app/src/org/commcare/views/widgets/AudioRecordingService.java around lines 50
to 58, add a null check for intent.getExtras() before accessing
getString(RECORDING_FILENAME_EXTRA_KEY) to prevent a NullPointerException.
Modify the code to first verify that intent and intent.getExtras() are not null,
and only then retrieve the fileName from the extras. Handle the case where
extras are null appropriately, such as by logging an error or returning early.

Comment on lines +122 to +127
@RequiresApi(api = Build.VERSION_CODES.N)
public void pauseRecording() {
recorder.pause();
notificationManager.notify(RecordingFragment.RECORDING_NOTIFICATION_ID,
createNotification(false));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check before pausing recording

The pauseRecording method should check if the recorder is null or not active before attempting to pause it.

@RequiresApi(api = Build.VERSION_CODES.N)
public void pauseRecording() {
+    if (recorder == null) {
+        return;
+    }
    recorder.pause();
    notificationManager.notify(RecordingFragment.RECORDING_NOTIFICATION_ID,
            createNotification(false));
}
📝 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
@RequiresApi(api = Build.VERSION_CODES.N)
public void pauseRecording() {
recorder.pause();
notificationManager.notify(RecordingFragment.RECORDING_NOTIFICATION_ID,
createNotification(false));
}
@RequiresApi(api = Build.VERSION_CODES.N)
public void pauseRecording() {
if (recorder == null) {
return;
}
recorder.pause();
notificationManager.notify(RecordingFragment.RECORDING_NOTIFICATION_ID,
createNotification(false));
}
🤖 Prompt for AI Agents (early access)
In app/src/org/commcare/views/widgets/AudioRecordingService.java around lines
122 to 127, the pauseRecording method calls recorder.pause() without checking if
recorder is null or currently active. Add a null check to ensure recorder is not
null and verify it is in a state that can be paused before calling pause() to
prevent potential NullPointerException or illegal state errors.

Comment on lines +136 to +138
public void stopRecording() {
recorder.stop();
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check before stopping recording

The stopRecording method should check if the recorder is null before attempting to stop it.

public void stopRecording() {
+    if (recorder == null) {
+        return;
+    }
    recorder.stop();
}
📝 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
public void stopRecording() {
recorder.stop();
}
public void stopRecording() {
if (recorder == null) {
return;
}
recorder.stop();
}
🤖 Prompt for AI Agents (early access)
In app/src/org/commcare/views/widgets/AudioRecordingService.java around lines
136 to 138, the stopRecording method calls recorder.stop() without checking if
recorder is null. Add a null check to ensure recorder is not null before calling
stop() to prevent potential NullPointerException.

@shubham1g5
Copy link
Contributor

Closing this PR as some mistake in merge

@Jignesh-dimagi Can you explain, this seems alright to me.

@Jignesh-dimagi
Copy link
Contributor Author

@shubham1g5 Please use #3105

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.

6 participants