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}