CCCT-2439 Launch Connect Apps Without Showing The Login Screen#3753
CCCT-2439 Launch Connect Apps Without Showing The Login Screen#3753conroy-ricketts wants to merge 22 commits into
Conversation
[AI] Added ConnectAppLauncher to seat and authenticate Connect apps headlessly and wired ConnectJobsListsFragment to route success to Home and failures to retry/recovery without showing LoginActivity. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[AI] Addressed code review by hardening silent-launch crash paths (defensive credential resolution and a main-thread-safe progress sink), centralizing home-intent construction, routing outcomes through an exhaustive testable dispatcher, and expanding unit coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[AI] Matched the silent-launch progress dialog to the legacy LoginActivity dialog (spinner then bar, reused Localization strings) and replaced the custom retry dialog with a fallback to the legacy launch, removing the six new strings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[AI] Showed the seating message during the Seating phase and routed silent-launch success back through DispatchActivity so backing out of Home returns to the Connect jobs list. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[AI] Launched Home in place on top of the Connect screen instead of routing silent-launch success through DispatchActivity, removing the blank-screen flash while keeping back-from-Home returning to the Connect list. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[AI] Cleaned up the silent-launch code: renamed launchHome, dropped redundant comments and unused imports, and applied minor formatting, with no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[AI] Tightened the silent-launch KDoc comments to one-liners and removed the doc comments from the test classes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[AI] Dropped the "silent" qualifier from the launch types, members, and comments, renaming SilentLaunch* to Launch* across the Connect launch code and tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[AI] Removed the single-flight launch guard and AlreadyLaunching outcome since the modal progress dialog already prevents re-entry, dropping the related router branch, action, and tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[AI] Inlined the DispatchActivity post-login intent handling and deleted the unused PostLoginRouter and PostLoginDestination classes along with their test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[AI] Documented the app-seating and Connect-launch additions (phases 2 and 3) in the login-engine doc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Suggested Review Order
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3753 +/- ##
============================================
+ Coverage 25.80% 25.86% +0.05%
- Complexity 4364 4386 +22
============================================
Files 947 949 +2
Lines 56993 57125 +132
Branches 6782 6804 +22
============================================
+ Hits 14709 14776 +67
- Misses 40455 40521 +66
+ Partials 1829 1828 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@coderabbitai full review |
✅ Action performedFull review finished. |
|
Need an answer fast? Review this PR in Change Stack to ask focused questions about the PR or a changed range. 📝 WalkthroughWalkthroughThis PR refactors the Connect app launch flow by introducing Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes The PR spans multiple architectural layers (new launcher/router abstractions, activity simplification, fragment integration) with dense logic in the launcher and fragment outcome handling. Multiple files are affected across activities, fragments, and tests. The removal of old abstractions requires tracing usage across multiple locations. The new sealed-type outcome model and dispatch pattern introduce new concepts that require careful validation against the test coverage. Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (4)
app/src/org/commcare/fragments/connect/ConnectJobsListsFragment.java (1)
79-80: ⚡ Quick winRemove the newly added inline comment in Java source.
This addition conflicts with the repository rule for Java/Kotlin source comments.
As per coding guidelines
**/*.{kt,java}: "Do not add any in-code comments unless explicitly requested".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/org/commcare/fragments/connect/ConnectJobsListsFragment.java` around lines 79 - 80, Remove the newly added inline comment attached to the LAUNCH_DIALOG_TASK_ID constant in ConnectJobsListsFragment (the line starting with "Negative so it can't collide..."); leave only the field declaration "private static final int LAUNCH_DIALOG_TASK_ID = -10;" in the ConnectJobsListsFragment class to comply with the repository rule banning in-code comments in Java/Kotlin files.app/src/org/commcare/connect/LaunchOutcomeRouter.kt (1)
3-3: ⚡ Quick winRemove newly added in-code comments in this Kotlin file.
The added comments conflict with the repo rule for Java/Kotlin source files.
As per coding guidelines
**/*.{kt,java}: "Do not add any in-code comments unless explicitly requested".Also applies to: 18-18
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/org/commcare/connect/LaunchOutcomeRouter.kt` at line 3, Remove the newly added in-code comments in LaunchOutcomeRouter.kt (e.g., the comment mentioning "Actions a caller runs for a [LaunchOutcome]; a seam so the mapping is testable without a fragment" and the other comment at the same file) so the file complies with the repository rule forbidding in-code comments in .kt/.java files; locate any occurrences referencing LaunchOutcome or similar inline comments and delete them (if explanation is required, move it to external documentation or a separate README rather than leaving inline comments).app/src/org/commcare/connect/ConnectAppLauncher.kt (1)
23-26: ⚡ Quick winRemove newly added in-code comments in Kotlin segments.
These added KDoc/inline comments violate the repo rule for Java/Kotlin files.
As per coding guidelines
**/*.{kt,java}: "Do not add any in-code comments unless explicitly requested".Also applies to: 41-44, 60-60
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/org/commcare/connect/ConnectAppLauncher.kt` around lines 23 - 26, Remove the newly added KDoc/inline comments in the Kotlin file: delete the comment block describing the "Terminal result of a Connect launch" and any other in-code KDoc/comments around the sealed outcome types (e.g., the comment referencing [Retryable]) as well as the other comment blocks at the other ranges; leave the Kotlin declarations (such as the sealed result types and the Retryable symbol) untouched and ensure no in-code comments remain in this file per the Java/Kotlin guideline.app/unit-tests/src/org/commcare/connect/ConnectAppLauncherTest.kt (1)
60-60: 💤 Low valueRemove unused stub.
The
reportCccAppFailedAutoLoginstub is never called or verified in any test method. Per coding guidelines, unused code should be cleaned up.♻️ Proposed cleanup
mockkStatic(FirebaseAnalyticsUtil::class) every { FirebaseAnalyticsUtil.reportCccAppLaunch(any(), any()) } returns Unit - every { FirebaseAnalyticsUtil.reportCccAppFailedAutoLogin(any()) } returns Unit }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/unit-tests/src/org/commcare/connect/ConnectAppLauncherTest.kt` at line 60, Remove the unused mock/stub for FirebaseAnalyticsUtil.reportCccAppFailedAutoLogin in ConnectAppLauncherTest (the line using every { FirebaseAnalyticsUtil.reportCccAppFailedAutoLogin(any()) } returns Unit) since it is never invoked or asserted; simply delete that line to clean up the test setup and ensure no other tests rely on that stub.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/org/commcare/activities/DispatchActivity.java`:
- Around line 511-516: Ensure every RESULT_OK exit from LoginActivity supplies
an Intent with the login-result extras DispatchActivity expects: always call
setResult(RESULT_OK, intent) where intent includes LoginActivity.LOGIN_MODE
(serializable LoginMode), LoginActivity.MANUAL_SWITCH_TO_PW_MODE (boolean),
DispatchActivity.PERSONALID_MANAGED_LOGIN (boolean) and
DispatchActivity.CONNECT_MANAGED_LOGIN (boolean); specifically update the
shouldFinish() path, the RecoveryMeasuresHelper.recoveryMeasuresPending()
return, and handleConnectButtonPress() to populate those keys (or centralize
into a helper that builds the result Intent) so DispatchActivity’s reads of
lastLoginMode, userManuallyEnteredPasswordMode, personalIdManagedLogin and
connectManagedLogin never see a null/missing Intent.
In `@app/src/org/commcare/connect/ConnectAppLauncher.kt`:
- Around line 89-91: The code in ConnectAppLauncher.kt uses locale-sensitive
lowercase() when normalizing the login identifier; update the connectUsername
usage so username = connectUsername(context)?.trim()?.lowercase(Locale.ROOT) (or
Locale.ENGLISH) instead of lowercase(), and add the necessary import for
java.util.Locale; keep the existing null/empty check using
username.isNullOrEmpty() unchanged.
In `@app/src/org/commcare/fragments/connect/ConnectJobsListsFragment.java`:
- Around line 299-317: The dialog is being shown with launchDialog.showNow(...)
which can throw IllegalStateException if the fragment manager state is already
saved; update showLaunchDialog(...) to first check
getChildFragmentManager().isStateSaved() and if true, return early (do this
check before calling dismissLaunchDialog() so you don't null out launchDialog
and cause updateLaunchProgress() to NPE), otherwise call dismissLaunchDialog()
and use launchDialog.show(...) (not showNow) to safely enqueue the dialog;
reference the methods/fields showLaunchDialog, dismissLaunchDialog,
launchDialog, updateLaunchProgress, ConnectAppLauncher callbacks,
showingSyncDialog, LAUNCH_DIALOG_TASK_ID and LAUNCH_DIALOG_TAG when making the
change.
In `@app/unit-tests/src/org/commcare/connect/ConnectAppLauncherTest.kt`:
- Around line 33-37: Reset the mutable capture lists before each test to avoid
cross-test pollution: in the setUp() method clear the mutable lists `seatedApps`
and `capturedRequests` (and if present reinitialize `seatResult`, `loginAnswer`,
and `username` to their defaults) so each test starts with fresh state; locate
the fields `seatedApps`, `capturedRequests`, `seatResult`, `loginAnswer`, and
`username` in ConnectAppLauncherTest and add calls to clear or reassign them in
the test class's `setUp()` method.
---
Nitpick comments:
In `@app/src/org/commcare/connect/ConnectAppLauncher.kt`:
- Around line 23-26: Remove the newly added KDoc/inline comments in the Kotlin
file: delete the comment block describing the "Terminal result of a Connect
launch" and any other in-code KDoc/comments around the sealed outcome types
(e.g., the comment referencing [Retryable]) as well as the other comment blocks
at the other ranges; leave the Kotlin declarations (such as the sealed result
types and the Retryable symbol) untouched and ensure no in-code comments remain
in this file per the Java/Kotlin guideline.
In `@app/src/org/commcare/connect/LaunchOutcomeRouter.kt`:
- Line 3: Remove the newly added in-code comments in LaunchOutcomeRouter.kt
(e.g., the comment mentioning "Actions a caller runs for a [LaunchOutcome]; a
seam so the mapping is testable without a fragment" and the other comment at the
same file) so the file complies with the repository rule forbidding in-code
comments in .kt/.java files; locate any occurrences referencing LaunchOutcome or
similar inline comments and delete them (if explanation is required, move it to
external documentation or a separate README rather than leaving inline
comments).
In `@app/src/org/commcare/fragments/connect/ConnectJobsListsFragment.java`:
- Around line 79-80: Remove the newly added inline comment attached to the
LAUNCH_DIALOG_TASK_ID constant in ConnectJobsListsFragment (the line starting
with "Negative so it can't collide..."); leave only the field declaration
"private static final int LAUNCH_DIALOG_TASK_ID = -10;" in the
ConnectJobsListsFragment class to comply with the repository rule banning
in-code comments in Java/Kotlin files.
In `@app/unit-tests/src/org/commcare/connect/ConnectAppLauncherTest.kt`:
- Line 60: Remove the unused mock/stub for
FirebaseAnalyticsUtil.reportCccAppFailedAutoLogin in ConnectAppLauncherTest (the
line using every { FirebaseAnalyticsUtil.reportCccAppFailedAutoLogin(any()) }
returns Unit) since it is never invoked or asserted; simply delete that line to
clean up the test setup and ensure no other tests rely on that stub.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5a270819-7f56-4402-a1cb-1ebf071daae2
📒 Files selected for processing (12)
RELEASES.mdapp/src/org/commcare/activities/DispatchActivity.javaapp/src/org/commcare/activities/HomeScreenBaseActivity.javaapp/src/org/commcare/connect/ConnectAppLauncher.ktapp/src/org/commcare/connect/LaunchOutcomeRouter.ktapp/src/org/commcare/fragments/connect/ConnectJobsListsFragment.javaapp/src/org/commcare/login/PostLoginDestination.ktapp/src/org/commcare/login/PostLoginRouter.ktapp/unit-tests/src/org/commcare/connect/ConnectAppLauncherTest.ktapp/unit-tests/src/org/commcare/connect/LaunchOutcomeRouterTest.ktapp/unit-tests/src/org/commcare/login/PostLoginRouterTest.ktdocs/commcare/login-engine.md
💤 Files with no reviewable changes (3)
- app/unit-tests/src/org/commcare/login/PostLoginRouterTest.kt
- app/src/org/commcare/login/PostLoginDestination.kt
- app/src/org/commcare/login/PostLoginRouter.kt
[AI] Addressed code review by normalizing the Connect username with Locale.ROOT, guarding showLaunchDialog against a saved fragment state, and removing an unused analytics mock stub. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[AI] Collapsed SeatResult.Failed to a parameterless object, removed the unused SeatFailure enum and SeatResultCallback interface, and dropped the dead SeatResult parameter from SeatAppActivity.finishWithResult. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng' of github.com:dimagi/commcare-android into CCCT-2439-connect-login-silent-launch-path
…ng' of github.com:dimagi/commcare-android into CCCT-2439-connect-login-silent-launch-path
…on-and-inline-app-seating' into CCCT-2439-connect-login-silent-launch-path
| } | ||
|
|
||
| private void finishWithResult(SeatResult result) { | ||
| private void finishWithResult() { |
There was a problem hiding this comment.
Nit: This function no longer takes a result so the name is misleading. Maybe rename it to setResultAndFinish instead?
[AI] Renamed SeatAppActivity.finishWithResult() to setResultAndFinish() to match the method body and avoid collision with UpdateActivity.finishWithResult(String). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The base branch was changed.
CCCT-2439
Product Description
When a worker launches an installed app from a Connect opportunity, it now opens directly behind a single progress dialog instead of briefly flashing the
LoginActivityand app-seating screens. Backing out of the app's Home screen returns to the Connect opportunities list, as before.Here is the new login flow (I intentionally slowed it down for this demo so every phase is shown):
Screen_Recording_20260605_131211_CommCare.Debug.mp4
Here is the old login flow (I intentionally slowed it down for this demo so every phase is shown):
Screen_Recording_20260605_131613_CommCare.Debug.mp4
Technical Summary
Phase 3 of the headless-login work (builds on CCCT-2437/CCCT-2438).
ConnectAppLauncherruns the seat + PersonalID login headlessly off the opportunities list and reports aLaunchOutcome;LaunchOutcomeRouter/LaunchActionsis a thin seam (unit-tested without a fragment) that maps each outcome to a UI/nav action.ConnectJobsListsFragmentdrives it onviewLifecycleOwner.lifecycleScopebehind a modalCustomProgressDialog.A few decisions worth knowing before the diff:
TokenDenied→TokenExceptionHandler; a seat failure →DispatchActivity(which reaches recovery for a corrupted app); credential/other failures → fall back to the legacyConnectAppUtils.launchApp, which showsLoginActivitywith its usual error handling (no new error UI/strings).DispatchActivity's post-login intent handling back to its master form and deletes the now-unusedPostLoginRouter/PostLoginDestination/LaunchContext(+ test). They were added in CCCT-2438 on the expectation phase 3 would consume them, but phase 3 doesn't —route()only ever producedHomethere, so the round-trip was behavior-identical to the inline version.SeatResultis now justSuccess/Failed, dropping the plan's per-reasonSeatFailureenum and theSeatResultCallbackseam. No caller acts on the reason — a seat failure is already surfaced through the app'sSTATE_CORRUPTEDstate thatDispatchActivitydetects — so the extra detail was unused.Open Question
Open question for the team (not addressed here): the progress dialog shows the same text in both its title and body during the seating and login phases (pre-existing for the login phase; introduced for seating to match). Is this worth tweaking?
Safety Assurance
Safety story
What gives me confidence:
Risks to review:
DispatchActivity), so the session stays active with the Connect screen on the back stack and theHOME_SCREEN-for-result logout/back contract is bypassed for this entry point — worth confirming logout/back behavior is acceptable.ConnectJobsListsFragment(no fragment-Robolectric harness exists in the suite); the routing decision is covered via theLaunchActionsseam instead.Automated test coverage
ConnectAppLauncherTest— phase/credential resolution, seat-failure short-circuit, andLoginResult→LaunchOutcomemapping for each failure variant.LaunchOutcomeRouterTest— every outcome dismisses progress first and dispatches the expectedLaunchActionscall.