termux_gui/components/
web_view.rs

1//! WebView component for displaying web content
2//!
3//! The WebView component allows you to display web pages and HTML content within your application,
4//! with support for loading URLs, executing JavaScript, and more.
5//!
6//! # Important Usage Notes
7//!
8//! When displaying HTML content with dynamic effects, **you must enable JavaScript first**:
9//!
10//! ```no_run
11//! use termux_gui_rust_demo::prelude::*;
12//!
13//! let mut activity = Activity::new()?;
14//! let layout = LinearLayout::new(&mut activity, None, true)?;
15//! let webview = WebView::new(&mut activity, Some(layout.id()))?;
16//!
17//! // ⚠️ Critical: Enable JavaScript first, or dynamic HTML content won't display
18//! webview.allow_javascript(&mut activity, true)?;
19//!
20//! // Set HTML content
21//! let html = r#"
22//!     <html>
23//!     <body style="background: purple;">
24//!         <h1>Hello WebView!</h1>
25//!     </body>
26//!     </html>
27//! "#;
28//! webview.set_data(&mut activity, html)?;
29//!
30//! // Or load an external webpage
31//! webview.load_uri(&mut activity, "https://www.google.com")?;
32//! # Ok::<(), termux_gui_rust_demo::error::GuiError>(())
33//! ```
34
35use serde_json::json;
36use crate::activity::Activity;
37use crate::view::View;
38use crate::error::Result;
39
40/// WebView component for displaying web content
41///
42/// # Important Usage Notes
43///
44/// 1. **JavaScript Support**: If your HTML content contains JavaScript or dynamic effects,
45///    you must call `allow_javascript()` first to enable JavaScript, otherwise you may see
46///    a blank page.
47///
48/// 2. **HTML Content Display Order**:
49///    ```no_run
50///    # use termux_gui_rust_demo::prelude::*;
51///    # let mut activity = Activity::new()?;
52///    # let webview = WebView::new(&mut activity, None)?;
53///    // Step 1: Enable JavaScript (if needed)
54///    webview.allow_javascript(&mut activity, true)?;
55///    
56///    // Step 2: Set HTML content
57///    webview.set_data(&mut activity, "<html>...</html>")?;
58///    # Ok::<(), termux_gui_rust_demo::error::GuiError>(())
59///    ```
60///
61/// 3. **Loading External URLs**: Simply call `load_uri()` - no special order required
62///
63/// # Examples
64///
65/// ```no_run
66/// use termux_gui_rust_demo::prelude::*;
67///
68/// let mut activity = Activity::new()?;
69/// let webview = WebView::new(&mut activity, None)?;
70///
71/// // Method 1: Display HTML content (requires JavaScript to be enabled first)
72/// webview.allow_javascript(&mut activity, true)?;
73/// webview.set_data(&mut activity, "<html><body><h1>Hello</h1></body></html>")?;
74///
75/// // Method 2: Load a webpage
76/// webview.load_uri(&mut activity, "https://www.example.com")?;
77/// # Ok::<(), termux_gui_rust_demo::error::GuiError>(())
78/// ```
79pub struct WebView {
80    view: View,
81    aid: i64,
82}
83
84impl WebView {
85    /// Creates a new WebView
86    ///
87    /// # Arguments
88    /// - `activity`: Reference to the Activity
89    /// - `parent`: Optional parent view ID
90    ///
91    /// # Examples
92    /// ```no_run
93    /// # use termux_gui_rust_demo::prelude::*;
94    /// # let mut activity = Activity::new()?;
95    /// # let layout_id = 0;
96    /// let webview = WebView::new(&mut activity, Some(layout_id))?;
97    /// # Ok::<(), termux_gui_rust_demo::error::GuiError>(())
98    /// ```
99    pub fn new(activity: &mut Activity, parent: Option<i64>) -> Result<Self> {
100        eprintln!("[DEBUG] WebView::new() - creating WebView...");
101        
102        let mut params = json!({
103            "aid": activity.id()
104        });
105        
106        if let Some(parent_id) = parent {
107            params["parent"] = json!(parent_id);
108        }
109        
110        eprintln!("[DEBUG] WebView::new() - sending createWebView...");
111        let response = activity.send_read(&json!({
112            "method": "createWebView",
113            "params": params
114        }))?;
115        
116        eprintln!("[DEBUG] WebView::new() - got response: {:?}", response);
117        
118        let id = response
119            .as_i64()
120            .ok_or_else(|| crate::error::GuiError::InvalidResponse("Invalid id".to_string()))?;
121        
122        Ok(WebView {
123            view: View::new(id),
124            aid: activity.id(),
125        })
126    }
127    
128    /// Gets the view ID
129    pub fn id(&self) -> i64 {
130        self.view.id()
131    }
132    
133    /// Gets a reference to the underlying View
134    pub fn view(&self) -> &View {
135        &self.view
136    }
137    
138    /// Loads a URI/URL
139    ///
140    /// # Arguments
141    /// - `activity`: Reference to the Activity
142    /// - `uri`: The URL to load, e.g., "https://www.google.com"
143    ///
144    /// # Examples
145    /// ```no_run
146    /// # use termux_gui_rust_demo::prelude::*;
147    /// # let mut activity = Activity::new()?;
148    /// # let webview = WebView::new(&mut activity, None)?;
149    /// webview.load_uri(&mut activity, "https://www.google.com")?;
150    /// # Ok::<(), termux_gui_rust_demo::error::GuiError>(())
151    /// ```
152    pub fn load_uri(&self, activity: &mut Activity, uri: &str) -> Result<()> {
153        activity.send(&json!({
154            "method": "loadURI",
155            "params": {
156                "aid": self.aid,
157                "id": self.view.id(),
158                "uri": uri
159            }
160        }))?;
161        Ok(())
162    }
163    
164    /// Sets HTML content
165    ///
166    /// ⚠️ **Important**: If the HTML contains JavaScript or dynamic effects, you must call
167    /// `allow_javascript(true)` first, otherwise you may see a blank page.
168    ///
169    /// # Arguments
170    /// - `activity`: Reference to the Activity
171    /// - `data`: The HTML document content
172    ///
173    /// # Examples
174    /// ```no_run
175    /// # use termux_gui_rust_demo::prelude::*;
176    /// # let mut activity = Activity::new()?;
177    /// # let webview = WebView::new(&mut activity, None)?;
178    /// // If HTML contains JavaScript, enable it first
179    /// webview.allow_javascript(&mut activity, true)?;
180    /// 
181    /// // Then set the HTML content
182    /// webview.set_data(&mut activity, "<html><body><h1>Hello</h1></body></html>")?;
183    /// # Ok::<(), termux_gui_rust_demo::error::GuiError>(())
184    /// ```
185    pub fn set_data(&self, activity: &mut Activity, data: &str) -> Result<()> {
186        // Use base64 encoding to support HTML content with non-ASCII characters
187        let encoded = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, data.as_bytes());
188        
189        activity.send(&json!({
190            "method": "setData",
191            "params": {
192                "aid": self.aid,
193                "id": self.view.id(),
194                "doc": encoded,
195                "base64": true
196            }
197        }))?;
198        Ok(())
199    }
200    
201    /// Allows JavaScript execution
202    ///
203    /// ⚠️ **Important**: When displaying HTML with JavaScript or dynamic effects, you must
204    /// call this method first to enable JavaScript.
205    ///
206    /// If JavaScript is requested to be enabled, a user confirmation dialog will appear,
207    /// and the user can deny the request. This method blocks until the user responds.
208    ///
209    /// # Arguments
210    /// - `activity`: Reference to the Activity
211    /// - `allow`: Whether to allow JavaScript
212    ///
213    /// # Returns
214    /// Returns whether JavaScript is enabled after the call (if the user denies,
215    /// it will return false even if you passed true)
216    ///
217    /// # Examples
218    /// ```no_run
219    /// # use termux_gui_rust_demo::prelude::*;
220    /// # let mut activity = Activity::new()?;
221    /// # let webview = WebView::new(&mut activity, None)?;
222    /// // Enable JavaScript (requires user confirmation)
223    /// let enabled = webview.allow_javascript(&mut activity, true)?;
224    /// if enabled {
225    ///     println!("JavaScript enabled");
226    ///     // Now you can set HTML with JavaScript
227    ///     webview.set_data(&mut activity, "<html><body><script>alert('Hi!');</script></body></html>")?;
228    /// }
229    /// # Ok::<(), termux_gui_rust_demo::error::GuiError>(())
230    /// ```
231    pub fn allow_javascript(&self, activity: &mut Activity, allow: bool) -> Result<bool> {
232        let response = activity.send_read(&json!({
233            "method": "allowJavascript",
234            "params": {
235                "aid": self.aid,
236                "id": self.view.id(),
237                "allow": allow
238            }
239        }))?;
240        
241        Ok(response.as_bool().unwrap_or(false))
242    }
243    
244    /// Allows loading content from content:// URIs
245    ///
246    /// # Arguments
247    /// - `activity`: Reference to the Activity
248    /// - `allow`: Whether to allow loading from content URIs
249    pub fn allow_content_uri(&self, activity: &mut Activity, allow: bool) -> Result<()> {
250        activity.send(&json!({
251            "method": "allowContentURI",
252            "params": {
253                "aid": self.aid,
254                "id": self.view.id(),
255                "allow": allow
256            }
257        }))?;
258        Ok(())
259    }
260    
261    /// Allows navigation to different sites
262    ///
263    /// # Arguments
264    /// - `activity`: Reference to the Activity
265    /// - `allow`: Whether to allow users and JavaScript to navigate to different sites
266    pub fn allow_navigation(&self, activity: &mut Activity, allow: bool) -> Result<()> {
267        activity.send(&json!({
268            "method": "allowNavigation",
269            "params": {
270                "aid": self.aid,
271                "id": self.view.id(),
272                "allow": allow
273            }
274        }))?;
275        Ok(())
276    }
277    
278    /// Executes JavaScript code in the WebView
279    ///
280    /// ⚠️ **Prerequisite**: You must enable JavaScript via `allow_javascript(true)` first,
281    /// otherwise the code will not execute.
282    ///
283    /// # Arguments
284    /// - `activity`: Reference to the Activity
285    /// - `code`: The JavaScript code to execute
286    ///
287    /// # Examples
288    /// ```no_run
289    /// # use termux_gui_rust_demo::prelude::*;
290    /// # let mut activity = Activity::new()?;
291    /// # let webview = WebView::new(&mut activity, None)?;
292    /// // Step 1: Enable JavaScript
293    /// webview.allow_javascript(&mut activity, true)?;
294    ///
295    /// // Step 2: Execute JavaScript code
296    /// webview.evaluate_js(&mut activity, "document.body.style.background = 'red';")?;
297    /// # Ok::<(), termux_gui_rust_demo::error::GuiError>(())
298    /// ```
299    pub fn evaluate_js(&self, activity: &mut Activity, code: &str) -> Result<()> {
300        activity.send(&json!({
301            "method": "evaluateJS",
302            "params": {
303                "aid": self.aid,
304                "id": self.view.id(),
305                "code": code
306            }
307        }))?;
308        Ok(())
309    }
310    
311    /// Goes back to the previous page in history
312    pub fn go_back(&self, activity: &mut Activity) -> Result<()> {
313        activity.send(&json!({
314            "method": "goBack",
315            "params": {
316                "aid": self.aid,
317                "id": self.view.id()
318            }
319        }))?;
320        Ok(())
321    }
322    
323    /// Goes forward to the next page in history
324    pub fn go_forward(&self, activity: &mut Activity) -> Result<()> {
325        activity.send(&json!({
326            "method": "goForward",
327            "params": {
328                "aid": self.aid,
329                "id": self.view.id()
330            }
331        }))?;
332        Ok(())
333    }
334}