Skip to content

Commit 2aec8ff

Browse files
authored
feat(opener): add inAppBrowser option for iOS and Android (#2803)
1 parent 9799f0d commit 2aec8ff

File tree

8 files changed

+69
-18
lines changed

8 files changed

+69
-18
lines changed

.changes/in-app-browser.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"opener": patch:feat
3+
"opener-js": patch:feat
4+
---
5+
6+
Add `inAppBrowser` option to open URLs in an in-app browser on Android and iOS.

examples/api/src-tauri/capabilities/base.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@
8383
},
8484
"store:default",
8585
"opener:default",
86+
{
87+
"identifier": "opener:allow-open-url",
88+
"allow": [
89+
{
90+
"url": "https://*",
91+
"app": "inAppBrowser"
92+
}
93+
]
94+
},
8695
{
8796
"identifier": "opener:allow-open-path",
8897
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }]

examples/api/src/views/Opener.svelte

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
<script>
22
import * as opener from '@tauri-apps/plugin-opener'
3+
import { platform } from '@tauri-apps/plugin-os'
34
45
export let onMessage
56
6-
let url = ''
7-
let urlProgram = ''
7+
let url = 'https://tauri.app'
8+
let urlProgram =
9+
platform() === 'ios' || platform() === 'android' ? 'inAppBrowser' : ''
810
function openUrl() {
911
opener.openUrl(url, urlProgram ? urlProgram : undefined).catch(onMessage)
1012
}

plugins/opener/android/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,6 @@ android {
3535
dependencies {
3636
implementation("androidx.core:core-ktx:1.9.0")
3737
implementation("com.fasterxml.jackson.core:jackson-databind:2.15.3")
38+
implementation("androidx.browser:browser:1.8.0")
3839
implementation(project(":tauri-android"))
3940
}

plugins/opener/android/src/main/java/OpenerPlugin.kt

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,36 @@ package app.tauri.opener
66

77
import android.app.Activity
88
import android.content.Intent
9-
import android.net.Uri
9+
import androidx.browser.customtabs.CustomTabsIntent
1010
import app.tauri.annotation.Command
1111
import app.tauri.annotation.TauriPlugin
1212
import app.tauri.plugin.Invoke
1313
import app.tauri.plugin.Plugin
14-
import java.io.File
14+
import androidx.core.net.toUri
15+
import app.tauri.annotation.InvokeArg
16+
17+
@InvokeArg
18+
class OpenArgs {
19+
lateinit var url: String
20+
var with: String? = null
21+
}
1522

1623
@TauriPlugin
1724
class OpenerPlugin(private val activity: Activity) : Plugin(activity) {
1825
@Command
1926
fun open(invoke: Invoke) {
2027
try {
21-
val url = invoke.parseArgs(String::class.java)
22-
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
23-
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
24-
activity.applicationContext?.startActivity(intent)
28+
val args = invoke.parseArgs(OpenArgs::class.java)
29+
30+
if (args.with == "inAppBrowser") {
31+
val builder = CustomTabsIntent.Builder()
32+
val intent = builder.build()
33+
intent.launchUrl(activity, args.url.toUri())
34+
} else {
35+
val intent = Intent(Intent.ACTION_VIEW, args.url.toUri())
36+
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
37+
activity.applicationContext?.startActivity(intent)
38+
}
2539
invoke.resolve()
2640
} catch (ex: Exception) {
2741
invoke.reject(ex.message)

plugins/opener/guest-js/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ import { invoke } from '@tauri-apps/api/core'
3535
*
3636
* @param url The URL to open.
3737
* @param openWith The app to open the URL with. If not specified, defaults to the system default application for the specified url type.
38+
* On mobile, `openWith` can be provided as `inAppBrowser` to open the URL in an in-app browser. Otherwise, it will open the URL in the system default browser.
3839
*
3940
* @since 2.0.0
4041
*/
4142
export async function openUrl(
4243
url: string | URL,
43-
openWith?: string
44+
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
45+
openWith?: 'inAppBrowser' | string
4446
): Promise<void> {
4547
await invoke('plugin:opener|open_url', {
4648
url,

plugins/opener/ios/Sources/OpenerPlugin.swift

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,35 @@
33
// SPDX-License-Identifier: MIT
44

55
import Foundation
6+
import SafariServices
67
import SwiftRs
78
import Tauri
89
import UIKit
910
import WebKit
1011

12+
struct OpenArgs: Decodable {
13+
let url: String
14+
let with: String?
15+
}
16+
1117
class OpenerPlugin: Plugin {
1218
@objc public func open(_ invoke: Invoke) throws {
1319
do {
14-
let urlString = try invoke.parseArgs(String.self)
15-
if let url = URL(string: urlString) {
16-
if #available(iOS 10, *) {
17-
UIApplication.shared.open(url, options: [:])
20+
let args = try invoke.parseArgs(OpenArgs.self)
21+
if let url = URL(string: args.url) {
22+
if args.with == "inAppBrowser" {
23+
DispatchQueue.main.async {
24+
let safariVC = SFSafariViewController(url: url)
25+
self.manager.viewController?.present(safariVC, animated: true)
26+
}
1827
} else {
19-
UIApplication.shared.openURL(url)
28+
if #available(iOS 10, *) {
29+
UIApplication.shared.open(url, options: [:])
30+
} else {
31+
UIApplication.shared.openURL(url)
32+
}
2033
}
34+
2135
}
2236
invoke.resolve()
2337
} catch {

plugins/opener/src/lib.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl<R: Runtime> Opener<R> {
5555
///
5656
/// ## Platform-specific:
5757
///
58-
/// - **Android / iOS**: Always opens using default program.
58+
/// - **Android / iOS**: Always opens using default program, unless `with` is provided as "inAppBrowser".
5959
#[cfg(desktop)]
6060
pub fn open_url(&self, url: impl Into<String>, with: Option<impl Into<String>>) -> Result<()> {
6161
crate::open::open(url.into(), with.map(Into::into))
@@ -78,11 +78,14 @@ impl<R: Runtime> Opener<R> {
7878
///
7979
/// ## Platform-specific:
8080
///
81-
/// - **Android / iOS**: Always opens using default program.
81+
/// - **Android / iOS**: Always opens using default program, unless `with` is provided as "inAppBrowser".
8282
#[cfg(mobile)]
83-
pub fn open_url(&self, url: impl Into<String>, _with: Option<impl Into<String>>) -> Result<()> {
83+
pub fn open_url(&self, url: impl Into<String>, with: Option<impl Into<String>>) -> Result<()> {
8484
self.mobile_plugin_handle
85-
.run_mobile_plugin("open", url.into())
85+
.run_mobile_plugin(
86+
"open",
87+
serde_json::json!({ "url": url.into(), "with": with.map(Into::into) }),
88+
)
8689
.map_err(Into::into)
8790
}
8891

0 commit comments

Comments
 (0)