Reputation: 81
Im integrating a Unity-based mobile game with a web-based mobile application built using Ionic + Capacitor and React.
Used plugin: https://github.com/gree/unity-webview
The goal is to embed the web-based mobile app inside the Unity mobile game, allowing users to interact with the web app’s functionality while be inside the mobile game. WebView is working when loading external URLs (e.g., Google).
Now moving on with the main goal: we’ve copied the web app's compiled files (HTML, JS, CSS, etc.) into Unity’s StreamingAssets folder. We need to properly load the local index.html file of the web app in the WebView, and ensure that it can also load all the necessary local resources (like JS, CSS, and assets) from the StreamingAssets folder without issues. In short, the problem is serving local files from Unity’s StreamingAssets folder inside a WebView for Android/iOS platforms.
WebViewHandler.cs
script to serve the problem is serving local files from Unity’s StreamingAssets and not a simple external URL.using System.Collections;
using UnityEngine;
#if UNITY_2018_4_OR_NEWER
using UnityEngine.Networking;
#endif
using UnityEngine.UI;
public class SampleWebView : MonoBehaviour
{
public GameObject webViewGameObject; // The GameObject holding the WebView component
public string Url;
public Text status;
WebViewObject webViewObject;
IEnumerator Start()
{
webViewObject = (new GameObject("WebViewObject")).AddComponent<WebViewObject>();
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
webViewObject.canvas = GameObject.Find("Canvas");
#endif
webViewObject.Init(
cb: (msg) =>
{
Debug.Log(string.Format("CallFromJS[{0}]", msg));
status.text = msg;
status.GetComponent<Animation>().Play();
},
err: (msg) =>
{
Debug.Log(string.Format("CallOnError[{0}]", msg));
status.text = msg;
status.GetComponent<Animation>().Play();
},
httpErr: (msg) =>
{
Debug.Log(string.Format("CallOnHttpError[{0}]", msg));
status.text = msg;
status.GetComponent<Animation>().Play();
},
started: (msg) =>
{
Debug.Log(string.Format("CallOnStarted[{0}]", msg));
},
hooked: (msg) =>
{
Debug.Log(string.Format("CallOnHooked[{0}]", msg));
},
cookies: (msg) =>
{
Debug.Log(string.Format("CallOnCookies[{0}]", msg));
},
ld: (msg) =>
{
Debug.Log(string.Format("CallOnLoaded[{0}]", msg));
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS
// NOTE: the following js definition is required only for UIWebView; if
// enabledWKWebView is true and runtime has WKWebView, Unity.call is defined
// directly by the native plugin.
#if true
var js = @"
if (!(window.webkit && window.webkit.messageHandlers)) {
window.Unity = {
call: function(msg) {
window.location = 'unity:' + msg;
}
};
}
";
#else
// NOTE: depending on the situation, you might prefer this 'iframe' approach.
// cf. https://github.com/gree/unity-webview/issues/189
var js = @"
if (!(window.webkit && window.webkit.messageHandlers)) {
window.Unity = {
call: function(msg) {
var iframe = document.createElement('IFRAME');
iframe.setAttribute('src', 'unity:' + msg);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}
};
}
";
#endif
#elif UNITY_WEBPLAYER || UNITY_WEBGL
var js = @"
window.Unity = {
call:function(msg) {
parent.unityWebView.sendMessage('WebViewObject', msg);
}
};
";
#else
var js = "";
#endif
webViewObject.EvaluateJS(js + @"Unity.call('ua=' + navigator.userAgent)");
}
//transparent: false,
//zoom: true,
//ua: "custom user agent string",
//radius: 0, // rounded corner radius in pixel
//// android
//androidForceDarkMode: 0, // 0: follow system setting, 1: force dark off, 2: force dark on
//// ios
//enableWKWebView: true,
//wkContentMode: 0, // 0: recommended, 1: mobile, 2: desktop
//wkAllowsLinkPreview: true,
//// editor
//separated: false
);
// cf. https://github.com/gree/unity-webview/issues/1094#issuecomment-2358718029
while (!webViewObject.IsInitialized())
{
yield return null;
}
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
webViewObject.bitmapRefreshCycle = 1;
webViewObject.devicePixelRatio = 1; // 1 or 2
#endif
// cf. https://github.com/gree/unity-webview/pull/512
// Added alertDialogEnabled flag to enable/disable alert/confirm/prompt dialogs. by KojiNakamaru · Pull Request #512 · gree/unity-webview
//webViewObject.SetAlertDialogEnabled(false);
// cf. https://github.com/gree/unity-webview/pull/728
//webViewObject.SetCameraAccess(true);
//webViewObject.SetMicrophoneAccess(true);
// cf. https://github.com/gree/unity-webview/pull/550
// introduced SetURLPattern(..., hookPattern). by KojiNakamaru · Pull Request #550 · gree/unity-webview
//webViewObject.SetURLPattern("", "^https://.*youtube.com", "^https://.*google.com");
// cf. https://github.com/gree/unity-webview/pull/570
// Add BASIC authentication feature (Android and iOS with WKWebView only) by takeh1k0 · Pull Request #570 · gree/unity-webview
//webViewObject.SetBasicAuthInfo("id", "password");
//webViewObject.SetScrollbarsVisibility(true);
webViewObject.SetMargins(5, 100, 5, Screen.height / 4);
webViewObject.SetTextZoom(100); // android only. cf. https://stackoverflow.com/questions/21647641/android-webview-set-font-size-system-default/47017410#47017410
//webViewObject.SetMixedContentMode(2); // android only. 0: MIXED_CONTENT_ALWAYS_ALLOW, 1: MIXED_CONTENT_NEVER_ALLOW, 2: MIXED_CONTENT_COMPATIBILITY_MODE
webViewObject.SetVisibility(true);
#if !UNITY_WEBPLAYER && !UNITY_WEBGL
if (Url.StartsWith("http"))
{
webViewObject.LoadURL(Url.Replace(" ", "%20"));
}
else
{
var exts = new string[]{
".jpg",
".js",
".html" // should be last
};
foreach (var ext in exts)
{
var url = Url.Replace(".html", ext);
var src = System.IO.Path.Combine(Application.streamingAssetsPath, url);
var dst = System.IO.Path.Combine(Application.temporaryCachePath, url);
byte[] result = null;
if (src.Contains("://"))
{ // for Android
#if UNITY_2018_4_OR_NEWER
// NOTE: a more complete code that utilizes UnityWebRequest can be found in https://github.com/gree/unity-webview/commit/2a07e82f760a8495aa3a77a23453f384869caba7#diff-4379160fa4c2a287f414c07eb10ee36d
var unityWebRequest = UnityWebRequest.Get(src);
yield return unityWebRequest.SendWebRequest();
result = unityWebRequest.downloadHandler.data;
#else
var www = new WWW(src);
yield return www;
result = www.bytes;
#endif
}
else
{
result = System.IO.File.ReadAllBytes(src);
}
System.IO.File.WriteAllBytes(dst, result);
if (ext == ".html")
{
webViewObject.LoadURL("file://" + dst.Replace(" ", "%20"));
break;
}
}
}
#else
if (Url.StartsWith("http")) {
webViewObject.LoadURL(Url.Replace(" ", "%20"));
} else {
webViewObject.LoadURL("StreamingAssets/" + Url.Replace(" ", "%20"));
}
#endif
yield break;
}
void OnGUI()
{
var x = 10;
GUI.enabled = (webViewObject == null) ? false : webViewObject.CanGoBack();
if (GUI.Button(new Rect(x, 10, 80, 80), "<"))
{
webViewObject?.GoBack();
}
GUI.enabled = true;
x += 90;
GUI.enabled = (webViewObject == null) ? false : webViewObject.CanGoForward();
if (GUI.Button(new Rect(x, 10, 80, 80), ">"))
{
webViewObject?.GoForward();
}
GUI.enabled = true;
x += 90;
if (GUI.Button(new Rect(x, 10, 80, 80), "r"))
{
webViewObject?.Reload();
}
x += 90;
GUI.TextField(new Rect(x, 10, 180, 80), "" + ((webViewObject == null) ? 0 : webViewObject.Progress()));
x += 190;
if (GUI.Button(new Rect(x, 10, 80, 80), "*"))
{
var g = GameObject.Find("WebViewObject");
if (g != null)
{
Destroy(g);
}
else
{
StartCoroutine(Start());
}
}
x += 90;
if (GUI.Button(new Rect(x, 10, 80, 80), "c"))
{
webViewObject?.GetCookies(Url);
}
x += 90;
if (GUI.Button(new Rect(x, 10, 80, 80), "x"))
{
webViewObject?.ClearCookies();
}
x += 90;
if (GUI.Button(new Rect(x, 10, 80, 80), "D"))
{
webViewObject?.SetInteractionEnabled(false);
}
x += 90;
if (GUI.Button(new Rect(x, 10, 80, 80), "E"))
{
webViewObject?.SetInteractionEnabled(true);
}
x += 90;
}
}
Upvotes: 0
Views: 45