Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changes/android-app-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": minor:feat
---

Added mobile app plugin to support exit and back button press event.
5 changes: 5 additions & 0 deletions .changes/back-button-press-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tauri-apps/api": minor:feat
---

Added `app > onBackButtonPress` for Android back button handling.
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/tauri-runtime-wry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
wry = { version = "0.53.2", default-features = false, features = [
wry = { version = "0.53.4", default-features = false, features = [
"drag-drop",
"protocol",
"os-webview",
Expand Down
1 change: 1 addition & 0 deletions crates/tauri/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
("set_app_theme", false),
("set_dock_visibility", false),
("bundle_type", true),
("register_listener", true),
],
),
(
Expand Down
1 change: 1 addition & 0 deletions crates/tauri/mobile/android-codegen/TauriActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import app.tauri.plugin.PluginManager

abstract class TauriActivity : WryActivity() {
var pluginManager: PluginManager = PluginManager(this)
override val handleBackNavigation: Boolean = false

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

package app.tauri

import android.app.Activity
import android.webkit.WebView
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import app.tauri.annotation.Command
import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.Plugin
import app.tauri.plugin.Invoke
import app.tauri.plugin.JSObject

@TauriPlugin
class AppPlugin(private val activity: Activity): Plugin(activity) {
private val BACK_BUTTON_EVENT = "back-button"

private var webView: WebView? = null

override fun load(webView: WebView) {
this.webView = webView
}

init {
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (!hasListener(BACK_BUTTON_EVENT)) {
if ([email protected]?.canGoBack() == true) {
[email protected]!!.goBack()
}
} else {
val data = JSObject().apply {
put("canGoBack", [email protected]?.canGoBack() ?: false)
}
trigger(BACK_BUTTON_EVENT, data)
}
}
}
(activity as AppCompatActivity).onBackPressedDispatcher.addCallback(activity, callback)
}

@Command
fun exit(invoke: Invoke) {
invoke.resolve()
activity.finish()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.webkit.WebView
import androidx.activity.result.IntentSenderRequest
import androidx.core.app.ActivityCompat
Expand All @@ -22,7 +21,6 @@ import app.tauri.annotation.InvokeArg
import app.tauri.annotation.PermissionCallback
import app.tauri.annotation.TauriPlugin
import com.fasterxml.jackson.databind.ObjectMapper
import org.json.JSONException
import java.util.*
import java.util.concurrent.CopyOnWriteArrayList

Expand Down Expand Up @@ -148,6 +146,10 @@ abstract class Plugin(private val activity: Activity) {
}
}

fun hasListener(event: String): Boolean {
return !listeners[event].isNullOrEmpty()
}

@Command
open fun registerListener(invoke: Invoke) {
val args = invoke.parseArgs(RegisterListenerArgs::class.java)
Expand Down
27 changes: 27 additions & 0 deletions crates/tauri/permissions/app/autogenerated/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Default permissions for the plugin.
- `allow-tauri-version`
- `allow-identifier`
- `allow-bundle-type`
- `allow-register-listener`

## Permission Table

Expand Down Expand Up @@ -204,6 +205,32 @@ Denies the name command without any pre-configured scope.
<tr>
<td>

`core:app:allow-register-listener`

</td>
<td>

Enables the register_listener command without any pre-configured scope.

</td>
</tr>

<tr>
<td>

`core:app:deny-register-listener`

</td>
<td>

Denies the register_listener command without any pre-configured scope.

</td>
</tr>

<tr>
<td>

`core:app:allow-remove-data-store`

</td>
Expand Down
2 changes: 1 addition & 1 deletion crates/tauri/scripts/bundle.global.js

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions crates/tauri/src/app/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,5 +132,16 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
set_dock_visibility,
bundle_type,
])
.setup(|_app, _api| {
#[cfg(target_os = "android")]
{
let handle = _api.register_android_plugin("app.tauri", "AppPlugin")?;
_app.manage(AppPlugin(handle));
}
Ok(())
})
.build()
}

#[cfg(target_os = "android")]
pub(crate) struct AppPlugin<R: Runtime>(pub crate::plugin::PluginHandle<R>);
5 changes: 1 addition & 4 deletions crates/tauri/src/path/android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@

use super::Result;
use crate::{plugin::PluginHandle, Runtime};
use std::{
ffi::OsStr,
path::{Path, PathBuf},
};
use std::path::{Path, PathBuf};

/// A helper class to access the mobile path APIs.
pub struct PathResolver<R: Runtime>(pub(crate) PluginHandle<R>);
Expand Down
28 changes: 26 additions & 2 deletions packages/api/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

import { invoke } from './core'
import { addPluginListener, invoke, PluginListener } from './core'
import { Image } from './image'
import { Theme } from './window'

Expand Down Expand Up @@ -252,6 +252,28 @@ async function getBundleType(): Promise<BundleType> {
return invoke('plugin:app|bundle_type')
}

/**
* Payload for the onBackButtonPress event.
*/
type OnBackButtonPressPayload = {
/** Whether the webview canGoBack property is true. */
canGoBack: boolean
}

/**
* Listens to the backButton event on Android.
* @param handler
*/
async function onBackButtonPress(
handler: (payload: OnBackButtonPressPayload) => void
): Promise<PluginListener> {
return addPluginListener<OnBackButtonPressPayload>(
'app',
'back-button',
handler
)
}

export {
getName,
getVersion,
Expand All @@ -264,5 +286,7 @@ export {
fetchDataStoreIdentifiers,
removeDataStore,
setDockVisibility,
getBundleType
getBundleType,
type OnBackButtonPressPayload,
onBackButtonPress
}
Loading