BaseKeyboard.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // Copyright (c) 2022 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. using System;
  15. using System.Threading.Tasks;
  16. using UnityEngine;
  17. using Vuplex.WebView.Internal;
  18. namespace Vuplex.WebView {
  19. public abstract class BaseKeyboard : MonoBehaviour {
  20. /// <summary>
  21. /// Indicates that the user clicked a key on the keyboard.
  22. /// </summary>
  23. public event EventHandler<EventArgs<string>> InputReceived;
  24. /// <summary>
  25. /// Indicates that the keyboard finished initializing.
  26. /// </summary>
  27. public event EventHandler Initialized;
  28. /// <summary>
  29. /// If you want to load a customized version of the Keyboard UI, you can
  30. /// do so by setting this field. For example, you could load a customized
  31. /// Keyboard UI from StreamingAssets by using a URL like "streaming-assets://keyboard/index.html".
  32. /// <summary>
  33. [Label("Custom Keyboard URL (optional)")]
  34. [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\".")]
  35. public string CustomKeyboardUrl;
  36. /// <summary>
  37. /// Returns a task that completes when the keyboard is initialized,
  38. /// which means that its WebViewPrefab property is ready for use.
  39. /// </summary>
  40. /// <example>
  41. /// <code>
  42. /// await keyboard.WaitUntilInitialized();
  43. /// keyboard.WebViewPrefab.Clicked += (sender, eventArgs) => {
  44. /// Debug.Log("Keyboard was clicked");
  45. /// };
  46. /// </code>
  47. /// </example>
  48. public Task WaitUntilInitialized() {
  49. var taskSource = new TaskCompletionSource<bool>();
  50. if (_isInitialized) {
  51. taskSource.SetResult(true);
  52. } else {
  53. Initialized += (sender, e) => taskSource.SetResult(true);
  54. }
  55. return taskSource.Task;
  56. }
  57. bool _isInitialized;
  58. [SerializeField]
  59. [HideInInspector]
  60. protected BaseWebViewPrefab _webViewPrefab;
  61. protected static readonly WebViewOptions _webViewOptions = new WebViewOptions {
  62. clickWithoutStealingFocus = true,
  63. disableVideo = true,
  64. // If both Android plugins are installed, prefer the original Chromium
  65. // plugin for the keyboard, since the Gecko plugin doesn't support
  66. // transparent backgrounds.
  67. preferredPlugins = new WebPluginType[] { WebPluginType.Android }
  68. };
  69. async protected void _init() {
  70. _webViewPrefab.CursorIconsEnabled = false;
  71. // Reset InitialUrl to null in case the developer modified WebViewPrefab.prefab to set a default InitialUrl.
  72. _webViewPrefab.InitialUrl = null;
  73. await _webViewPrefab.WaitUntilInitialized();
  74. var pluginType = _webViewPrefab.WebView.PluginType;
  75. if (pluginType == WebPluginType.AndroidGecko) {
  76. // On Android Gecko, hovering steals focus.
  77. _webViewPrefab.HoveringEnabled = false;
  78. }
  79. // Scrolling and dragging can also cause the keyboard
  80. // to steal focus on Android Gecko, so just disable them.
  81. _webViewPrefab.ScrollingEnabled = false;
  82. _webViewPrefab.DragMode = DragMode.Disabled;
  83. _webViewPrefab.WebView.MessageEmitted += WebView_MessageEmitted;
  84. // Android Gecko and Hololens don't support transparent webviews, so set the cutout
  85. // rect to the entire view so that the shader makes its black background
  86. // pixels transparent.
  87. if (pluginType == WebPluginType.AndroidGecko || pluginType == WebPluginType.UniversalWindowsPlatform) {
  88. _webViewPrefab.SetCutoutRect(new Rect(0, 0, 1, 1));
  89. }
  90. if (!String.IsNullOrWhiteSpace(CustomKeyboardUrl)) {
  91. _webViewPrefab.WebView.LoadUrl(CustomKeyboardUrl.Trim());
  92. } else {
  93. _webViewPrefab.WebView.LoadHtml(KeyboardUi.Html);
  94. }
  95. }
  96. void WebView_MessageEmitted(object sender, EventArgs<string> e) {
  97. var serializedMessage = e.Value;
  98. var messageType = JsonUtility.FromJson<BridgeMessage>(serializedMessage).type;
  99. switch (messageType) {
  100. case "keyboard.inputReceived":
  101. var input = StringBridgeMessage.ParseValue(serializedMessage);
  102. InputReceived?.Invoke(this, new EventArgs<string>(input));
  103. break;
  104. case "keyboard.initialized":
  105. _sendKeyboardLanguageMessage();
  106. _isInitialized = true;
  107. Initialized?.Invoke(this, EventArgs.Empty);
  108. break;
  109. }
  110. }
  111. string _getKeyboardLanguage() {
  112. switch (Application.systemLanguage) {
  113. case SystemLanguage.Danish:
  114. return "da";
  115. case SystemLanguage.French:
  116. return "fr";
  117. case SystemLanguage.German:
  118. return "de";
  119. case SystemLanguage.Norwegian:
  120. return "no";
  121. case SystemLanguage.Russian:
  122. return "ru";
  123. case SystemLanguage.Spanish:
  124. return "es";
  125. case SystemLanguage.Swedish:
  126. return "sv";
  127. default:
  128. return "en";
  129. }
  130. }
  131. /// <summary>
  132. /// Initializes the keyboard language based on the system language.
  133. /// </summary>
  134. void _sendKeyboardLanguageMessage() {
  135. var message = new StringBridgeMessage {
  136. type = "keyboard.setLanguage",
  137. value = _getKeyboardLanguage()
  138. };
  139. var serializedMessage = JsonUtility.ToJson(message);
  140. _webViewPrefab.WebView.PostMessage(serializedMessage);
  141. }
  142. protected static void _setLayerRecursively(GameObject gameObject, int layer) {
  143. if (gameObject == null) {
  144. return;
  145. }
  146. gameObject.layer = layer;
  147. foreach (Transform child in gameObject.transform) {
  148. if (child != null) {
  149. _setLayerRecursively(child.gameObject, layer);
  150. }
  151. }
  152. }
  153. }
  154. }