Skip to content

feat(ui): use Chrome Custom Tabs for explorer launches (#138)#148

Merged
RaheemJnr merged 1 commit intomainfrom
feat/issue-138-custom-tabs
Apr 30, 2026
Merged

feat(ui): use Chrome Custom Tabs for explorer launches (#138)#148
RaheemJnr merged 1 commit intomainfrom
feat/issue-138-custom-tabs

Conversation

@RaheemJnr
Copy link
Copy Markdown
Owner

Closes #138.

Summary

Replaces `Intent.ACTION_VIEW` with `CustomTabsIntent` for every web URL the app launches. Six explorer launch sites + one GitHub link swapped. The APK install intent in `UpdateDownloader` is unchanged (file:// URI, not a web URL).

Why this matters

The smoke report on v1.5.2 confirmed: even with the SavedStateHandle persistence backstop, the round-trip itself feels jarring. User taps the explorer link, Chrome takes over its own task, the user copies a block height, returns to the app, the auth gate re-engages and forces PIN re-entry, then they land on Home. The `SavedStateHandle` fix means the sheet at least re-opens with their typed digits intact, but the unexpected lock screen mid-task is still bad UX.

Custom Tabs runs inside Pocket Node's task instead of starting Chrome's own task. The OS treats the round-trip as part of Pocket Node, so:

  • The auth gate does not re-engage on return.
  • Process death by aggressive memory managers (Tecno, Infinix, MIUI) is much less likely because the Custom Tab and host app share a task.
  • The browser back arrow drops the user straight back where they were, with the sheet still open.

Implementation

Single helper at `ui/util/CustomTabsLauncher.kt:openInBrowser` handles the launch with a defensive fallback to plain `ACTION_VIEW` if the Custom Tabs launch crashes for any reason. Returns `Boolean` so callers can surface a "no browser available" snackbar on the rare device with no web browser at all.

```kotlin
fun openInBrowser(context: Context, url: String): Boolean {
val uri = Uri.parse(url)
return try {
CustomTabsIntent.Builder()
.setShowTitle(true)
.setUrlBarHidingEnabled(false)
.build()
.launchUrl(context, uri)
true
} catch (: ActivityNotFoundException) { false }
catch (
: Throwable) {
try { context.startActivity(Intent(Intent.ACTION_VIEW, uri)); true }
catch (_: ActivityNotFoundException) { false }
}
}
```

Call sites swapped

File Context
`HomeScreen.kt` "Look up on explorer" from CUSTOM block-height field
`HomeScreen.kt` Transaction detail bottom sheet
`SettingsScreen.kt` Sync explorer helper
`SettingsScreen.kt` GitHub "Open Source" link
`ActivityScreen.kt` Transaction detail
`SendScreen.kt` Tx-confirmed explorer link

Defense in depth

The `SavedStateHandle` persistence for `showSyncOptionsDialog` + `showPostImportSyncDialog` from v1.5.2 stays in place. If a device still kills the process despite Custom Tabs (unlikely but possible on the most aggressive ROMs), the sheet still re-opens via the prior fix.

Test plan

  • `assembleDebug` succeeds.
  • `testDebugUnitTest` passes.
  • All 6 explorer + 1 GitHub launches now route through `openInBrowser`. Verified via grep for `Intent.ACTION_VIEW` — only remaining instances are inside the helper itself (defensive fallback) and `UpdateDownloader.kt` (APK install file:// URI, intentionally unchanged).
  • Manual smoke on a Tecno or Infinix device: tap Look up on explorer, copy a block height, return to app. Verify auth gate does NOT re-engage and the bottom sheet is exactly where it was, no PIN re-entry.
  • Manual smoke on a stock-Android device: same flow, verify Custom Tab opens correctly.

Related

Replaces Intent.ACTION_VIEW with CustomTabsIntent for every web URL the
app launches, except the APK install intent in UpdateDownloader (which
is a file:// URI, not a web URL).

Why: Custom Tabs runs inside Pocket Node's task instead of starting
Chrome's own task. The OS treats the round-trip as part of Pocket Node,
so the auth gate doesn't re-engage on return and the user isn't bounced
through a PIN re-entry. Process death by aggressive memory managers
(Tecno / Infinix / MIUI) is also much less likely because the Custom Tab
and host app share a task.

Single helper at ui/util/CustomTabsLauncher.kt:openInBrowser handles the
launch with a defensive fallback to plain ACTION_VIEW if the Custom Tabs
launch crashes for any reason. Returns Boolean so callers can surface a
'no browser available' snackbar on the rare device with no web browser
at all.

Six explorer launch sites + one GitHub link swapped:
- HomeScreen 'Look up on explorer' helper
- HomeScreen tx detail bottom sheet
- SettingsScreen sync explorer helper
- SettingsScreen GitHub 'Open Source' link
- ActivityScreen tx detail
- SendScreen tx-confirmed explorer link

The v1.5.2 SavedStateHandle backstop for the post-import sync sheet
stays in place as defense-in-depth for any device that still kills the
process despite Custom Tabs.
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pocket-node Ready Ready Preview, Comment Apr 30, 2026 1:25pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

Warning

Rate limit exceeded

@RaheemJnr has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 39 minutes and 42 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b8a320a2-1dc2-4e66-857c-4d29359d1f28

📥 Commits

Reviewing files that changed from the base of the PR and between ac9fac6 and 0eb730a.

📒 Files selected for processing (7)
  • android/app/build.gradle.kts
  • android/app/src/main/java/com/rjnr/pocketnode/ui/screens/activity/ActivityScreen.kt
  • android/app/src/main/java/com/rjnr/pocketnode/ui/screens/home/HomeScreen.kt
  • android/app/src/main/java/com/rjnr/pocketnode/ui/screens/send/SendScreen.kt
  • android/app/src/main/java/com/rjnr/pocketnode/ui/screens/settings/SettingsScreen.kt
  • android/app/src/main/java/com/rjnr/pocketnode/ui/util/CustomTabsLauncher.kt
  • android/gradle/libs.versions.toml
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-138-custom-tabs

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
Review rate limit: 0/1 reviews remaining, refill in 39 minutes and 42 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@RaheemJnr RaheemJnr merged commit cada93f into main Apr 30, 2026
5 checks passed
@RaheemJnr RaheemJnr deleted the feat/issue-138-custom-tabs branch April 30, 2026 14:01
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.

Use Chrome Custom Tabs for explorer block-height lookup

1 participant