Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# These are supported funding model platforms

github: [mcodex]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
2 changes: 1 addition & 1 deletion .github/workflows/ios-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,6 @@ jobs:
-scheme InappbrowserNitroExample \
-sdk iphonesimulator \
-configuration Debug \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-destination 'generic/platform=iOS Simulator' \
build \
CODE_SIGNING_ALLOWED=NO | xcpretty
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,10 @@ android/keystores/debug.keystore
lib/
tsconfig.tsbuildinfo

# stray tsc output in sources (should never be committed)
src/**/*.js
src/**/*.js.map
src/**/*.d.ts
src/**/*.d.ts.map

nitrogen/
40 changes: 35 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ No additional steps—Gradle autolinking handles everything.
### Imperative API

```tsx
import { InAppBrowser } from 'react-native-inappbrowser-nitro'
import { isAvailable, open } from 'react-native-inappbrowser-nitro'

async function openDocs() {
if (!(await InAppBrowser.isAvailable())) {
if (!(await isAvailable())) {
console.warn('No compatible browser found')
return
}

const result = await InAppBrowser.open('https://nitro.margelo.com', {
const result = await open('https://nitro.margelo.com', {
preferredBarTintColor: { base: '#111827', light: '#1F2933', highContrast: '#000000' },
preferredControlTintColor: { base: '#F9FAFB', highContrast: '#FFD700' }, // iOS 26+
toolbarColor: { base: '#2563EB', dark: '#1E3A8A' },
Expand Down Expand Up @@ -125,7 +125,9 @@ export function LaunchButton() {
### Authentication Flow (OAuth / SSO)

```tsx
const result = await InAppBrowser.openAuth(
import { openAuth } from 'react-native-inappbrowser-nitro'

const result = await openAuth(
'https://provider.com/oauth/authorize?client_id=abc',
'myapp://oauth/callback',
{
Expand All @@ -144,6 +146,34 @@ if (result.type === 'success' && result.url) {

Migrating from earlier `react-native-inappbrowser-nitro` versions? Note these key changes when adopting the Nitro rewrite:

### Migrating to v3 (named exports)

The `InAppBrowser` static class has been replaced with individual named exports for better tree shaking. Update your imports:

```diff
-import { InAppBrowser } from 'react-native-inappbrowser-nitro'
+import { open, openAuth, close, closeAuth, isAvailable } from 'react-native-inappbrowser-nitro'

-InAppBrowser.open(url, options)
+open(url, options)

-InAppBrowser.openAuth(url, redirectUrl, options)
+openAuth(url, redirectUrl, options)

-InAppBrowser.close()
+close()

-InAppBrowser.closeAuth()
+closeAuth()

-InAppBrowser.isAvailable()
+isAvailable()
```

The hook import is unchanged: `import { useInAppBrowser } from 'react-native-inappbrowser-nitro'`

---

### 1. `open()` resolves on presentation

Older releases resolved the promise when the browser closed. The new implementation resolves as soon as Safari/Custom Tabs is shown (mirroring Android behavior), and dismissal status is delivered asynchronously.
Expand Down Expand Up @@ -194,7 +224,7 @@ Run `yarn codegen && yarn build` (or your project script) to regenerate Nitro bi
| `close()` | Programmatically dismiss an open browser session. |
| `closeAuth()` | Abort an authentication session. |

All functions return Promises and are fully typed. See `src/core/InAppBrowser.ts` for the higher-level wrapper implementation.
All functions return Promises and are fully typed. See `src/core/native.ts` for the native module wrapper implementation.

## Options Reference

Expand Down
1 change: 1 addition & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ dependencies {
implementation project(":react-native-nitro-modules")

implementation "androidx.browser:browser:1.7.0"
implementation "androidx.core:core:1.13.1"
implementation "androidx.core:core-ktx:1.13.1"
}

Expand Down
5 changes: 4 additions & 1 deletion android/src/main/cpp/cpp-adapter.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include <jni.h>
#include <fbjni/fbjni.h>
#include "InappbrowserNitroOnLoad.hpp"

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
return margelo::nitro::inappbrowsernitro::initialize(vm);
return facebook::jni::initialize(vm, [] {
margelo::nitro::inappbrowsernitro::registerAllNatives();
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class HybridInappbrowserNitro : HybridInappbrowserNitroSpec() {
private val reactContext get() = NitroModules.applicationContext
private val applicationContext: Context?
get() = reactContext ?: NitroModules.applicationContext
private val applicationContext get() = NitroModules.applicationContext

override fun isAvailable(): Promise<Boolean> {
val context = applicationContext ?: return Promise.resolved(false)
Expand Down Expand Up @@ -50,10 +48,12 @@ class HybridInappbrowserNitro : HybridInappbrowserNitroSpec() {
}

override fun close(): Promise<Unit> {
// Custom Tabs runs in a separate Activity; close is handled by the user navigating back.
return Promise.resolved(Unit)
}

override fun closeAuth(): Promise<Unit> {
// Custom Tabs runs in a separate Activity; close is handled by the user navigating back.
return Promise.resolved(Unit)
}

Expand All @@ -63,9 +63,9 @@ class HybridInappbrowserNitro : HybridInappbrowserNitroSpec() {
?: return dismiss("Invalid URL: $url")

val customTabsPackage = CustomTabsPackageHelper.resolvePackage(context, null)
val launchContext = reactContext?.currentActivity ?: context
val launchContext = applicationContext?.currentActivity ?: context

if (customTabsPackage == "com.android.chrome") {
if (customTabsPackage != null) {
val intent = CustomTabsIntentFactory(context, null).create(options)
intent.intent.setPackage(customTabsPackage)
val launched = launchCustomTab(intent, launchContext, parsedUri)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@ internal object CustomTabsPackageHelper {
return CustomTabsClient.getPackageName(context, null)
}

@Suppress("DEPRECATION")
private fun isPackageInstalled(context: Context, packageName: String): Boolean {
return try {
context.packageManager.getPackageInfo(packageName, 0)
true
} catch (e: PackageManager.NameNotFoundException) {
} catch (_: PackageManager.NameNotFoundException) {
false
} catch (_: Exception) {
false
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.inappbrowsernitro.browser

import android.content.Context
import android.graphics.Color
import android.os.Build
import android.view.accessibility.AccessibilityManager
import androidx.core.content.getSystemService
import com.margelo.nitro.inappbrowsernitro.DynamicColor
Expand All @@ -11,14 +12,15 @@ internal object DynamicColorResolver {
dynamicColor ?: return null

val accessibilityManager = context.getSystemService<AccessibilityManager>()
val isHighContrast = accessibilityManager?.let { manager ->
runCatching {
val method = AccessibilityManager::class.java.getMethod("isHighTextContrastEnabled")
(method.invoke(manager) as? Boolean) == true
}.getOrDefault(false)
} == true
val isHighContrast = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
isHighTextContrastEnabledSafe(accessibilityManager)
} else {
false
}

val isDark = (context.resources.configuration.uiMode and android.content.res.Configuration.UI_MODE_NIGHT_MASK) == android.content.res.Configuration.UI_MODE_NIGHT_YES
val isDark = (context.resources.configuration.uiMode and
android.content.res.Configuration.UI_MODE_NIGHT_MASK) ==
android.content.res.Configuration.UI_MODE_NIGHT_YES

val candidate = when {
isHighContrast && dynamicColor.highContrast != null -> dynamicColor.highContrast
Expand Down Expand Up @@ -49,5 +51,18 @@ internal object DynamicColorResolver {
}
}

// `AccessibilityManager.isHighTextContrastEnabled()` is an @hide API on
// Android; it is not exposed through the public SDK. Access it via
// reflection and fall back to `false` if unavailable.
private fun isHighTextContrastEnabledSafe(manager: AccessibilityManager?): Boolean {
manager ?: return false
return try {
val method = AccessibilityManager::class.java.getMethod("isHighTextContrastEnabled")
method.invoke(manager) as? Boolean ?: false
} catch (_: Throwable) {
false
}
}

enum class DynamicScheme { SYSTEM, LIGHT, DARK }
}
41 changes: 41 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.12/schema.json",
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
"files": {
"includes": [
"**",
"!**/node_modules",
"!**/lib",
"!**/build",
"!**/dist",
"!**/nitrogen/generated",
"!**/android",
"!**/ios",
"!example/vendor",
"!**/*.min.js",
"!**/*.bundle.js"
]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 80
},
"linter": {
"enabled": true,
"rules": { "recommended": true }
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"semicolons": "asNeeded",
"trailingCommas": "es5",
"quoteProperties": "preserve"
}
},
"assist": {
"enabled": true,
"actions": { "source": { "organizeImports": "on" } }
}
}
4 changes: 0 additions & 4 deletions example/.eslintrc.js

This file was deleted.

5 changes: 0 additions & 5 deletions example/.prettierrc.js

This file was deleted.

Loading
Loading