tauri-plugin-intent

Crates.iotauri-plugin-intent
lib.rstauri-plugin-intent
version0.1.0
created_at2025-09-01 07:01:13.348391+00
updated_at2025-09-01 07:01:13.348391+00
descriptionTauri plugin for handling Android and iOS intents.
homepagehttps://github.com/modeckrus/tauri-plugin-intent
repositoryhttps://github.com/modeckrus/tauri-plugin-intent
max_upload_size
id1819222
size172,023
Mukhanov Matthew (modeckrus)

documentation

https://docs.rs/tauri-plugin-intent

README

tauri-plugin-intent

crates.io docs.rs github

tauri-plugin-intent — это плагин для Tauri, который позволяет работать с Android Intent и открывать системные обработчики на десктопных платформах.


Tauri Plugin Intent

A Tauri plugin for opening Android Intents and system default handlers on desktop platforms.

Features

  • Android Intent Support: Open Android intents with full parameter support
  • Cross-platform: Fallback to system default handlers on desktop platforms
  • TypeScript Support: Full TypeScript definitions and convenience functions
  • Multiple Intent Types: Support for VIEW, SEND, CALL and other intent actions
  • Extras Support: Pass complex data through intent extras
  • Framework Integration: Works with any Tauri frontend framework (React, Vue, Svelte, Leptos, etc.)

Installation

1. Add the plugin to your Cargo.toml:

[dependencies]
tauri-plugin-intent = { git = "https://github.com/your-repo/tauri-plugin-intent" }

2. Initialize the plugin in your Tauri app:

fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_intent::init())
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

3. Add permissions to your capabilities:

{
  "identifier": "main",
  "description": "Capability for the main window",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "intent:default"
  ]
}

Usage

JavaScript/TypeScript API

Install the npm package (if using separate package):

npm install @tauri-apps/plugin-intent

Basic Usage

import { openIntent, openPhoneDialer, openUrl, sendEmail } from '@tauri-apps/plugin-intent';

// Open phone dialer
await openPhoneDialer('+1234567890');

// Open URL
await openUrl('https://example.com');

// Send email
await sendEmail({
  to: ['recipient@example.com'],
  subject: 'Hello',
  body: 'This is a test email'
});

// Custom intent
await openIntent({
  action: 'android.intent.action.VIEW',
  data: 'tel:+1234567890',
  flags: ['FLAG_ACTIVITY_NEW_TASK']
});

Advanced Usage with Extras

// Send SMS with custom message
await openIntent({
  action: 'android.intent.action.VIEW',
  data: 'sms:+1234567890',
  extras: {
    'sms_body': 'Hello from Tauri!'
  }
});

// Share content
await openIntent({
  action: 'android.intent.action.SEND',
  mimeType: 'text/plain',
  extras: {
    'android.intent.extra.TEXT': 'Check out this awesome app!',
    'android.intent.extra.SUBJECT': 'Amazing Discovery'
  }
});

Rust API

use tauri_plugin_intent::IntentExt;

#[tauri::command]
async fn open_custom_intent(app: tauri::AppHandle) -> Result<(), String> {
    let request = tauri_plugin_intent::OpenIntentRequest {
        action: "android.intent.action.VIEW".to_string(),
        data: Some("https://example.com".to_string()),
        category: None,
        mime_type: None,
        package_name: None,
        class_name: None,
        flags: None,
        extras: None,
    };
    
    let response = app.intent().open_intent(request).await
        .map_err(|e| e.to_string())?;
    
    if !response.success {
        return Err(response.error.unwrap_or("Unknown error".to_string()));
    }
    
    Ok(())
}

Framework Integration

Leptos Integration

For Leptos applications, you can use the JavaScript API with wasm-bindgen and serde-wasm-bindgen:

Add dependencies to your Cargo.toml:

[dependencies]
leptos = { version = "0.8", features = ["csr"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde-wasm-bindgen = "0.6"
wasm-bindgen = "0.2"
js-sys = "0.3"

Example Leptos component:

use leptos::task::spawn_local;
use leptos::{ev::SubmitEvent, prelude::*};
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"])]
    async fn invoke(cmd: &str, args: JsValue) -> JsValue;
}

#[derive(Serialize, Deserialize)]
struct OpenIntentArgs {
    payload: OpenIntentPayload,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct OpenIntentPayload {
    action: String,
    data: Option<String>,
    // ... other fields
}

#[derive(Serialize, Deserialize)]
struct OpenIntentResponse {
    success: bool,
    error: Option<String>,
}

#[component]
pub fn IntentExample() -> impl IntoView {
    let (phone_number, set_phone_number) = signal(String::new());
    let (result_message, set_result_message) = signal(String::new());

    let open_dialer = move |ev: SubmitEvent| {
        ev.prevent_default();
        spawn_local(async move {
            let number = phone_number.get_untracked();
            if number.is_empty() { return; }

            let payload = OpenIntentPayload {
                action: "android.intent.action.VIEW".to_string(),
                data: Some(format!("tel:{}", number)),
                // ... other fields
            };

            let args = serde_wasm_bindgen::to_value(&OpenIntentArgs { payload }).unwrap();

            match serde_wasm_bindgen::from_value::<OpenIntentResponse>(
                invoke("plugin:intent|open_intent", args).await,
            ) {
                Ok(response) => {
                    if response.success {
                        set_result_message.set("Phone dialer opened!".to_string());
                    } else {
                        set_result_message.set(format!("Error: {}", 
                            response.error.unwrap_or("Unknown error".to_string())));
                    }
                }
                Err(e) => {
                    set_result_message.set(format!("Failed: {:?}", e));
                }
            }
        });
    };

    view! {
        <div>
            <h2>"Phone Dialer"</h2>
            <form on:submit=open_dialer>
                <input
                    placeholder="Enter phone number..."
                    on:input=move |ev| set_phone_number.set(event_target_value(&ev))
                />
                <button type="submit">"Open Dialer"</button>
            </form>
            <p>{move || result_message.get()}</p>
        </div>
    }
}

React/Next.js Integration

import { openPhoneDialer } from '@tauri-apps/plugin-intent';

function PhoneDialer() {
  const [phoneNumber, setPhoneNumber] = useState('');
  const [message, setMessage] = useState('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      const result = await openPhoneDialer(phoneNumber);
      if (result.success) {
        setMessage('Phone dialer opened successfully!');
      } else {
        setMessage(`Error: ${result.error}`);
      }
    } catch (error) {
      setMessage(`Failed to open dialer: ${error}`);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="tel"
        value={phoneNumber}
        onChange={(e) => setPhoneNumber(e.target.value)}
        placeholder="Enter phone number..."
      />
      <button type="submit">Open Dialer</button>
      {message && <p>{message}</p>}
    </form>
  );
}

Supported Intent Actions

Android

  • android.intent.action.VIEW - Open URLs, phone numbers, etc.
  • android.intent.action.SEND - Send content to other apps
  • android.intent.action.CALL - Initiate phone calls
  • android.intent.action.SENDTO - Send to specific targets
  • android.intent.action.DIAL - Open phone dialer
  • android.intent.action.EDIT - Edit content
  • Custom actions supported

Desktop Fallback

  • android.intent.action.VIEW - Opens URLs with system default handler
  • Other actions return appropriate error messages

Intent Parameters

Parameter Type Description
action string The intent action (required)
data string? The intent data URI
category string? Intent category
mimeType string? MIME type for the intent
packageName string? Target package name
className string? Target class name
flags string[]? Intent flags
extras Record<string, any>? Additional data

Supported Intent Flags

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_CLEAR_TASK
  • FLAG_ACTIVITY_SINGLE_TOP
  • FLAG_ACTIVITY_NO_HISTORY
  • FLAG_ACTIVITY_MULTIPLE_TASK

Common Use Cases

Phone and SMS

// Open phone dialer
await openIntent({
  action: 'android.intent.action.VIEW',
  data: 'tel:+1234567890'
});

// Send SMS
await openIntent({
  action: 'android.intent.action.VIEW',
  data: 'sms:+1234567890',
  extras: { 'sms_body': 'Hello!' }
});

// Make a phone call (requires CALL_PHONE permission)
await openIntent({
  action: 'android.intent.action.CALL',
  data: 'tel:+1234567890'
});

Web and Maps

// Open website
await openUrl('https://example.com');

// Open location in maps
await openIntent({
  action: 'android.intent.action.VIEW',
  data: 'geo:37.7749,-122.4194?q=San+Francisco'
});

// Open maps with specific app
await openIntent({
  action: 'android.intent.action.VIEW',
  data: 'google.navigation:q=San+Francisco',
  packageName: 'com.google.android.apps.maps'
});

Email and Sharing

// Send email using mailto
await openIntent({
  action: 'android.intent.action.VIEW',
  data: 'mailto:test@example.com?subject=Hello&body=Message'
});

// Share text
await openIntent({
  action: 'android.intent.action.SEND',
  mimeType: 'text/plain',
  extras: {
    'android.intent.extra.TEXT': 'Check this out!',
    'android.intent.extra.SUBJECT': 'Amazing Content'
  }
});

// Share image (Android)
await openIntent({
  action: 'android.intent.action.SEND',
  mimeType: 'image/*',
  extras: {
    'android.intent.extra.STREAM': 'file:///path/to/image.jpg'
  }
});

Error Handling

All functions return a Promise<OpenIntentResponse>:

interface OpenIntentResponse {
  success: boolean;
  error?: string;
}

Always check the success field:

const result = await openPhoneDialer('+1234567890');
if (!result.success) {
  console.error('Failed to open dialer:', result.error);
  // Handle error appropriately
}

Platform Support

  • Android (Full intent support)
  • iOS (Limited support via system handlers)
  • Windows (System default handlers)
  • macOS (System default handlers)
  • Linux (System default handlers)

Android Permissions

For certain intents, you may need to add permissions to your AndroidManifest.xml:

<!-- For phone calls -->
<uses-permission android:name="android.permission.CALL_PHONE" />

<!-- For sending SMS -->
<uses-permission android:name="android.permission.SEND_SMS" />

<!-- For camera access -->
<uses-permission android:name="android.permission.CAMERA" />

<!-- For location access -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Development

Running the Example

The plugin includes a Leptos example demonstrating various intent operations:

cd examples/leptos-example
cargo tauri dev

Building the Plugin

# Build Rust code
cargo build

# Build JavaScript/TypeScript API
npm run build

# Run tests
cargo test

Project Structure

tauri-plugin-intent/
├── src/                    # Rust plugin code
├── android/                # Android-specific implementation
├── guest-js/               # JavaScript/TypeScript API
├── examples/               # Example applications
│   └── leptos-example/     # Leptos integration example
├── permissions/            # Plugin permissions
└── README.md              # This file

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Troubleshooting

Command not found errors

Ensure you have:

  1. Added the plugin to your Cargo.toml
  2. Initialized the plugin in your main.rs
  3. Added intent:default to your capabilities
  4. Rebuilt your application after adding the plugin

Intent not opening on Android

  • Check that the target application is installed
  • Verify the intent action and data format
  • Ensure required permissions are added to AndroidManifest.xml
  • Check Android logs: adb logcat | grep Intent

Desktop limitations

Desktop platforms have limited intent support:

  • Only android.intent.action.VIEW with URLs is supported
  • Other actions will return error messages
  • Use platform-specific APIs for advanced desktop integration

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Built with Tauri
  • Inspired by Android's Intent system
  • Thanks to the Tauri community for feedback and contributions
Commit count: 3

cargo fmt