// 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 UnityEngine; using Vuplex.WebView.Internal; namespace Vuplex.WebView { public abstract class BaseKeyboard : MonoBehaviour { /// /// Indicates that the user clicked a key on the keyboard. /// public event EventHandler> InputReceived; /// /// Indicates that the keyboard finished initializing. /// public event EventHandler Initialized; /// /// If you want to load a customized version of the Keyboard UI, you can /// do so by setting this field. For example, you could load a customized /// Keyboard UI from StreamingAssets by using a URL like "streaming-assets://keyboard/index.html". /// [Label("Custom Keyboard URL (optional)")] [Tooltip("If you want to load a customized version of the Keyboard UI, you can do so by setting this field. For example, you could load a customized Keyboard UI from StreamingAssets by using a URL like \"streaming-assets://keyboard/index.html\".")] public string CustomKeyboardUrl; /// /// Returns a task that completes when the keyboard is initialized, /// which means that its WebViewPrefab property is ready for use. /// /// /// /// await keyboard.WaitUntilInitialized(); /// keyboard.WebViewPrefab.Clicked += (sender, eventArgs) => { /// Debug.Log("Keyboard was clicked"); /// }; /// /// public Task WaitUntilInitialized() { var taskSource = new TaskCompletionSource(); if (_isInitialized) { taskSource.SetResult(true); } else { Initialized += (sender, e) => taskSource.SetResult(true); } return taskSource.Task; } bool _isInitialized; [SerializeField] [HideInInspector] protected BaseWebViewPrefab _webViewPrefab; protected static readonly WebViewOptions _webViewOptions = new WebViewOptions { clickWithoutStealingFocus = true, disableVideo = true, // If both Android plugins are installed, prefer the original Chromium // plugin for the keyboard, since the Gecko plugin doesn't support // transparent backgrounds. preferredPlugins = new WebPluginType[] { WebPluginType.Android } }; async protected void _init() { _webViewPrefab.CursorIconsEnabled = false; // Reset InitialUrl to null in case the developer modified WebViewPrefab.prefab to set a default InitialUrl. _webViewPrefab.InitialUrl = null; await _webViewPrefab.WaitUntilInitialized(); var pluginType = _webViewPrefab.WebView.PluginType; if (pluginType == WebPluginType.AndroidGecko) { // On Android Gecko, hovering steals focus. _webViewPrefab.HoveringEnabled = false; } // Scrolling and dragging can also cause the keyboard // to steal focus on Android Gecko, so just disable them. _webViewPrefab.ScrollingEnabled = false; _webViewPrefab.DragMode = DragMode.Disabled; _webViewPrefab.WebView.MessageEmitted += WebView_MessageEmitted; // Android Gecko and Hololens don't support transparent webviews, so set the cutout // rect to the entire view so that the shader makes its black background // pixels transparent. if (pluginType == WebPluginType.AndroidGecko || pluginType == WebPluginType.UniversalWindowsPlatform) { _webViewPrefab.SetCutoutRect(new Rect(0, 0, 1, 1)); } if (!String.IsNullOrWhiteSpace(CustomKeyboardUrl)) { _webViewPrefab.WebView.LoadUrl(CustomKeyboardUrl.Trim()); } else { _webViewPrefab.WebView.LoadHtml(KeyboardUi.Html); } } void WebView_MessageEmitted(object sender, EventArgs e) { var serializedMessage = e.Value; var messageType = JsonUtility.FromJson(serializedMessage).type; switch (messageType) { case "keyboard.inputReceived": var input = StringBridgeMessage.ParseValue(serializedMessage); InputReceived?.Invoke(this, new EventArgs(input)); break; case "keyboard.initialized": _sendKeyboardLanguageMessage(); _isInitialized = true; Initialized?.Invoke(this, EventArgs.Empty); break; } } string _getKeyboardLanguage() { switch (Application.systemLanguage) { case SystemLanguage.Danish: return "da"; case SystemLanguage.French: return "fr"; case SystemLanguage.German: return "de"; case SystemLanguage.Norwegian: return "no"; case SystemLanguage.Russian: return "ru"; case SystemLanguage.Spanish: return "es"; case SystemLanguage.Swedish: return "sv"; default: return "en"; } } /// /// Initializes the keyboard language based on the system language. /// void _sendKeyboardLanguageMessage() { var message = new StringBridgeMessage { type = "keyboard.setLanguage", value = _getKeyboardLanguage() }; var serializedMessage = JsonUtility.ToJson(message); _webViewPrefab.WebView.PostMessage(serializedMessage); } protected static void _setLayerRecursively(GameObject gameObject, int layer) { if (gameObject == null) { return; } gameObject.layer = layer; foreach (Transform child in gameObject.transform) { if (child != null) { _setLayerRecursively(child.gameObject, layer); } } } } }