Skip to content

Add handleSignInLink for Android (feature parity with iOS)#73

Merged
mhamann merged 2 commits into
rownd:mainfrom
leonelgalan:fix/android-handle-sign-in-link
Feb 26, 2026
Merged

Add handleSignInLink for Android (feature parity with iOS)#73
mhamann merged 2 commits into
rownd:mainfrom
leonelgalan:fix/android-handle-sign-in-link

Conversation

@leonelgalan
Copy link
Copy Markdown
Contributor

@leonelgalan leonelgalan commented Feb 26, 2026

handleSignInLink is implemented on iOS and called from the JS bridge (GlobalContext.tsx) for deep link sign-in, but has no Android implementation. This means deep link sign-in silently fails on Android.

Adds @ReactMethod handleSignInLink(url) to RowndPluginModule.kt. Uses reflection to call the internal SignInLinkApi since the Android SDK doesn't expose a public equivalent of iOS's Rownd.handleSmartLink.

Summary by Sourcery

New Features:

  • Expose a handleSignInLink(url) React Native method on Android to process deep link sign-in flows.

`handleSignInLink` is implemented on iOS and called from the JS bridge
(`GlobalContext.tsx`) for deep link sign-in, but has no Android implementation.
This means deep link sign-in silently fails on Android.

Adds `@ReactMethod handleSignInLink(url)` to `RowndPluginModule.kt`. Uses
reflection to call the internal `SignInLinkApi` since the Android SDK doesn't
expose a public equivalent of iOS's `Rownd.handleSmartLink`.
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Feb 26, 2026

Reviewer's Guide

Implements Android parity for deep link sign-in by adding a new handleSignInLink React Native bridge method that delegates to the internal Rownd SignInLinkApi via reflection and sets up an ACTION_VIEW intent on the current activity.

Sequence diagram for Android deep link sign-in handling via handleSignInLink

sequenceDiagram
    actor User
    participant AndroidOS
    participant ReactNativeJS as ReactNative_JS_GlobalContext
    participant RowndPluginModule
    participant Rownd
    participant SignInLinkApi

    User->>AndroidOS: Tap deep link URL
    AndroidOS->>ReactNativeJS: Launch app with deep link intent
    ReactNativeJS->>RowndPluginModule: handleSignInLink(url)
    RowndPluginModule->>RowndPluginModule: Get currentActivity
    alt Activity available
        RowndPluginModule->>RowndPluginModule: Set activity.intent = ACTION_VIEW(url)
        RowndPluginModule->>Rownd: getSignInLinkApi_android_release()
        Rownd-->>RowndPluginModule: SignInLinkApi instance
        RowndPluginModule->>SignInLinkApi: signInWithLinkIfPresentOnIntentOrClipboard_android_release(activity)
        SignInLinkApi-->>RowndPluginModule: Sign-in result
        RowndPluginModule-->>ReactNativeJS: Resolve sign-in flow (success/failure)
    else No activity
        RowndPluginModule-->>ReactNativeJS: Return early (no-op)
    end
Loading

Class diagram for RowndPluginModule with new handleSignInLink method

classDiagram
    class RowndPluginModule {
        +RowndPluginModule(reactContext: ReactApplicationContext)
        +handleSignInLink(url: String) void
        +requestSignIn(signInConfig: ReadableMap) void
    }

    class Rownd {
        +getSignInLinkApi_android_release() SignInLinkApi
    }

    class SignInLinkApi {
        +signInWithLinkIfPresentOnIntentOrClipboard_android_release(activity: android.app.Activity) void
    }

    class ReactNativeJS_GlobalContext {
        +handleSignInLink(url: string) void
    }

    ReactNativeJS_GlobalContext --> RowndPluginModule : calls
    RowndPluginModule ..> Rownd : reflection getSignInLinkApi_android_release
    Rownd --> SignInLinkApi : returns instance
    RowndPluginModule ..> SignInLinkApi : reflection signInWithLinkIfPresentOnIntentOrClipboard_android_release
    RowndPluginModule --> android.app.Activity : sets ACTION_VIEW intent

    class android.app.Activity
Loading

File-Level Changes

Change Details Files
Add Android React Native bridge method to handle deep link sign-in via Rownd's internal SignInLinkApi using reflection.
  • Expose a new @ReactMethod handleSignInLink(url: String) in the RowndPluginModule React Native module.
  • Obtain the current Activity from the React application context and construct an ACTION_VIEW intent with the provided URL, assigning it to activity.intent.
  • Use reflection to access Rownd.getSignInLinkApi$android-release and obtain the internal SignInLinkApi instance.
  • Use reflection to invoke signInWithLinkIfPresentOnIntentOrClipboard$android-release on the SignInLinkApi with the current Activity.
  • Wrap the reflection logic in a try/catch and log an error with Log.e if any exception occurs.
android/src/main/java/com/reactnativerowndplugin/RowndPluginModule.kt

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • Overwriting activity.intent with a new ACTION_VIEW intent could interfere with other logic that relies on the original launch intent; consider passing a local Intent instance directly to the reflective call instead of mutating activity.intent.
  • The reflective calls rely on hard-coded, release-specific method names (e.g., getSignInLinkApi$android-release); if possible, centralize these strings as constants or add a small helper/wrapper to reduce the risk of typos and ease future refactors.
  • The catch (Exception) block only logs the error message; consider logging the full exception or at least the stack trace to aid debugging when the reflection fails on certain builds or devices.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Overwriting `activity.intent` with a new `ACTION_VIEW` intent could interfere with other logic that relies on the original launch intent; consider passing a local `Intent` instance directly to the reflective call instead of mutating `activity.intent`.
- The reflective calls rely on hard-coded, release-specific method names (e.g., `getSignInLinkApi$android-release`); if possible, centralize these strings as constants or add a small helper/wrapper to reduce the risk of typos and ease future refactors.
- The `catch (Exception)` block only logs the error message; consider logging the full exception or at least the stack trace to aid debugging when the reflection fails on certain builds or devices.

## Individual Comments

### Comment 1
<location path="android/src/main/java/com/reactnativerowndplugin/RowndPluginModule.kt" line_range="124-127" />
<code_context>
+    @ReactMethod
+    fun handleSignInLink(url: String) {
+      val activity = reactApplicationContext.currentActivity ?: return
+      activity.intent = android.content.Intent(
+        android.content.Intent.ACTION_VIEW,
+        android.net.Uri.parse(url)
+      )
+      try {
+        val apiGetter = Rownd.javaClass.getMethod("getSignInLinkApi\$android-release")
</code_context>
<issue_to_address>
**issue (bug_risk):** Assigning a new Intent to `activity.intent` may have unintended side effects on the host Activity.

This can break future `activity.intent` usage (e.g., subsequent reads, deep links, or lifecycle logic relying on the original intent). Instead, create a local `Intent` for the SDK call or mutate only the necessary fields on the existing `activity.intent` so the Activity’s state isn’t globally changed.
</issue_to_address>

### Comment 2
<location path="android/src/main/java/com/reactnativerowndplugin/RowndPluginModule.kt" line_range="136-138" />
<code_context>
+          android.app.Activity::class.java
+        )
+        method.invoke(api, activity)
+      } catch (e: Exception) {
+        Log.e("RowndPlugin", "handleSignInLink failed: ${e.message}")
+      }
+    }
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Catching `Exception` and only logging the message hides useful debugging information and may swallow important failures.

This turns any reflection, parsing, or SDK failure into a single log line and drops the stack trace. Please log the full exception (e.g., `Log.e(

```suggestion
      } catch (e: Exception) {
        Log.e("RowndPlugin", "handleSignInLink failed", e)
      }
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Preserve stack trace by passing exception object to Log.e() instead of only logging the message string. This improves debugging for reflection, parsing, or SDK failures.
@leonelgalan
Copy link
Copy Markdown
Contributor Author

  • Re: overwriting activity.intent — The internal method reads the URL from activity.intent, so we have to set it. We could save/restore the original, but that adds complexity for a workaround that should be replaced by a public API. Open to suggestions if there’s a cleaner way to pass the URL to the internal method.
  • Re: hardcoded reflection strings — Agreed it’s fragile, but that’s inherent to reflection against internal APIs. Constants don’t reduce the real risk — if the method is renamed, it breaks either way. This reinforces that a public handleSignInLink/handleSmartLink API on the Android SDK would be the proper fix.
  • Fixed comment 3 in d146981

@mhamann mhamann merged commit 9f593de into rownd:main Feb 26, 2026
7 checks passed
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.

2 participants