feat(opener): add `inAppBrowser` option for iOS and Android (#2803)

pull/2780/head
Lucas Fernandes Nogueira 3 weeks ago committed by GitHub
parent 9799f0dbab
commit 2aec8ff4c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,6 @@
---
"opener": patch:feat
"opener-js": patch:feat
---
Add `inAppBrowser` option to open URLs in an in-app browser on Android and iOS.

@ -83,6 +83,15 @@
}, },
"store:default", "store:default",
"opener:default", "opener:default",
{
"identifier": "opener:allow-open-url",
"allow": [
{
"url": "https://*",
"app": "inAppBrowser"
}
]
},
{ {
"identifier": "opener:allow-open-path", "identifier": "opener:allow-open-path",
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }] "allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }]

@ -1,10 +1,12 @@
<script> <script>
import * as opener from '@tauri-apps/plugin-opener' import * as opener from '@tauri-apps/plugin-opener'
import { platform } from '@tauri-apps/plugin-os'
export let onMessage export let onMessage
let url = '' let url = 'https://tauri.app'
let urlProgram = '' let urlProgram =
platform() === 'ios' || platform() === 'android' ? 'inAppBrowser' : ''
function openUrl() { function openUrl() {
opener.openUrl(url, urlProgram ? urlProgram : undefined).catch(onMessage) opener.openUrl(url, urlProgram ? urlProgram : undefined).catch(onMessage)
} }

@ -35,5 +35,6 @@ android {
dependencies { dependencies {
implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.core:core-ktx:1.9.0")
implementation("com.fasterxml.jackson.core:jackson-databind:2.15.3") implementation("com.fasterxml.jackson.core:jackson-databind:2.15.3")
implementation("androidx.browser:browser:1.8.0")
implementation(project(":tauri-android")) implementation(project(":tauri-android"))
} }

@ -6,22 +6,36 @@ package app.tauri.opener
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.net.Uri import androidx.browser.customtabs.CustomTabsIntent
import app.tauri.annotation.Command import app.tauri.annotation.Command
import app.tauri.annotation.TauriPlugin import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.Invoke import app.tauri.plugin.Invoke
import app.tauri.plugin.Plugin import app.tauri.plugin.Plugin
import java.io.File import androidx.core.net.toUri
import app.tauri.annotation.InvokeArg
@InvokeArg
class OpenArgs {
lateinit var url: String
var with: String? = null
}
@TauriPlugin @TauriPlugin
class OpenerPlugin(private val activity: Activity) : Plugin(activity) { class OpenerPlugin(private val activity: Activity) : Plugin(activity) {
@Command @Command
fun open(invoke: Invoke) { fun open(invoke: Invoke) {
try { try {
val url = invoke.parseArgs(String::class.java) val args = invoke.parseArgs(OpenArgs::class.java)
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
if (args.with == "inAppBrowser") {
val builder = CustomTabsIntent.Builder()
val intent = builder.build()
intent.launchUrl(activity, args.url.toUri())
} else {
val intent = Intent(Intent.ACTION_VIEW, args.url.toUri())
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.applicationContext?.startActivity(intent) activity.applicationContext?.startActivity(intent)
}
invoke.resolve() invoke.resolve()
} catch (ex: Exception) { } catch (ex: Exception) {
invoke.reject(ex.message) invoke.reject(ex.message)

@ -35,12 +35,14 @@ import { invoke } from '@tauri-apps/api/core'
* *
* @param url The URL to open. * @param url The URL to open.
* @param openWith The app to open the URL with. If not specified, defaults to the system default application for the specified url type. * @param openWith The app to open the URL with. If not specified, defaults to the system default application for the specified url type.
* 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.
* *
* @since 2.0.0 * @since 2.0.0
*/ */
export async function openUrl( export async function openUrl(
url: string | URL, url: string | URL,
openWith?: string // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
openWith?: 'inAppBrowser' | string
): Promise<void> { ): Promise<void> {
await invoke('plugin:opener|open_url', { await invoke('plugin:opener|open_url', {
url, url,

@ -3,22 +3,36 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import Foundation import Foundation
import SafariServices
import SwiftRs import SwiftRs
import Tauri import Tauri
import UIKit import UIKit
import WebKit import WebKit
struct OpenArgs: Decodable {
let url: String
let with: String?
}
class OpenerPlugin: Plugin { class OpenerPlugin: Plugin {
@objc public func open(_ invoke: Invoke) throws { @objc public func open(_ invoke: Invoke) throws {
do { do {
let urlString = try invoke.parseArgs(String.self) let args = try invoke.parseArgs(OpenArgs.self)
if let url = URL(string: urlString) { if let url = URL(string: args.url) {
if args.with == "inAppBrowser" {
DispatchQueue.main.async {
let safariVC = SFSafariViewController(url: url)
self.manager.viewController?.present(safariVC, animated: true)
}
} else {
if #available(iOS 10, *) { if #available(iOS 10, *) {
UIApplication.shared.open(url, options: [:]) UIApplication.shared.open(url, options: [:])
} else { } else {
UIApplication.shared.openURL(url) UIApplication.shared.openURL(url)
} }
} }
}
invoke.resolve() invoke.resolve()
} catch { } catch {
invoke.reject(error.localizedDescription) invoke.reject(error.localizedDescription)

@ -55,7 +55,7 @@ impl<R: Runtime> Opener<R> {
/// ///
/// ## Platform-specific: /// ## Platform-specific:
/// ///
/// - **Android / iOS**: Always opens using default program. /// - **Android / iOS**: Always opens using default program, unless `with` is provided as "inAppBrowser".
#[cfg(desktop)] #[cfg(desktop)]
pub fn open_url(&self, url: impl Into<String>, with: Option<impl Into<String>>) -> Result<()> { pub fn open_url(&self, url: impl Into<String>, with: Option<impl Into<String>>) -> Result<()> {
crate::open::open(url.into(), with.map(Into::into)) crate::open::open(url.into(), with.map(Into::into))
@ -78,11 +78,14 @@ impl<R: Runtime> Opener<R> {
/// ///
/// ## Platform-specific: /// ## Platform-specific:
/// ///
/// - **Android / iOS**: Always opens using default program. /// - **Android / iOS**: Always opens using default program, unless `with` is provided as "inAppBrowser".
#[cfg(mobile)] #[cfg(mobile)]
pub fn open_url(&self, url: impl Into<String>, _with: Option<impl Into<String>>) -> Result<()> { pub fn open_url(&self, url: impl Into<String>, with: Option<impl Into<String>>) -> Result<()> {
self.mobile_plugin_handle self.mobile_plugin_handle
.run_mobile_plugin("open", url.into()) .run_mobile_plugin(
"open",
serde_json::json!({ "url": url.into(), "with": with.map(Into::into) }),
)
.map_err(Into::into) .map_err(Into::into)
} }

Loading…
Cancel
Save