BaseKeyboard.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // Copyright (c) 2024 Vuplex Inc. All rights reserved.
  2. //
  3. // Licensed under the Vuplex Commercial Software Library License, you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // a copy of the License at
  6. //
  7. // https://vuplex.com/commercial-library-license
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #pragma warning disable CS0067
  15. using System;
  16. using System.Threading.Tasks;
  17. using UnityEngine;
  18. using Vuplex.WebView.Internal;
  19. namespace Vuplex.WebView {
  20. public abstract class BaseKeyboard : MonoBehaviour {
  21. /// <summary>
  22. /// Indicates that the user pressed a key on the keyboard.
  23. /// </summary>
  24. public event EventHandler<EventArgs<string>> KeyPressed;
  25. /// <summary>
  26. /// Indicates that the keyboard finished initializing.
  27. /// </summary>
  28. public event EventHandler Initialized;
  29. /// <summary>
  30. /// If you want to load a customized version of the Keyboard UI, you can
  31. /// do so by setting this field. For example, you could load a customized
  32. /// Keyboard UI from StreamingAssets by using a URL like "streaming-assets://keyboard/index.html".
  33. /// <summary>
  34. [Label("Custom Keyboard URL (optional)")]
  35. [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\".")]
  36. public string CustomKeyboardUrl;
  37. /// <summary>
  38. /// Returns a task that completes when the keyboard is initialized,
  39. /// which means that its WebViewPrefab property is ready for use.
  40. /// </summary>
  41. /// <example>
  42. /// <code>
  43. /// await keyboard.WaitUntilInitialized();
  44. /// keyboard.WebViewPrefab.Clicked += (sender, eventArgs) => {
  45. /// Debug.Log("Keyboard was clicked");
  46. /// };
  47. /// </code>
  48. /// </example>
  49. public Task WaitUntilInitialized() {
  50. var taskSource = new TaskCompletionSource<bool>();
  51. if (_isInitialized) {
  52. taskSource.SetResult(true);
  53. } else {
  54. Initialized += (sender, e) => taskSource.SetResult(true);
  55. }
  56. return taskSource.Task;
  57. }
  58. internal BaseWebViewPrefab BaseWebViewPrefab { get => _webViewPrefab; }
  59. bool _isInitialized;
  60. [SerializeField]
  61. [HideInInspector]
  62. protected BaseWebViewPrefab _webViewPrefab;
  63. protected static readonly WebViewOptions _webViewOptions = new WebViewOptions {
  64. clickWithoutStealingFocus = true,
  65. disableVideo = true,
  66. // If both Android plugins are installed, prefer the original Chromium
  67. // plugin for the keyboard, since the Gecko plugin doesn't support
  68. // transparent backgrounds.
  69. preferredPlugins = new WebPluginType[] { WebPluginType.Android }
  70. };
  71. async protected void _init() {
  72. _webViewPrefab.CursorIconsEnabled = false;
  73. _webViewPrefab.KeyboardEnabled = false;
  74. // Reset InitialUrl to null in case the developer modified WebViewPrefab.prefab to set a default InitialUrl.
  75. _webViewPrefab.InitialUrl = null;
  76. await _webViewPrefab.WaitUntilInitialized();
  77. var pluginType = _webViewPrefab.WebView.PluginType;
  78. if (pluginType == WebPluginType.AndroidGecko) {
  79. // On Android Gecko, hovering steals focus.
  80. _webViewPrefab.HoveringEnabled = false;
  81. }
  82. // Scrolling and dragging can also cause the keyboard
  83. // to steal focus on Android Gecko, so just disable them.
  84. _webViewPrefab.ScrollingEnabled = false;
  85. _webViewPrefab.DragMode = DragMode.Disabled;
  86. _webViewPrefab.WebView.MessageEmitted += WebView_MessageEmitted;
  87. // Android Gecko and Hololens don't support transparent webviews, so set the cutout
  88. // rect to the entire view so that the shader makes its black background
  89. // pixels transparent.
  90. if (pluginType == WebPluginType.AndroidGecko || pluginType == WebPluginType.UniversalWindowsPlatform) {
  91. _webViewPrefab.SetCutoutRect(new Rect(0, 0, 1, 1));
  92. }
  93. if (!String.IsNullOrWhiteSpace(CustomKeyboardUrl)) {
  94. _webViewPrefab.WebView.LoadUrl(CustomKeyboardUrl.Trim());
  95. } else {
  96. _webViewPrefab.WebView.LoadHtml(KeyboardUI.Html);
  97. }
  98. }
  99. void OnDestroy() {
  100. var keyboardInstance = Internal.KeyboardManager.Instance;
  101. if (keyboardInstance != null) {
  102. keyboardInstance.RemoveKeyboard(this);
  103. }
  104. }
  105. void WebView_MessageEmitted(object sender, EventArgs<string> e) {
  106. var serializedMessage = e.Value;
  107. var messageType = JsonUtility.FromJson<BridgeMessage>(serializedMessage).type;
  108. switch (messageType) {
  109. case "keyboard.inputReceived":
  110. var input = StringBridgeMessage.ParseValue(serializedMessage);
  111. KeyPressed?.Invoke(this, new EventArgs<string>(input));
  112. break;
  113. case "keyboard.initialized":
  114. _sendKeyboardLanguageMessage();
  115. _isInitialized = true;
  116. Internal.KeyboardManager.Instance.AddKeyboard(this);
  117. Initialized?.Invoke(this, EventArgs.Empty);
  118. break;
  119. }
  120. }
  121. string _getKeyboardLanguage() {
  122. switch (Application.systemLanguage) {
  123. case SystemLanguage.Danish:
  124. return "da";
  125. case SystemLanguage.French:
  126. return "fr";
  127. case SystemLanguage.German:
  128. return "de";
  129. case SystemLanguage.Italian:
  130. return "it";
  131. case SystemLanguage.Norwegian:
  132. return "no";
  133. case SystemLanguage.Russian:
  134. return "ru";
  135. case SystemLanguage.Spanish:
  136. return "es";
  137. case SystemLanguage.Swedish:
  138. return "sv";
  139. default:
  140. return "en";
  141. }
  142. }
  143. /// <summary>
  144. /// Initializes the keyboard language based on the system language.
  145. /// </summary>
  146. void _sendKeyboardLanguageMessage() {
  147. var message = new StringBridgeMessage {
  148. type = "keyboard.setLanguage",
  149. value = _getKeyboardLanguage()
  150. };
  151. var serializedMessage = JsonUtility.ToJson(message);
  152. _webViewPrefab.WebView.PostMessage(serializedMessage);
  153. }
  154. protected static void _setLayerRecursively(GameObject gameObject, int layer) {
  155. if (gameObject == null) {
  156. return;
  157. }
  158. gameObject.layer = layer;
  159. foreach (Transform child in gameObject.transform) {
  160. if (child != null) {
  161. _setLayerRecursively(child.gameObject, layer);
  162. }
  163. }
  164. }
  165. // Added in v1.0, removed in v4.3.
  166. [Obsolete("Keyboard.InputReceived was removed in v4.3 because WebViewPrefab and CanvasWebViewPrefab now automatically handle keyboard input by default. Please remove your code that references Keyboard.InputReceived, and keyboard support will still work. For more info, including details about how you can still access keyboard input programmatically, please see this article: https://support.vuplex.com/articles/keyboard", true)]
  167. public event EventHandler<EventArgs<string>> InputReceived;
  168. }
  169. }