AndroidWebView.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861
  1. /**
  2. * Copyright (c) 2021 Vuplex Inc. All rights reserved.
  3. *
  4. * Licensed under the Vuplex Commercial Software Library License, you may
  5. * not use this file except in compliance with the License. You may obtain
  6. * a copy of the License at
  7. *
  8. * https://vuplex.com/commercial-library-license
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #if UNITY_ANDROID && !UNITY_EDITOR
  17. #pragma warning disable CS0108
  18. #pragma warning disable CS0067
  19. using System;
  20. using System.Collections;
  21. using System.Collections.Generic;
  22. using System.Reflection;
  23. using System.Runtime.InteropServices;
  24. using UnityEngine;
  25. using UnityEngine.Rendering;
  26. namespace Vuplex.WebView {
  27. /// <summary>
  28. /// The IWebView implementation used by 3D WebView for Android.
  29. /// This class also includes extra methods for Android-specific functionality.
  30. /// </summary>
  31. public class AndroidWebView : BaseWebView,
  32. IWebView,
  33. IWithMovablePointer,
  34. IWithPointerDownAndUp,
  35. IWithPopups {
  36. public WebPluginType PluginType {
  37. get {
  38. return WebPluginType.Android;
  39. }
  40. }
  41. /// <see cref="IWithPopups"/>
  42. public event EventHandler<PopupRequestedEventArgs> PopupRequested;
  43. /// <summary>
  44. /// Indicates that the browser's render process terminated, either because it
  45. /// crashed or because the operating system killed it.
  46. /// </summary>
  47. /// <remarks>
  48. /// 3D WebView for Android internally uses the `android.webkit.WebView` system
  49. /// package as its browser engine. Android's documentation indicates that
  50. /// the browser's render process can terminate in some rare circumstances.
  51. /// This RenderProcessGone event indicates when that occurs so that the application
  52. /// can recover be destroying the existing webviews and creating new webviews.
  53. ///
  54. /// Sources:
  55. /// - [`android.webkit.WebViewClient.onRenderProcessGone()`](https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail))
  56. /// - [Termination Handling API (Android docs)](https://developer.android.com/guide/webapps/managing-webview#termination-handle)
  57. /// </remarks>
  58. public event EventHandler RenderProcessGone;
  59. [Obsolete("The ScriptAlert event has been renamed to ScriptAlerted. Please use ScriptAlerted instead.", true)]
  60. public event EventHandler<ScriptDialogEventArgs> ScriptAlert;
  61. /// <summary>
  62. /// Event raised when a script in the page calls `window.alert()`.
  63. /// </summary>
  64. /// <remarks>
  65. /// If no handler is attached to this event, then `window.alert()` will return
  66. /// immediately and the script will continue execution. If a handler is attached to
  67. /// this event, then script execution will be paused until `ScriptDialogEventArgs.Continue()`
  68. /// is called.
  69. /// </remarks>
  70. public event EventHandler<ScriptDialogEventArgs> ScriptAlerted {
  71. add {
  72. if (_scriptAlertHandler != null) {
  73. throw new InvalidOperationException("ScriptAlerted supports only one event handler. Please remove the existing handler before adding a new one.");
  74. }
  75. _scriptAlertHandler = value;
  76. _webView.Call("setScriptAlertHandler", new AndroidStringAndBoolDelegateCallback(_handleScriptAlert));
  77. }
  78. remove {
  79. if (_scriptAlertHandler == value) {
  80. _scriptAlertHandler = null;
  81. _webView.Call("setScriptAlertHandler", null);
  82. }
  83. }
  84. }
  85. /// <summary>
  86. /// Event raised when a script in the page calls `window.confirm()`.
  87. /// </summary>
  88. /// <remarks>
  89. /// If no handler is attached to this event, then `window.confirm()` will return
  90. /// `false` immediately and the script will continue execution. If a handler is attached to
  91. /// this event, then script execution will be paused until `ScriptDialogEventArgs<bool>.Continue()`
  92. /// is called, and `window.confirm()` will return the value passed to `Continue()`.
  93. /// </remarks>
  94. public event EventHandler<ScriptDialogEventArgs<bool>> ScriptConfirmRequested {
  95. add {
  96. if (_scriptConfirmHandler != null) {
  97. throw new InvalidOperationException("ScriptConfirmRequested supports only one event handler. Please remove the existing handler before adding a new one.");
  98. }
  99. _scriptConfirmHandler = value;
  100. _webView.Call("setScriptConfirmHandler", new AndroidStringAndBoolDelegateCallback(_handleScriptConfirm));
  101. }
  102. remove {
  103. if (_scriptConfirmHandler == value) {
  104. _scriptConfirmHandler = null;
  105. _webView.Call("setScriptConfirmHandler", null);
  106. }
  107. }
  108. }
  109. public static AndroidWebView Instantiate() {
  110. return (AndroidWebView) new GameObject().AddComponent<AndroidWebView>();
  111. }
  112. public override void Init(Texture2D viewportTexture, float width, float height, Texture2D videoTexture) {
  113. AssertWebViewIsAvailable();
  114. _init(viewportTexture, width, height, videoTexture, null);
  115. }
  116. internal static void AssertWebViewIsAvailable() {
  117. if (!IsWebViewAvailable()) {
  118. throw new WebViewUnavailableException("The Android WebView package is currently unavailable. This is rare but can occur if it's not installed on the system or is currently being updated.");
  119. }
  120. }
  121. public override void Blur() {
  122. _assertValidState();
  123. _webView.Call("blur");
  124. }
  125. public override void CanGoBack(Action<bool> callback) {
  126. _assertValidState();
  127. _webView.Call("canGoBack", new AndroidBoolCallback(callback));
  128. }
  129. public override void CanGoForward(Action<bool> callback) {
  130. _assertValidState();
  131. _webView.Call("canGoForward", new AndroidBoolCallback(callback));
  132. }
  133. /// <summary>
  134. /// Overrides `BaseWebView.CaptureScreenshot()` because it doesn't work
  135. /// with Android OES textures.
  136. /// </summary>
  137. public override void CaptureScreenshot(Action<byte[]> callback) {
  138. _assertValidState();
  139. _webView.Call("captureScreenshot", new AndroidByteArrayCallback(callback));
  140. }
  141. public static void ClearAllData() {
  142. _class.CallStatic("clearAllData");
  143. }
  144. /// <summary>
  145. /// Clears the webview's back / forward navigation history.
  146. /// </summary>
  147. public void ClearHistory() {
  148. _assertValidState();
  149. _webView.Call("clearHistory");
  150. }
  151. public override void Click(Vector2 point) {
  152. _assertValidState();
  153. var nativeX = (int)(point.x * _nativeWidth);
  154. var nativeY = (int)(point.y * _nativeHeight);
  155. _webView.Call("click", nativeX, nativeY);
  156. }
  157. public override void DisableViewUpdates() {
  158. _assertValidState();
  159. _webView.Call("disableViewUpdates");
  160. _viewUpdatesAreEnabled = false;
  161. }
  162. public override void Dispose() {
  163. _assertValidState();
  164. // Cancel the render if it has been scheduled via GL.IssuePluginEvent().
  165. WebView_removePointer(_webView.GetRawObject());
  166. IsDisposed = true;
  167. _webView.Call("destroy");
  168. _webView.Dispose();
  169. Destroy(gameObject);
  170. }
  171. public override void EnableViewUpdates() {
  172. _assertValidState();
  173. _webView.Call("enableViewUpdates");
  174. _viewUpdatesAreEnabled = true;
  175. }
  176. public override void ExecuteJavaScript(string javaScript, Action<string> callback) {
  177. _assertValidState();
  178. var nativeCallback = callback == null ? null : new AndroidStringCallback(callback);
  179. _webView.Call("executeJavaScript", javaScript, nativeCallback);
  180. }
  181. public override void Focus() {
  182. _assertValidState();
  183. _webView.Call("focus");
  184. }
  185. public static string GetGraphicsApiErrorMessage(GraphicsDeviceType graphicsDeviceType) {
  186. var isValid = graphicsDeviceType == GraphicsDeviceType.OpenGLES3 || graphicsDeviceType == GraphicsDeviceType.OpenGLES2;
  187. if (isValid) {
  188. return null;
  189. }
  190. return String.Format("Unsupported graphics API: 3D WebView for Android requires OpenGLES3 or OpenGLES2, but the graphics API in use is {0}. Please go to Player Settings and set \"Graphics APIs\" to OpenGLES3 or OpenGLES2.", graphicsDeviceType);
  191. }
  192. /// <summary>
  193. /// Overrides `BaseWebView.GetRawTextureData()` because it's slow on Android.
  194. /// </summary>
  195. public override void GetRawTextureData(Action<byte[]> callback) {
  196. _assertValidState();
  197. _webView.Call("getRawTextureData", new AndroidByteArrayCallback(callback));
  198. }
  199. public static void GloballySetUserAgent(bool mobile) {
  200. _class.CallStatic("globallySetUserAgent", mobile);
  201. }
  202. public static void GloballySetUserAgent(string userAgent) {
  203. _class.CallStatic("globallySetUserAgent", userAgent);
  204. }
  205. [Obsolete("AndroidWebView.GloballyUseAlternativeInputEventSystem() has been removed. Please use AndroidWebView.SetAlternativePointerInputSystemEnabled() and/or SetAlternativeKeyboardInputSystemEnabled() instead.", true)]
  206. public static void GloballyUseAlternativeInputEventSystem(bool useAlternativeInputEventSystem) {}
  207. public override void GoBack() {
  208. _assertValidState();
  209. _webView.Call("goBack");
  210. }
  211. public override void GoForward() {
  212. _assertValidState();
  213. _webView.Call("goForward");
  214. }
  215. public override void HandleKeyboardInput(string input) {
  216. _assertValidState();
  217. _webView.Call("handleKeyboardInput", input);
  218. }
  219. /// <summary>
  220. /// Indicates whether native video rendering is available for the current
  221. /// version of Android and is enabled.
  222. /// </summary>
  223. /// <remarks>
  224. /// Native video rendering is available in Android API level 23 and above.
  225. /// If native video rendering isn't supported (i.e. the Android version is
  226. /// lower than 23), then the AndroidWebView plugin will use a fallback video
  227. /// implementation to support basic video playback.
  228. /// </remarks>
  229. public static bool IsUsingNativeVideoRendering() {
  230. return _class.CallStatic<bool>("isUsingNativeVideoRendering");
  231. }
  232. /// <summary>
  233. /// Indicates whether the Android WebView package is installed on the system and available.
  234. /// </summary>
  235. /// <remarks>
  236. /// 3D WebView internally depends on Android's WebView package, which is normally installed
  237. /// as part of the operating system. In rare circumstances, the Android WebView package may be unavailable.
  238. /// For example, this can happen if the user used developer tools to delete the WebView package
  239. /// or if [updates to the WebView package are currently being installed](https://bugs.chromium.org/p/chromium/issues/detail?id=506369) .
  240. /// </remarks>
  241. public static bool IsWebViewAvailable() {
  242. if (_webViewPackageIsAvailable == null) {
  243. _webViewPackageIsAvailable = _class.CallStatic<bool>("isWebViewAvailable");
  244. }
  245. return (bool)_webViewPackageIsAvailable;
  246. }
  247. public override void LoadHtml(string html) {
  248. _assertValidState();
  249. _webView.Call("loadHtml", html);
  250. }
  251. /// <summary>
  252. /// Like `LoadHtml(string html)`, but also allows a virtual base URL
  253. /// to be specified.
  254. /// </summary>
  255. public void LoadHtml(string html, string baseUrl) {
  256. _assertValidState();
  257. _webView.Call("loadHtml", html, baseUrl);
  258. }
  259. public override void LoadUrl(string url) {
  260. _assertValidState();
  261. _webView.Call("loadUrl", _transformStreamingAssetsUrlIfNeeded(url));
  262. }
  263. public override void LoadUrl(string url, Dictionary<string, string> additionalHttpHeaders) {
  264. _assertValidState();
  265. if (additionalHttpHeaders == null) {
  266. LoadUrl(url);
  267. } else {
  268. var map = _convertDictionaryToJavaMap(additionalHttpHeaders);
  269. _webView.Call("loadUrl", url, map);
  270. }
  271. }
  272. /// <see cref="IWithMovablePointer"/>
  273. public void MovePointer(Vector2 point) {
  274. _assertValidState();
  275. var nativeX = (int)(point.x * _nativeWidth);
  276. var nativeY = (int)(point.y * _nativeHeight);
  277. _webView.Call("movePointer", nativeX, nativeY);
  278. }
  279. /// <summary>
  280. /// Pauses processing, media, and rendering for this webview instance
  281. /// until `Resume()` is called.
  282. /// </summary>
  283. public void Pause() {
  284. _assertValidState();
  285. _webView.Call("pause");
  286. }
  287. /// <summary>
  288. /// Pauses processing, media, and rendering for all webview instances.
  289. /// This method is automatically called by the plugin when the application
  290. /// is paused.
  291. /// </summary>
  292. public static void PauseAll() {
  293. _class.CallStatic("pauseAll");
  294. }
  295. /// <see cref="IWithPointerDownAndUp"/>
  296. public void PointerDown(Vector2 point) {
  297. _pointerDown(point, MouseButton.Left, 1);
  298. }
  299. /// <see cref="IWithPointerDownAndUp"/>
  300. public void PointerDown(Vector2 point, PointerOptions options) {
  301. if (options == null) {
  302. options = new PointerOptions();
  303. }
  304. _pointerDown(point, options.Button, options.ClickCount);
  305. }
  306. /// <see cref="IWithPointerDownAndUp"/>
  307. public void PointerUp(Vector2 point) {
  308. _pointerUp(point, MouseButton.Left, 1);
  309. }
  310. /// <see cref="IWithPointerDownAndUp"/>
  311. public void PointerUp(Vector2 point, PointerOptions options) {
  312. if (options == null) {
  313. options = new PointerOptions();
  314. }
  315. _pointerUp(point, options.Button, options.ClickCount);
  316. }
  317. /// <summary>
  318. /// Loads the given URL using an HTTP POST request and the given
  319. /// application/x-www-form-urlencoded data.
  320. /// </summary>
  321. /// <example>
  322. /// webView.PostUrl("https://postman-echo.com/post", Encoding.Unicode.GetBytes("foo=bar"));
  323. /// </example>
  324. public void PostUrl(string url, byte[] data) {
  325. _assertValidState();
  326. _webView.Call("postUrl", url, data);
  327. }
  328. public override void Reload() {
  329. _assertValidState();
  330. _webView.Call("reload");
  331. }
  332. /// <summary>
  333. /// Resumes processing and rendering for all webview instances
  334. /// after a previous call to `Pause().`
  335. /// </summary>
  336. public void Resume() {
  337. _assertValidState();
  338. _webView.Call("resume");
  339. }
  340. /// <summary>
  341. /// Resumes processing and rendering for all webview instances
  342. /// after a previous call to `PauseAll().` This method
  343. /// is automatically called by the plugin when the application resumes after
  344. /// being paused.
  345. /// </summary>
  346. public static void ResumeAll() {
  347. _class.CallStatic("resumeAll");
  348. }
  349. public override void Scroll(Vector2 scrollDelta) {
  350. _assertValidState();
  351. var deltaX = (int)(scrollDelta.x * _numberOfPixelsPerUnityUnit);
  352. var deltaY = (int)(scrollDelta.y * _numberOfPixelsPerUnityUnit);
  353. _webView.Call("scroll", deltaX, deltaY);
  354. }
  355. public override void Scroll(Vector2 scrollDelta, Vector2 point) {
  356. _assertValidState();
  357. var deltaX = (int)(scrollDelta.x * _numberOfPixelsPerUnityUnit);
  358. var deltaY = (int)(scrollDelta.y * _numberOfPixelsPerUnityUnit);
  359. var pointerX = (int)(point.x * _nativeWidth);
  360. var pointerY = (int)(point.y * _nativeHeight);
  361. _webView.Call("scroll", deltaX, deltaY, pointerX, pointerY);
  362. }
  363. public static void SetAlternativeKeyboardInputSystemEnabled(bool enabled) {
  364. _class.CallStatic("setAlternativeKeyboardInputSystemEnabled", enabled);
  365. }
  366. /// <summary>
  367. /// By default, 3D WebView dispatches pointer (a.k.a mouse) events to the
  368. /// browser engine in a way that accurately mimics the functionality of
  369. /// a desktop browser. This works great in most cases, but on some systems
  370. /// (i.e. Oculus Quest 2), the system version of Chromium is buggy and out-of-date,
  371. /// which can lead to issues where pointer events aren't dispatched accurately.
  372. /// In those cases, this method can be used to enable an alternative pointer
  373. /// input system that is less flexible but doesn't suffer from the Chromium
  374. /// bugs. This method is called automatically by AndroidWebPlugin.cs when
  375. /// running on Oculus Quest 2. Note that calling this method effectively disables
  376. /// the ability to trigger hover or drag events with `MovePointer()`.
  377. /// </summary>
  378. public static void SetAlternativePointerInputSystemEnabled(bool enabled) {
  379. _class.CallStatic("setAlternativePointerInputSystemEnabled", enabled);
  380. }
  381. /// <summary>
  382. /// By default, web pages cannot access the device's
  383. /// camera or microphone via JavaScript, even if the user has granted
  384. /// the app permission to use them. Invoking `SetAudioAndVideoCaptureEnabled(true)` allows
  385. /// **all web pages** to access the camera and microphone if the user has
  386. /// granted the app permission to use them via the standard Android permission dialogs.
  387. /// </summary>
  388. /// <remarks>
  389. /// This is useful, for example, to enable WebRTC support.
  390. /// In addition to calling this method, the application must include the following Android
  391. /// permissions in its AndroidManifest.xml and also request the permissions at runtime.
  392. /// - android.permission.RECORD_AUDIO
  393. /// - android.permission.MODIFY_AUDIO_SETTINGS
  394. /// - android.permission.CAMERA
  395. /// </remarks>
  396. public static void SetAudioAndVideoCaptureEnabled(bool enabled) {
  397. _class.CallStatic("setAudioAndVideoCaptureEnabled", enabled);
  398. }
  399. public static void SetClickCorrectionEnabled(bool enabled) {
  400. _class.CallStatic("setClickCorrectionEnabled", enabled);
  401. }
  402. [Obsolete("AndroidWebView.SetCustomUriSchemesEnabled() has been removed. Now when a page redirects to a URI with a custom scheme, 3D WebView will automatically emit the UrlChanged and LoadProgressChanged events for the navigation, but a deep link (i.e. to an external application) won't occur.", true)]
  403. public static void SetCustomUriSchemesEnabled(bool enabled) {}
  404. [Obsolete("AndroidWebView.SetForceDrawEnabled() has been removed because it is no longer needed.", true)]
  405. public static void SetForceDrawEnabled(bool enabled) {}
  406. /// <summary>
  407. /// By default, web pages cannot access the device's
  408. /// geolocation via JavaScript, even if the user has granted
  409. /// the app permission to access location. Invoking `SetGeolocationPermissionEnabled(true)` allows
  410. /// **all web pages** to access the geolocation if the user has
  411. /// granted the app location permissions via the standard Android permission dialogs.
  412. /// </summary>
  413. /// <remarks>
  414. /// The following Android permissions must be included in the app's AndroidManifest.xml
  415. /// and also requested by the application at runtime:
  416. /// - android.permission.ACCESS_COARSE_LOCATION
  417. /// - android.permission.ACCESS_FINE_LOCATION
  418. /// </remarks>
  419. public static void SetGeolocationPermissionEnabled(bool enabled) {
  420. _class.CallStatic("setGeolocationPermissionEnabled", enabled);
  421. }
  422. public static void SetIgnoreCertificateErrors(bool ignore) {
  423. _class.CallStatic("setIgnoreCertificateErrors", ignore);
  424. }
  425. [Obsolete("AndroidWebView.SetIgnoreSslErrors() is now deprecated. Please use Web.SetIgnoreCertificateErrors() instead.")]
  426. public static void SetIgnoreSslErrors(bool ignore) {
  427. SetIgnoreCertificateErrors(ignore);
  428. }
  429. /// <summary>
  430. /// Sets the initial scale for web content, where 1.0 is the default scale.
  431. /// </summary>
  432. public void SetInitialScale(float scale) {
  433. _assertValidState();
  434. _webView.Call("setInitialScale", scale);
  435. }
  436. /// <summary>
  437. /// By default, AndroidWebView prevents JavaScript from auto-playing sound
  438. /// from most sites unless the user has first interacted with the page.
  439. /// You can call this method to disable or re-enable enforcement of this auto-play policy.
  440. /// </summary>
  441. public void SetMediaPlaybackRequiresUserGesture(bool mediaPlaybackRequiresUserGesture) {
  442. _assertValidState();
  443. _webView.Call("setMediaPlaybackRequiresUserGesture", mediaPlaybackRequiresUserGesture);
  444. }
  445. [Obsolete("AndroidWebView.SetNativeKeyboardEnabled() is now deprecated. Please use Web.SetTouchScreenKeyboardEnabled() instead.")]
  446. public static void SetNativeKeyboardEnabled(bool enabled) {
  447. SetTouchScreenKeyboardEnabled(enabled);
  448. }
  449. /// <summary>
  450. /// Enables or disables native video rendering on versions of Android
  451. /// that support native video rendering.
  452. /// </summary>
  453. /// <remarks>
  454. /// The default is enabled. If disabled, then the `AndroidWebView`
  455. /// plugin will use a fallback video implementation to support basic
  456. /// video playback. This method is automatically called when the
  457. /// Oculus VR SDK is enabled, because the Oculus Go and Quest
  458. /// headsets don't support native video rendering.
  459. /// </remarks>
  460. public static void SetNativeVideoRenderingEnabled(bool enabled) {
  461. _class.CallStatic("setNativeVideoRenderingEnabled", enabled);
  462. }
  463. /// <see cref="IWithPopups"/>
  464. public void SetPopupMode(PopupMode popupMode) {
  465. _assertValidState();
  466. _webView.Call("setPopupMode", (int)popupMode);
  467. }
  468. public static void SetStorageEnabled(bool enabled) {
  469. _class.CallStatic("setStorageEnabled", enabled);
  470. }
  471. /// <summary>
  472. /// Sets the `android.view.Surface` to which the webview renders.
  473. /// This can be used, for example, to render to an Oculus
  474. /// [OVROverlay](https://developer.oculus.com/reference/unity/1.30/class_o_v_r_overlay).
  475. /// After this method is called, the webview no longer renders
  476. /// to its original texture and instead renders to the given surface.
  477. /// </summary>
  478. /// <example>
  479. /// var surface = ovrOverlay.externalSurfaceObject();
  480. /// // Set the resolution to 1 px / Unity unit
  481. /// // to make it easy to specify the size in pixels.
  482. /// webView.SetResolution(1);
  483. /// // Or if the webview is attached to a prefab, call WebViewPrefab.Resize()
  484. /// webView.WebView.Resize(surface.externalSurfaceWidth(), surface.externalSurfaceHeight());
  485. /// #if UNITY_ANDROID && !UNITY_EDITOR
  486. /// (webView as AndroidWebView).SetSurface(surface);
  487. /// #endif
  488. /// </example>
  489. public void SetSurface(IntPtr surface) {
  490. _assertValidState();
  491. var surfaceObject = _convertIntPtrToAndroidJavaObject(surface);
  492. _webView.Call("setSurface", surfaceObject);
  493. }
  494. public static void SetTouchScreenKeyboardEnabled(bool enabled) {
  495. _class.CallStatic("setTouchScreenKeyboardEnabled", enabled);
  496. }
  497. /// <summary>
  498. /// Like `Web.SetUserAgent(bool mobile)`, except it sets the user-agent
  499. /// for a single webview instance instead of setting it globally.
  500. /// </summary>
  501. /// <remarks>
  502. /// If you globally set a default user-agent using `Web.SetUserAgent()`,
  503. /// you can still use this method to override the user-agent for a
  504. /// single webview instance.
  505. /// </remarks>
  506. public void SetUserAgent(bool mobile) {
  507. _assertValidState();
  508. _webView.Call("setUserAgent", mobile);
  509. }
  510. /// <summary>
  511. /// Like `Web.SetUserAgent(string userAgent)`, except it sets the user-agent
  512. /// for a single webview instance instead of setting it globally.
  513. /// </summary>
  514. /// <remarks>
  515. /// If you globally set a default user-agent using `Web.SetUserAgent()`,
  516. /// you can still use this method to override the user-agent for a
  517. /// single webview instance.
  518. /// </remarks>
  519. public void SetUserAgent(string userAgent) {
  520. _assertValidState();
  521. _webView.Call("setUserAgent", userAgent);
  522. }
  523. [Obsolete("AndroidWebView.UseAlternativeInputEventSystem() has been removed. Please use AndroidWebView.SetAlternativePointerInputSystemEnabled() and/or SetAlternativeKeyboardInputSystemEnabled() instead.", true)]
  524. public void UseAlternativeInputEventSystem(bool useAlternativeInputEventSystem) {}
  525. /// <summary>
  526. /// Zooms in or out by the given factor, which is multiplied by the current zoom level
  527. /// to reach the new zoom level.
  528. /// </summary>
  529. /// <remarks>
  530. /// Note that the zoom level gets reset when a new page is loaded.
  531. /// </remarks>
  532. /// <param name="zoomFactor">
  533. /// The zoom factor to apply in the range from 0.01 to 100.0.
  534. /// </param>
  535. public void ZoomBy(float zoomFactor) {
  536. _assertValidState();
  537. _webView.Call("zoomBy", zoomFactor);
  538. }
  539. public override void ZoomIn() {
  540. _assertValidState();
  541. _webView.Call("zoomIn");
  542. }
  543. public override void ZoomOut() {
  544. _assertValidState();
  545. _webView.Call("zoomOut");
  546. }
  547. // Get a reference to AndroidJavaObject's hidden constructor that takes
  548. // the IntPtr for a jobject as a parameter.
  549. readonly static ConstructorInfo _androidJavaObjectIntPtrConstructor = typeof(AndroidJavaObject).GetConstructor(
  550. BindingFlags.Instance | BindingFlags.NonPublic,
  551. null,
  552. new []{ typeof(IntPtr) },
  553. null
  554. );
  555. internal static AndroidJavaClass _class = new AndroidJavaClass(FULL_CLASS_NAME);
  556. const string FULL_CLASS_NAME = "com.vuplex.webview.WebView";
  557. EventHandler<ScriptDialogEventArgs> _scriptAlertHandler;
  558. EventHandler<ScriptDialogEventArgs<bool>> _scriptConfirmHandler;
  559. readonly WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame();
  560. internal AndroidJavaObject _webView;
  561. static bool? _webViewPackageIsAvailable = null;
  562. AndroidJavaObject _convertDictionaryToJavaMap(Dictionary<string, string> dictionary) {
  563. AndroidJavaObject map = new AndroidJavaObject("java.util.HashMap");
  564. IntPtr putMethod = AndroidJNIHelper.GetMethodID(map.GetRawClass(), "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
  565. foreach (var entry in dictionary) {
  566. AndroidJNI.CallObjectMethod(
  567. map.GetRawObject(),
  568. putMethod,
  569. AndroidJNIHelper.CreateJNIArgArray(new object[] { entry.Key, entry.Value })
  570. );
  571. }
  572. return map;
  573. }
  574. static AndroidJavaObject _convertIntPtrToAndroidJavaObject(IntPtr jobject) {
  575. if (jobject == IntPtr.Zero) {
  576. return null;
  577. }
  578. return (AndroidJavaObject) _androidJavaObjectIntPtrConstructor.Invoke(new object[] { jobject });
  579. }
  580. /// <summary>
  581. /// The native plugin invokes this method.
  582. /// </summary>
  583. protected virtual void HandleInitialVideoPlayRequest(string serializedVideo) {
  584. _assertValidState();
  585. var video = JsonUtility.FromJson<Video>(serializedVideo);
  586. var nativeVideoPlayer = _webView.Call<AndroidJavaObject>("getOrCreateVideoPlayer", serializedVideo, _videoTexture.GetNativeTexturePtr().ToInt32());
  587. nativeVideoPlayer.Call("play", video.videoUrl);
  588. }
  589. void _handlePopup(string url, AndroidJavaObject popupResultMessage) {
  590. var handler = PopupRequested;
  591. if (handler == null) {
  592. return;
  593. }
  594. if (popupResultMessage == null) {
  595. handler(this, new PopupRequestedEventArgs(url, null));
  596. return;
  597. }
  598. var popupWebView = Instantiate();
  599. Dispatcher.RunOnMainThread(() => {
  600. AndroidWebPlugin.Instance.CreateTexture(1, 1, texture => {
  601. // Use the same resolution and dimensions as the current webview.
  602. popupWebView.SetResolution(_numberOfPixelsPerUnityUnit);
  603. popupWebView._init(texture, _width, _height, null, popupResultMessage);
  604. handler(this, new PopupRequestedEventArgs(url, popupWebView));
  605. });
  606. });
  607. }
  608. /// <summary>
  609. /// The native plugin invokes this method.
  610. /// </summary>
  611. void HandleRenderProcessGone() {
  612. var handler = RenderProcessGone;
  613. if (handler != null) {
  614. handler(this, EventArgs.Empty);
  615. }
  616. }
  617. void _handleScriptAlert(string message, Action<bool> continueCallback) {
  618. _scriptAlertHandler(this, new ScriptDialogEventArgs(message, () => continueCallback(true)));
  619. }
  620. void _handleScriptConfirm(string message, Action<bool> continueCallback) {
  621. _scriptConfirmHandler(this, new ScriptDialogEventArgs<bool>(message, continueCallback));
  622. }
  623. void _init(Texture2D viewportTexture, float width, float height, Texture2D videoTexture, AndroidJavaObject popupResultMessage) {
  624. base.Init(viewportTexture, width, height, videoTexture);
  625. _webView = new AndroidJavaObject(
  626. FULL_CLASS_NAME,
  627. gameObject.name,
  628. viewportTexture.GetNativeTexturePtr().ToInt32(),
  629. _nativeWidth,
  630. _nativeHeight,
  631. SystemInfo.graphicsMultiThreaded,
  632. videoTexture != null,
  633. new AndroidStringAndObjectCallback(_handlePopup),
  634. popupResultMessage
  635. );
  636. }
  637. void OnEnable() {
  638. // Start the coroutine from OnEnable so that the coroutine
  639. // is restarted if the object is deactivated and then reactivated.
  640. StartCoroutine(_renderPluginOncePerFrame());
  641. }
  642. void _pointerDown(Vector2 point, MouseButton mouseButton, int clickCount) {
  643. _assertValidState();
  644. var nativeX = (int)(point.x * _nativeWidth);
  645. var nativeY = (int)(point.y * _nativeHeight);
  646. _webView.Call("pointerDown", nativeX, nativeY, (int)mouseButton, clickCount);
  647. }
  648. void _pointerUp(Vector2 point, MouseButton mouseButton, int clickCount) {
  649. _assertValidState();
  650. var nativeX = (int)(point.x * _nativeWidth);
  651. var nativeY = (int)(point.y * _nativeHeight);
  652. _webView.Call("pointerUp", nativeX, nativeY, (int)mouseButton, clickCount);
  653. }
  654. IEnumerator _renderPluginOncePerFrame() {
  655. while (true) {
  656. yield return _waitForEndOfFrame;
  657. if (!_viewUpdatesAreEnabled || IsDisposed || _webView == null) {
  658. continue;
  659. }
  660. var nativeWebViewPtr = _webView.GetRawObject();
  661. if (nativeWebViewPtr != IntPtr.Zero) {
  662. int pointerId = WebView_depositPointer(nativeWebViewPtr);
  663. GL.IssuePluginEvent(WebView_getRenderFunction(), pointerId);
  664. }
  665. }
  666. }
  667. protected override void _resize() {
  668. // Only trigger a resize if the webview has been initialized
  669. if (_viewportTexture) {
  670. _assertValidState();
  671. Utils.ThrowExceptionIfAbnormallyLarge(_nativeWidth, _nativeHeight);
  672. _webView.Call("resize", _nativeWidth, _nativeHeight);
  673. }
  674. }
  675. protected override void _setConsoleMessageEventsEnabled(bool enabled) {
  676. _assertValidState();
  677. _webView.Call("setConsoleMessageEventsEnabled", enabled);
  678. }
  679. protected override void _setFocusedInputFieldEventsEnabled(bool enabled) {
  680. _assertValidState();
  681. _webView.Call("setFocusedInputFieldEventsEnabled", enabled);
  682. }
  683. [DllImport(_dllName)]
  684. static extern IntPtr WebView_getRenderFunction();
  685. [DllImport(_dllName)]
  686. static extern int WebView_depositPointer(IntPtr pointer);
  687. [DllImport(_dllName)]
  688. static extern void WebView_removePointer(IntPtr pointer);
  689. }
  690. }
  691. #endif