// Copyright (c) 2022 Vuplex Inc. All rights reserved. // // Licensed under the Vuplex Commercial Software Library License, you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // https://vuplex.com/commercial-library-license // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using System; using System.Threading.Tasks; using System.Timers; using UnityEngine; namespace Vuplex.WebView.Demos { /// /// Sets up the AdvancedWebViewDemo scene, which displays web content in a main /// world-space WebViewPrefab and then renders a UI in a second webview to display the current URL /// and provide back / forward navigation controls.

/// /// Note: The address bar currently only displays the current URL and is not an input. /// I plan to add a dedicated browser prefab in the future that will include /// a functional address bar. In the meantime, you can edit the CONTROLS_HTML field /// below to implement a URL input. ///
/// /// This scene demonstrates the following:
/// - Programmatically instantiating WebViewPrefabs at runtime
/// - Creating and hooking up an on-screen keyboard
/// - Using IWebView methods like LoadUrl, LoadHtml, GoBack, and GoForward
/// - Attaching handlers to the IWebView.UrlChanged and MessageEmitted events
/// - Message passing from C#-to-JavaScript and vice versa
/// - Creating a transparent webview using the transparent meta tag

/// /// Links:
/// - WebViewPrefab docs: https://developer.vuplex.com/webview/WebViewPrefab
/// - How clicking works: https://support.vuplex.com/articles/clicking
/// - Other examples: https://developer.vuplex.com/webview/overview#examples
///
class AdvancedWebViewDemo : MonoBehaviour { Timer _buttonRefreshTimer = new Timer(); WebViewPrefab _controlsWebViewPrefab; HardwareKeyboardListener _hardwareKeyboardListener; WebViewPrefab _mainWebViewPrefab; async void Start() { Debug.Log("[AdvancedWebViewDemo] Just a heads-up: this scene's address bar currently only displays the current URL and is not an input. For more info, please see the comments in AdvancedWebViewDemo.cs."); // Use a desktop User-Agent to request the desktop versions of websites. // https://developer.vuplex.com/webview/Web#SetUserAgent Web.SetUserAgent(false); // Create a 0.6 x 0.3 webview for the main web content. _mainWebViewPrefab = WebViewPrefab.Instantiate(0.6f, 0.3f); _mainWebViewPrefab.PixelDensity = 2; _mainWebViewPrefab.transform.parent = transform; _mainWebViewPrefab.transform.localPosition = new Vector3(0, -0.05f, 0.4f); _mainWebViewPrefab.transform.localEulerAngles = new Vector3(0, 180, 0); // Create a second webview above the first to show a UI that // displays the current URL and provides back / forward navigation buttons. _controlsWebViewPrefab = WebViewPrefab.Instantiate(0.6f, 0.05f); _controlsWebViewPrefab.transform.parent = _mainWebViewPrefab.transform; _controlsWebViewPrefab.transform.localPosition = new Vector3(0, 0.06f, 0); _controlsWebViewPrefab.transform.localEulerAngles = Vector3.zero; // Set up a timer to allow the state of the back / forward buttons to be // refreshed one second after a URL change occurs. _buttonRefreshTimer.AutoReset = false; _buttonRefreshTimer.Interval = 1000; _buttonRefreshTimer.Elapsed += ButtonRefreshTimer_Elapsed; _setUpKeyboards(); // Wait for both WebViewPrefabs to initialize, because the // WebViewPrefab.WebView property is null until the prefabs have initialized. await Task.WhenAll(new Task[] { _mainWebViewPrefab.WaitUntilInitialized(), _controlsWebViewPrefab.WaitUntilInitialized() }); // Now that the WebViewPrefabs are initialized, we can use the WebViewPrefab.WebView property. _mainWebViewPrefab.WebView.UrlChanged += MainWebView_UrlChanged; _mainWebViewPrefab.WebView.LoadUrl("https://www.google.com"); _controlsWebViewPrefab.WebView.MessageEmitted += Controls_MessageEmitted; _controlsWebViewPrefab.WebView.LoadHtml(CONTROLS_HTML); // Android Gecko and UWP w/ XR enabled don't support transparent webviews, so set the cutout // rect to the entire view so that the shader makes its black background pixels transparent. var pluginType = _controlsWebViewPrefab.WebView.PluginType; if (pluginType == WebPluginType.AndroidGecko || pluginType == WebPluginType.UniversalWindowsPlatform) { _controlsWebViewPrefab.SetCutoutRect(new Rect(0, 0, 1, 1)); } } void ButtonRefreshTimer_Elapsed(object sender, ElapsedEventArgs eventArgs) { // Get the main webview's back / forward state and then post a message // to the controls UI to update its buttons' state. Vuplex.WebView.Internal.Dispatcher.RunOnMainThread(async () => { var canGoBack = await _mainWebViewPrefab.WebView.CanGoBack(); var canGoForward = await _mainWebViewPrefab.WebView.CanGoForward(); var serializedMessage = $"{{ \"type\": \"SET_BUTTONS\", \"canGoBack\": {canGoBack.ToString().ToLowerInvariant()}, \"canGoForward\": {canGoForward.ToString().ToLowerInvariant()} }}"; _controlsWebViewPrefab.WebView.PostMessage(serializedMessage); }); } void Controls_MessageEmitted(object sender, EventArgs eventArgs) { if (eventArgs.Value == "CONTROLS_INITIALIZED") { // The controls UI won't be initialized in time to receive the first UrlChanged event, // so explicitly set the initial URL after the controls UI indicates it's ready. _setDisplayedUrl(_mainWebViewPrefab.WebView.Url); return; } var message = eventArgs.Value; if (message == "GO_BACK") { _mainWebViewPrefab.WebView.GoBack(); } else if (message == "GO_FORWARD") { _mainWebViewPrefab.WebView.GoForward(); } } void MainWebView_UrlChanged(object sender, UrlChangedEventArgs eventArgs) { _setDisplayedUrl(eventArgs.Url); _buttonRefreshTimer.Start(); } void _setDisplayedUrl(string url) { if (_controlsWebViewPrefab.WebView != null) { var serializedMessage = $"{{ \"type\": \"SET_URL\", \"url\": \"{url}\" }}"; _controlsWebViewPrefab.WebView.PostMessage(serializedMessage); } } async void _setUpKeyboards() { await _mainWebViewPrefab.WaitUntilInitialized(); // Send keys from the hardware (USB or Bluetooth) keyboard to the webview. // Use separate KeyDown() and KeyUp() methods if the webview supports // it, otherwise just use IWebView.SendKey(). // https://developer.vuplex.com/webview/IWithKeyDownAndUp var webViewWithKeyDownAndUp = _mainWebViewPrefab.WebView as IWithKeyDownAndUp; _hardwareKeyboardListener = HardwareKeyboardListener.Instantiate(); _hardwareKeyboardListener.KeyDownReceived += (sender, eventArgs) => { if (webViewWithKeyDownAndUp != null) { webViewWithKeyDownAndUp.KeyDown(eventArgs.Value, eventArgs.Modifiers); } else { _mainWebViewPrefab.WebView.SendKey(eventArgs.Value); } }; _hardwareKeyboardListener.KeyUpReceived += (sender, eventArgs) => { webViewWithKeyDownAndUp?.KeyUp(eventArgs.Value, eventArgs.Modifiers); }; // Also add an on-screen keyboard under the main webview. var keyboard = Keyboard.Instantiate(); keyboard.transform.SetParent(_mainWebViewPrefab.transform, false); keyboard.transform.localPosition = new Vector3(0, -0.31f, 0); keyboard.transform.localEulerAngles = Vector3.zero; keyboard.InputReceived += (sender, eventArgs) => { _mainWebViewPrefab.WebView.SendKey(eventArgs.Value); }; } const string CONTROLS_HTML = @"
"; } }