├── Editor.meta ├── Editor ├── MediationAdapterDependencies.xml ├── MediationAdapterDependencies.xml.meta ├── Scripts.meta ├── Scripts │ ├── UnityWebViewPostprocessBuild.cs │ └── UnityWebViewPostprocessBuild.cs.meta ├── TLab.WebView.Editor.asmdef └── TLab.WebView.Editor.asmdef.meta ├── LICENSE.md ├── LICENSE.md.meta ├── Media.meta ├── Media ├── header.png ├── header.png.meta ├── image.0.png ├── image.0.png.meta ├── tlab-webview-settings.png ├── tlab-webview-settings.png.meta ├── tlab-webview-vr.gif ├── tlab-webview-vr.gif.meta ├── tlab-webview.png └── tlab-webview.png.meta ├── Plugins.meta ├── Plugins ├── Android.meta └── Android │ ├── copy.bat │ ├── copy.bat.meta │ ├── libTLabWebView-release.aar │ └── libTLabWebView-release.aar.meta ├── README-ja.md ├── README-ja.md.meta ├── README.md ├── README.md.meta ├── Resources.meta ├── Resources ├── TLab.meta └── TLab │ ├── WebView.meta │ └── WebView │ ├── Browser.prefab │ ├── Browser.prefab.meta │ ├── Samples.meta │ └── Samples │ ├── Browser Sample.prefab │ ├── Browser Sample.prefab.meta │ ├── Scripts.meta │ ├── Scripts │ ├── JS.meta │ └── JS │ │ ├── disable-beforunload.txt │ │ ├── disable-beforunload.txt.meta │ │ ├── fetch-blob.txt │ │ ├── fetch-blob.txt.meta │ │ ├── focus-in-out-interaction.txt │ │ └── focus-in-out-interaction.txt.meta │ ├── Widget.meta │ └── Widget │ ├── Dialog.prefab │ ├── Dialog.prefab.meta │ ├── Number Picker.prefab │ ├── Number Picker.prefab.meta │ ├── Select.meta │ └── Select │ ├── Group.prefab │ ├── Group.prefab.meta │ ├── Selectable.prefab │ ├── Selectable.prefab.meta │ ├── Separator.prefab │ └── Separator.prefab.meta ├── Runtime.meta ├── Runtime ├── BaseInputListener.cs ├── BaseInputListener.cs.meta ├── Browser.cs ├── Browser.cs.meta ├── BrowserContainer.cs ├── BrowserContainer.cs.meta ├── BrowserInputField.cs ├── BrowserInputField.cs.meta ├── BrowserInputListener.cs ├── BrowserInputListener.cs.meta ├── BrowserManager.cs ├── BrowserManager.cs.meta ├── Common.cs ├── Common.cs.meta ├── FragmentCapture.cs ├── FragmentCapture.cs.meta ├── GeckoView.cs ├── GeckoView.cs.meta ├── IBrowser.cs ├── IBrowser.cs.meta ├── IOffscreen.cs ├── IOffscreen.cs.meta ├── NativePlugin.cs ├── NativePlugin.cs.meta ├── Sample.meta ├── Sample │ ├── BrowserBGSample.cs │ ├── BrowserBGSample.cs.meta │ ├── BrowserSample.cs │ ├── BrowserSample.cs.meta │ ├── CreateNewInRuntime.cs │ ├── CreateNewInRuntime.cs.meta │ ├── DownloadHandlerSample.cs │ ├── DownloadHandlerSample.cs.meta │ ├── FocusInOutInteractionSample.cs │ ├── FocusInOutInteractionSample.cs.meta │ ├── JSSnippets.cs │ ├── JSSnippets.cs.meta │ ├── LoadLocalFileSample.cs │ ├── LoadLocalFileSample.cs.meta │ ├── Util.meta │ ├── Util │ │ ├── FPSCounter.cs │ │ └── FPSCounter.cs.meta │ ├── Widget.meta │ └── Widget │ │ ├── AlertDialogSample.cs │ │ ├── AlertDialogSample.cs.meta │ │ ├── AuthWidgetSample.cs │ │ ├── AuthWidgetSample.cs.meta │ │ ├── ColorWidgetSample.cs │ │ ├── ColorWidgetSample.cs.meta │ │ ├── DateTimeWidgetSample.cs │ │ ├── DateTimeWidgetSample.cs.meta │ │ ├── SelectWidgetSample.cs │ │ └── SelectWidgetSample.cs.meta ├── SearchBar.cs ├── SearchBar.cs.meta ├── TLab.WebView.Runtime.asmdef ├── TLab.WebView.Runtime.asmdef.meta ├── Test.meta ├── Test │ ├── BrowserAPITest.cs │ ├── BrowserAPITest.cs.meta │ ├── LoadUnloadTest.cs │ └── LoadUnloadTest.cs.meta ├── WebView.cs ├── WebView.cs.meta ├── Widget.meta └── Widget │ ├── AlertDialog.cs │ ├── AlertDialog.cs.meta │ ├── AuthWidget.cs │ ├── AuthWidget.cs.meta │ ├── ColorWidget.cs │ ├── ColorWidget.cs.meta │ ├── Common.cs │ ├── Common.cs.meta │ ├── DateTimeWidget.cs │ ├── DateTimeWidget.cs.meta │ ├── NumberPicker.cs │ ├── NumberPicker.cs.meta │ ├── SelectWidget.cs │ ├── SelectWidget.cs.meta │ ├── TextWidget.cs │ ├── TextWidget.cs.meta │ ├── Widget.cs │ └── Widget.cs.meta ├── Samples.meta ├── Samples ├── Scenes.meta └── Scenes │ ├── Load Unload Sample.unity │ ├── Load Unload Sample.unity.meta │ ├── Mobile Sample.unity │ └── Mobile Sample.unity.meta ├── merge.bat ├── merge.bat.meta ├── package.json └── package.json.meta /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2929ddabb58e75f408b2839325c240b7 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/MediationAdapterDependencies.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | https://unity3ddist.jfrog.io/artifactory/unity-mediation-mvn-prod-local/ 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Editor/MediationAdapterDependencies.xml.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d46ce131972a0564f923f45bb8f3217f 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e06d850e31bed0242a6f287cefcc0293 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Scripts/UnityWebViewPostprocessBuild.cs: -------------------------------------------------------------------------------- 1 | /*** 2 | * This code is adapted from 3 | * https://github.com/gree/unity-webview/blob/master/dist/package/Assets/Plugins/Editor/UnityWebViewPostprocessBuild.cs 4 | **/ 5 | 6 | #if UNITY_EDITOR 7 | using System.IO; 8 | using System.Text; 9 | using System.Xml; 10 | using UnityEngine; 11 | using UnityEditor.Android; 12 | 13 | namespace TLab.WebView.Editor 14 | { 15 | #if UNITY_2018_1_OR_NEWER 16 | public class UnityWebViewPostprocessBuild : IPostGenerateGradleAndroidProject 17 | { 18 | //// for android/unity 2018.1 or newer 19 | //// cf. https://forum.unity.com/threads/android-hardwareaccelerated-is-forced-false-in-all-activities.532786/ 20 | //// cf. https://github.com/Over17/UnityAndroidManifestCallback 21 | 22 | // This function called from unity when Android Gradle project is generated and before building begins. 23 | public void OnPostGenerateGradleAndroidProject(string basePath) 24 | { 25 | // ---------------------------------------------------------------------------------- 26 | // Rewrite AndroidManifest.xml 27 | // 28 | 29 | var changed = false; 30 | var androidManifest = new AndroidManifest(GetManifestPath(basePath)); 31 | 32 | changed = androidManifest.AddApplicationElement("supportsRtl", true) || changed; 33 | 34 | changed = androidManifest.AddApplicationElement("allowBackup", true) || changed; 35 | 36 | changed = androidManifest.SetExported(true) || changed; 37 | 38 | changed = androidManifest.SetHardwareAccelerated(true) || changed; 39 | 40 | #if UNITYWEBVIEW_ANDROID_USES_CLEARTEXT_TRAFFIC 41 | changed = androidManifest.SetUsesCleartextTraffic(true) || changed; 42 | #endif 43 | 44 | #if UNITYWEBVIEW_ANDROID_ENABLE_CAMERA 45 | changed = androidManifest.AddCamera() || changed; 46 | changed = androidManifest.AddGallery() || changed; 47 | #endif 48 | 49 | #if UNITYWEBVIEW_ANDROID_ENABLE_MICROPHONE 50 | changed = androidManifest.AddMicrophone() || changed; 51 | #endif 52 | 53 | if (changed) 54 | { 55 | var result = androidManifest.Save(); 56 | Debug.Log("AndroidManifest.xml overwrite complete: " + result); 57 | } 58 | } 59 | 60 | 61 | public int callbackOrder 62 | { 63 | get 64 | { 65 | return 1; 66 | } 67 | } 68 | 69 | // ---------------------------------------------------------------------------------- 70 | // Get each file 71 | // 72 | 73 | private string GetManifestPath(string basePath) 74 | { 75 | var pathBuilder = new StringBuilder(basePath); 76 | pathBuilder.Append(Path.DirectorySeparatorChar).Append("src"); 77 | pathBuilder.Append(Path.DirectorySeparatorChar).Append("main"); 78 | pathBuilder.Append(Path.DirectorySeparatorChar).Append("AndroidManifest.xml"); 79 | var pathString = pathBuilder.ToString(); 80 | Debug.Log("android custom manifest path: " + pathString); 81 | return pathString; 82 | } 83 | } 84 | 85 | internal class AndroidXmlDocument : XmlDocument 86 | { 87 | private string m_Path; 88 | protected XmlNamespaceManager nsMgr; 89 | public readonly string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android"; 90 | 91 | public AndroidXmlDocument(string path) 92 | { 93 | m_Path = path; 94 | using (var reader = new XmlTextReader(m_Path)) 95 | { 96 | reader.Read(); 97 | Load(reader); 98 | } 99 | nsMgr = new XmlNamespaceManager(NameTable); 100 | nsMgr.AddNamespace("android", AndroidXmlNamespace); 101 | } 102 | 103 | public string Save() 104 | { 105 | return SaveAs(m_Path); 106 | } 107 | 108 | public string SaveAs(string path) 109 | { 110 | using (var writer = new XmlTextWriter(path, new UTF8Encoding(false))) 111 | { 112 | writer.Formatting = Formatting.Indented; 113 | Save(writer); 114 | } 115 | return path; 116 | } 117 | } 118 | 119 | internal class AndroidManifest : AndroidXmlDocument 120 | { 121 | private readonly XmlElement ManifestElement; 122 | private readonly XmlElement ApplicationElement; 123 | 124 | public AndroidManifest(string path) : base(path) 125 | { 126 | ManifestElement = SelectSingleNode("/manifest") as XmlElement; 127 | ApplicationElement = SelectSingleNode("/manifest/application") as XmlElement; 128 | } 129 | 130 | private XmlAttribute CreateAndroidAttribute(string key, string value) 131 | { 132 | XmlAttribute attr = CreateAttribute("android", key, AndroidXmlNamespace); 133 | attr.Value = value; 134 | return attr; 135 | } 136 | 137 | internal XmlNode GetActivityWithLaunchIntent() 138 | { 139 | return 140 | SelectSingleNode( 141 | "/manifest/application/activity[intent-filter/action/@android:name='android.intent.action.MAIN' and " 142 | + "intent-filter/category/@android:name='android.intent.category.LAUNCHER']", 143 | nsMgr); 144 | } 145 | 146 | internal bool AddApplicationElement(string attribute, bool enabled) 147 | { 148 | var changed = false; 149 | var value = enabled ? "true" : "false"; 150 | 151 | var attributeResult = ApplicationElement.GetAttribute(attribute, AndroidXmlNamespace); 152 | 153 | if (attributeResult == null || attributeResult == "" || attributeResult != value) 154 | { 155 | ApplicationElement.SetAttribute(attribute, AndroidXmlNamespace, value); 156 | changed = true; 157 | } 158 | 159 | Debug.Log($"Succeed in {nameof(ApplicationElement.SetAttribute)}: " + attribute); 160 | return changed; 161 | } 162 | 163 | internal bool SetUsesCleartextTraffic(bool enabled) 164 | { 165 | var changed = false; 166 | var value = enabled ? "true" : "false"; 167 | 168 | #if UNITY_2022_1_OR_NEWER 169 | changed = changed || AddApplicationElement("usesCleartextTraffic", true); 170 | #elif UNITY_2018_1_OR_NEWER 171 | var attributeResult = ApplicationElement.GetAttribute("usesCleartextTraffic", AndroidXmlNamespace); 172 | if (attributeResult != value) 173 | { 174 | ApplicationElement.SetAttribute("usesCleartextTraffic", AndroidXmlNamespace, value); 175 | changed = true; 176 | } 177 | #endif 178 | return changed; 179 | } 180 | 181 | internal bool SetAttributeInLaunchIntent(bool enabled, string key) 182 | { 183 | bool changed = false; 184 | var value = enabled ? "true" : "false"; 185 | 186 | var activity = GetActivityWithLaunchIntent() as XmlElement; 187 | if (activity != null) 188 | { 189 | var attributeResult = activity.HasAttribute(key) ? activity.GetAttribute(key, AndroidXmlNamespace) : null; 190 | if (attributeResult != value) 191 | { 192 | activity.SetAttribute(key, AndroidXmlNamespace, value); 193 | changed = true; 194 | } 195 | } 196 | else 197 | { 198 | Debug.LogWarning("Activity not found"); 199 | } 200 | return changed; 201 | } 202 | 203 | // For API level 33 204 | internal bool SetExported(bool enabled) => 205 | SetAttributeInLaunchIntent(enabled, "exported"); 206 | 207 | internal bool SetHardwareAccelerated(bool enabled) => 208 | SetAttributeInLaunchIntent(enabled, "hardwareAccelerated"); 209 | 210 | internal bool AddCamera() 211 | { 212 | bool changed = false; 213 | if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.CAMERA']", nsMgr).Count == 0) 214 | { 215 | var elem = CreateElement("uses-permission"); 216 | elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.CAMERA")); 217 | ManifestElement.AppendChild(elem); 218 | changed = true; 219 | } 220 | if (SelectNodes("/manifest/uses-feature[@android:name='android.hardware.camera']", nsMgr).Count == 0) 221 | { 222 | var elem = CreateElement("uses-feature"); 223 | elem.Attributes.Append(CreateAndroidAttribute("name", "android.hardware.camera")); 224 | ManifestElement.AppendChild(elem); 225 | changed = true; 226 | } 227 | // cf. https://developer.android.com/training/data-storage/shared/media#media-location-permission 228 | if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.ACCESS_MEDIA_LOCATION']", nsMgr).Count == 0) 229 | { 230 | var elem = CreateElement("uses-permission"); 231 | elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.ACCESS_MEDIA_LOCATION")); 232 | ManifestElement.AppendChild(elem); 233 | changed = true; 234 | } 235 | // cf. https://developer.android.com/training/package-visibility/declaring 236 | if (SelectNodes("/manifest/queries", nsMgr).Count == 0) 237 | { 238 | var elem = CreateElement("queries"); 239 | ManifestElement.AppendChild(elem); 240 | changed = true; 241 | } 242 | if (SelectNodes("/manifest/queries/intent/action[@android:name='android.media.action.IMAGE_CAPTURE']", nsMgr).Count == 0) 243 | { 244 | var action = CreateElement("action"); 245 | action.Attributes.Append(CreateAndroidAttribute("name", "android.media.action.IMAGE_CAPTURE")); 246 | var intent = CreateElement("intent"); 247 | intent.AppendChild(action); 248 | var queries = SelectSingleNode("/manifest/queries") as XmlElement; 249 | queries.AppendChild(intent); 250 | changed = true; 251 | } 252 | return changed; 253 | } 254 | 255 | internal bool AddGallery() 256 | { 257 | bool changed = false; 258 | // for api level 33 259 | if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.READ_MEDIA_IMAGES']", nsMgr).Count == 0) 260 | { 261 | var elem = CreateElement("uses-permission"); 262 | elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.READ_MEDIA_IMAGES")); 263 | ManifestElement.AppendChild(elem); 264 | changed = true; 265 | } 266 | if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.READ_MEDIA_VIDEO']", nsMgr).Count == 0) 267 | { 268 | var elem = CreateElement("uses-permission"); 269 | elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.READ_MEDIA_VIDEO")); 270 | ManifestElement.AppendChild(elem); 271 | changed = true; 272 | } 273 | if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.READ_MEDIA_AUDIO']", nsMgr).Count == 0) 274 | { 275 | var elem = CreateElement("uses-permission"); 276 | elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.READ_MEDIA_AUDIO")); 277 | ManifestElement.AppendChild(elem); 278 | changed = true; 279 | } 280 | // cf. https://developer.android.com/training/package-visibility/declaring 281 | if (SelectNodes("/manifest/queries", nsMgr).Count == 0) 282 | { 283 | var elem = CreateElement("queries"); 284 | ManifestElement.AppendChild(elem); 285 | changed = true; 286 | } 287 | if (SelectNodes("/manifest/queries/intent/action[@android:name='android.media.action.GET_CONTENT']", nsMgr).Count == 0) 288 | { 289 | var action = CreateElement("action"); 290 | action.Attributes.Append(CreateAndroidAttribute("name", "android.media.action.GET_CONTENT")); 291 | var intent = CreateElement("intent"); 292 | intent.AppendChild(action); 293 | var queries = SelectSingleNode("/manifest/queries") as XmlElement; 294 | queries.AppendChild(intent); 295 | changed = true; 296 | } 297 | return changed; 298 | } 299 | 300 | internal bool AddMicrophone() 301 | { 302 | bool changed = false; 303 | if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.MICROPHONE']", nsMgr).Count == 0) 304 | { 305 | var elem = CreateElement("uses-permission"); 306 | elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.MICROPHONE")); 307 | ManifestElement.AppendChild(elem); 308 | changed = true; 309 | } 310 | if (SelectNodes("/manifest/uses-feature[@android:name='android.hardware.microphone']", nsMgr).Count == 0) 311 | { 312 | var elem = CreateElement("uses-feature"); 313 | elem.Attributes.Append(CreateAndroidAttribute("name", "android.hardware.microphone")); 314 | ManifestElement.AppendChild(elem); 315 | changed = true; 316 | } 317 | // cf. https://github.com/gree/unity-webview/issues/679 318 | // cf. https://github.com/fluttercommunity/flutter_webview_plugin/issues/138#issuecomment-559307558 319 | // cf. https://stackoverflow.com/questions/38917751/webview-webrtc-not-working/68024032#68024032 320 | // cf. https://stackoverflow.com/questions/40236925/allowing-microphone-accesspermission-in-webview-android-studio-java/47410311#47410311 321 | if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.MODIFY_AUDIO_SETTINGS']", nsMgr).Count == 0) 322 | { 323 | var elem = CreateElement("uses-permission"); 324 | elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.MODIFY_AUDIO_SETTINGS")); 325 | ManifestElement.AppendChild(elem); 326 | changed = true; 327 | } 328 | if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.RECORD_AUDIO']", nsMgr).Count == 0) 329 | { 330 | var elem = CreateElement("uses-permission"); 331 | elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.RECORD_AUDIO")); 332 | ManifestElement.AppendChild(elem); 333 | changed = true; 334 | } 335 | return changed; 336 | } 337 | } 338 | #endif 339 | } 340 | #endif -------------------------------------------------------------------------------- /Editor/Scripts/UnityWebViewPostprocessBuild.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c1f38ba5c49717146a8b45aa13753678 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/TLab.WebView.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TLab.WebView.Editor", 3 | "rootNamespace": "", 4 | "references": [ 5 | "GUID:643142c4527239447a4feb8d9fba651e" 6 | ], 7 | "includePlatforms": [ 8 | "Editor" 9 | ], 10 | "excludePlatforms": [], 11 | "allowUnsafeCode": false, 12 | "overrideReferences": false, 13 | "precompiledReferences": [], 14 | "autoReferenced": true, 15 | "defineConstraints": [], 16 | "versionDefines": [], 17 | "noEngineReferences": false 18 | } -------------------------------------------------------------------------------- /Editor/TLab.WebView.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c70208384e755dc47bf7357c1918d1be 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 tlabaltoh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9ec5c2a3889e691439f3950d0e26d6ff 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Media.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 86f9a35fea2d5e34186fb1d8e032be3b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Media/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TLabAltoh/TLabWebView/0323d5f672b79ce14676e321757f09328be1c343/Media/header.png -------------------------------------------------------------------------------- /Media/header.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 589eba7235633cc47af2a18bf1db88d2 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | vTOnly: 0 27 | ignoreMasterTextureLimit: 0 28 | grayScaleToAlpha: 0 29 | generateCubemap: 6 30 | cubemapConvolution: 0 31 | seamlessCubemap: 0 32 | textureFormat: 1 33 | maxTextureSize: 2048 34 | textureSettings: 35 | serializedVersion: 2 36 | filterMode: 1 37 | aniso: 1 38 | mipBias: 0 39 | wrapU: 0 40 | wrapV: 0 41 | wrapW: 0 42 | nPOTScale: 1 43 | lightmap: 0 44 | compressionQuality: 50 45 | spriteMode: 0 46 | spriteExtrude: 1 47 | spriteMeshType: 1 48 | alignment: 0 49 | spritePivot: {x: 0.5, y: 0.5} 50 | spritePixelsToUnits: 100 51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 52 | spriteGenerateFallbackPhysicsShape: 1 53 | alphaUsage: 1 54 | alphaIsTransparency: 0 55 | spriteTessellationDetail: -1 56 | textureType: 0 57 | textureShape: 1 58 | singleChannelComponent: 0 59 | flipbookRows: 1 60 | flipbookColumns: 1 61 | maxTextureSizeSet: 0 62 | compressionQualitySet: 0 63 | textureFormatSet: 0 64 | ignorePngGamma: 0 65 | applyGammaDecoding: 0 66 | cookieLightType: 0 67 | platformSettings: 68 | - serializedVersion: 3 69 | buildTarget: DefaultTexturePlatform 70 | maxTextureSize: 2048 71 | resizeAlgorithm: 0 72 | textureFormat: -1 73 | textureCompression: 1 74 | compressionQuality: 50 75 | crunchedCompression: 0 76 | allowsAlphaSplitting: 0 77 | overridden: 0 78 | androidETC2FallbackOverride: 0 79 | forceMaximumCompressionQuality_BC6H_BC7: 0 80 | - serializedVersion: 3 81 | buildTarget: Standalone 82 | maxTextureSize: 2048 83 | resizeAlgorithm: 0 84 | textureFormat: -1 85 | textureCompression: 1 86 | compressionQuality: 50 87 | crunchedCompression: 0 88 | allowsAlphaSplitting: 0 89 | overridden: 0 90 | androidETC2FallbackOverride: 0 91 | forceMaximumCompressionQuality_BC6H_BC7: 0 92 | - serializedVersion: 3 93 | buildTarget: WebGL 94 | maxTextureSize: 2048 95 | resizeAlgorithm: 0 96 | textureFormat: -1 97 | textureCompression: 1 98 | compressionQuality: 50 99 | crunchedCompression: 0 100 | allowsAlphaSplitting: 0 101 | overridden: 0 102 | androidETC2FallbackOverride: 0 103 | forceMaximumCompressionQuality_BC6H_BC7: 0 104 | - serializedVersion: 3 105 | buildTarget: Android 106 | maxTextureSize: 2048 107 | resizeAlgorithm: 0 108 | textureFormat: -1 109 | textureCompression: 1 110 | compressionQuality: 50 111 | crunchedCompression: 0 112 | allowsAlphaSplitting: 0 113 | overridden: 0 114 | androidETC2FallbackOverride: 0 115 | forceMaximumCompressionQuality_BC6H_BC7: 0 116 | spriteSheet: 117 | serializedVersion: 2 118 | sprites: [] 119 | outline: [] 120 | physicsShape: [] 121 | bones: [] 122 | spriteID: 123 | internalID: 0 124 | vertices: [] 125 | indices: 126 | edges: [] 127 | weights: [] 128 | secondaryTextures: [] 129 | nameFileIdTable: {} 130 | spritePackingTag: 131 | pSDRemoveMatte: 0 132 | pSDShowRemoveMatteOption: 0 133 | userData: 134 | assetBundleName: 135 | assetBundleVariant: 136 | -------------------------------------------------------------------------------- /Media/image.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TLabAltoh/TLabWebView/0323d5f672b79ce14676e321757f09328be1c343/Media/image.0.png -------------------------------------------------------------------------------- /Media/image.0.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0afdd9fb8c8054c47898b73ab22c5ff5 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | vTOnly: 0 27 | ignoreMasterTextureLimit: 0 28 | grayScaleToAlpha: 0 29 | generateCubemap: 6 30 | cubemapConvolution: 0 31 | seamlessCubemap: 0 32 | textureFormat: 1 33 | maxTextureSize: 2048 34 | textureSettings: 35 | serializedVersion: 2 36 | filterMode: 1 37 | aniso: 1 38 | mipBias: 0 39 | wrapU: 0 40 | wrapV: 0 41 | wrapW: 0 42 | nPOTScale: 1 43 | lightmap: 0 44 | compressionQuality: 50 45 | spriteMode: 0 46 | spriteExtrude: 1 47 | spriteMeshType: 1 48 | alignment: 0 49 | spritePivot: {x: 0.5, y: 0.5} 50 | spritePixelsToUnits: 100 51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 52 | spriteGenerateFallbackPhysicsShape: 1 53 | alphaUsage: 1 54 | alphaIsTransparency: 0 55 | spriteTessellationDetail: -1 56 | textureType: 0 57 | textureShape: 1 58 | singleChannelComponent: 0 59 | flipbookRows: 1 60 | flipbookColumns: 1 61 | maxTextureSizeSet: 0 62 | compressionQualitySet: 0 63 | textureFormatSet: 0 64 | ignorePngGamma: 0 65 | applyGammaDecoding: 0 66 | cookieLightType: 0 67 | platformSettings: 68 | - serializedVersion: 3 69 | buildTarget: DefaultTexturePlatform 70 | maxTextureSize: 2048 71 | resizeAlgorithm: 0 72 | textureFormat: -1 73 | textureCompression: 1 74 | compressionQuality: 50 75 | crunchedCompression: 0 76 | allowsAlphaSplitting: 0 77 | overridden: 0 78 | androidETC2FallbackOverride: 0 79 | forceMaximumCompressionQuality_BC6H_BC7: 0 80 | - serializedVersion: 3 81 | buildTarget: Standalone 82 | maxTextureSize: 2048 83 | resizeAlgorithm: 0 84 | textureFormat: -1 85 | textureCompression: 1 86 | compressionQuality: 50 87 | crunchedCompression: 0 88 | allowsAlphaSplitting: 0 89 | overridden: 0 90 | androidETC2FallbackOverride: 0 91 | forceMaximumCompressionQuality_BC6H_BC7: 0 92 | - serializedVersion: 3 93 | buildTarget: WebGL 94 | maxTextureSize: 2048 95 | resizeAlgorithm: 0 96 | textureFormat: -1 97 | textureCompression: 1 98 | compressionQuality: 50 99 | crunchedCompression: 0 100 | allowsAlphaSplitting: 0 101 | overridden: 0 102 | androidETC2FallbackOverride: 0 103 | forceMaximumCompressionQuality_BC6H_BC7: 0 104 | - serializedVersion: 3 105 | buildTarget: Android 106 | maxTextureSize: 2048 107 | resizeAlgorithm: 0 108 | textureFormat: -1 109 | textureCompression: 1 110 | compressionQuality: 50 111 | crunchedCompression: 0 112 | allowsAlphaSplitting: 0 113 | overridden: 0 114 | androidETC2FallbackOverride: 0 115 | forceMaximumCompressionQuality_BC6H_BC7: 0 116 | spriteSheet: 117 | serializedVersion: 2 118 | sprites: [] 119 | outline: [] 120 | physicsShape: [] 121 | bones: [] 122 | spriteID: 123 | internalID: 0 124 | vertices: [] 125 | indices: 126 | edges: [] 127 | weights: [] 128 | secondaryTextures: [] 129 | nameFileIdTable: {} 130 | spritePackingTag: 131 | pSDRemoveMatte: 0 132 | pSDShowRemoveMatteOption: 0 133 | userData: 134 | assetBundleName: 135 | assetBundleVariant: 136 | -------------------------------------------------------------------------------- /Media/tlab-webview-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TLabAltoh/TLabWebView/0323d5f672b79ce14676e321757f09328be1c343/Media/tlab-webview-settings.png -------------------------------------------------------------------------------- /Media/tlab-webview-settings.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 43a59d8dd443d2740a8d5acd0370294c 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | vTOnly: 0 27 | ignoreMasterTextureLimit: 0 28 | grayScaleToAlpha: 0 29 | generateCubemap: 6 30 | cubemapConvolution: 0 31 | seamlessCubemap: 0 32 | textureFormat: 1 33 | maxTextureSize: 2048 34 | textureSettings: 35 | serializedVersion: 2 36 | filterMode: 1 37 | aniso: 1 38 | mipBias: 0 39 | wrapU: 0 40 | wrapV: 0 41 | wrapW: 0 42 | nPOTScale: 1 43 | lightmap: 0 44 | compressionQuality: 50 45 | spriteMode: 0 46 | spriteExtrude: 1 47 | spriteMeshType: 1 48 | alignment: 0 49 | spritePivot: {x: 0.5, y: 0.5} 50 | spritePixelsToUnits: 100 51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 52 | spriteGenerateFallbackPhysicsShape: 1 53 | alphaUsage: 1 54 | alphaIsTransparency: 0 55 | spriteTessellationDetail: -1 56 | textureType: 0 57 | textureShape: 1 58 | singleChannelComponent: 0 59 | flipbookRows: 1 60 | flipbookColumns: 1 61 | maxTextureSizeSet: 0 62 | compressionQualitySet: 0 63 | textureFormatSet: 0 64 | ignorePngGamma: 0 65 | applyGammaDecoding: 0 66 | cookieLightType: 0 67 | platformSettings: 68 | - serializedVersion: 3 69 | buildTarget: DefaultTexturePlatform 70 | maxTextureSize: 2048 71 | resizeAlgorithm: 0 72 | textureFormat: -1 73 | textureCompression: 1 74 | compressionQuality: 50 75 | crunchedCompression: 0 76 | allowsAlphaSplitting: 0 77 | overridden: 0 78 | androidETC2FallbackOverride: 0 79 | forceMaximumCompressionQuality_BC6H_BC7: 0 80 | - serializedVersion: 3 81 | buildTarget: Standalone 82 | maxTextureSize: 2048 83 | resizeAlgorithm: 0 84 | textureFormat: -1 85 | textureCompression: 1 86 | compressionQuality: 50 87 | crunchedCompression: 0 88 | allowsAlphaSplitting: 0 89 | overridden: 0 90 | androidETC2FallbackOverride: 0 91 | forceMaximumCompressionQuality_BC6H_BC7: 0 92 | - serializedVersion: 3 93 | buildTarget: Server 94 | maxTextureSize: 2048 95 | resizeAlgorithm: 0 96 | textureFormat: -1 97 | textureCompression: 1 98 | compressionQuality: 50 99 | crunchedCompression: 0 100 | allowsAlphaSplitting: 0 101 | overridden: 0 102 | androidETC2FallbackOverride: 0 103 | forceMaximumCompressionQuality_BC6H_BC7: 0 104 | - serializedVersion: 3 105 | buildTarget: Android 106 | maxTextureSize: 2048 107 | resizeAlgorithm: 0 108 | textureFormat: -1 109 | textureCompression: 1 110 | compressionQuality: 50 111 | crunchedCompression: 0 112 | allowsAlphaSplitting: 0 113 | overridden: 0 114 | androidETC2FallbackOverride: 0 115 | forceMaximumCompressionQuality_BC6H_BC7: 0 116 | - serializedVersion: 3 117 | buildTarget: WebGL 118 | maxTextureSize: 2048 119 | resizeAlgorithm: 0 120 | textureFormat: -1 121 | textureCompression: 1 122 | compressionQuality: 50 123 | crunchedCompression: 0 124 | allowsAlphaSplitting: 0 125 | overridden: 0 126 | androidETC2FallbackOverride: 0 127 | forceMaximumCompressionQuality_BC6H_BC7: 0 128 | - serializedVersion: 3 129 | buildTarget: Windows Store Apps 130 | maxTextureSize: 2048 131 | resizeAlgorithm: 0 132 | textureFormat: -1 133 | textureCompression: 1 134 | compressionQuality: 50 135 | crunchedCompression: 0 136 | allowsAlphaSplitting: 0 137 | overridden: 0 138 | androidETC2FallbackOverride: 0 139 | forceMaximumCompressionQuality_BC6H_BC7: 0 140 | spriteSheet: 141 | serializedVersion: 2 142 | sprites: [] 143 | outline: [] 144 | physicsShape: [] 145 | bones: [] 146 | spriteID: 147 | internalID: 0 148 | vertices: [] 149 | indices: 150 | edges: [] 151 | weights: [] 152 | secondaryTextures: [] 153 | nameFileIdTable: {} 154 | spritePackingTag: 155 | pSDRemoveMatte: 0 156 | pSDShowRemoveMatteOption: 0 157 | userData: 158 | assetBundleName: 159 | assetBundleVariant: 160 | -------------------------------------------------------------------------------- /Media/tlab-webview-vr.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TLabAltoh/TLabWebView/0323d5f672b79ce14676e321757f09328be1c343/Media/tlab-webview-vr.gif -------------------------------------------------------------------------------- /Media/tlab-webview-vr.gif.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: df176f80dbdf58940b966a8978e143af 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | vTOnly: 0 27 | ignoreMasterTextureLimit: 0 28 | grayScaleToAlpha: 0 29 | generateCubemap: 6 30 | cubemapConvolution: 0 31 | seamlessCubemap: 0 32 | textureFormat: 1 33 | maxTextureSize: 2048 34 | textureSettings: 35 | serializedVersion: 2 36 | filterMode: 1 37 | aniso: 1 38 | mipBias: 0 39 | wrapU: 0 40 | wrapV: 0 41 | wrapW: 0 42 | nPOTScale: 1 43 | lightmap: 0 44 | compressionQuality: 50 45 | spriteMode: 0 46 | spriteExtrude: 1 47 | spriteMeshType: 1 48 | alignment: 0 49 | spritePivot: {x: 0.5, y: 0.5} 50 | spritePixelsToUnits: 100 51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 52 | spriteGenerateFallbackPhysicsShape: 1 53 | alphaUsage: 1 54 | alphaIsTransparency: 0 55 | spriteTessellationDetail: -1 56 | textureType: 0 57 | textureShape: 1 58 | singleChannelComponent: 0 59 | flipbookRows: 1 60 | flipbookColumns: 1 61 | maxTextureSizeSet: 0 62 | compressionQualitySet: 0 63 | textureFormatSet: 0 64 | ignorePngGamma: 0 65 | applyGammaDecoding: 0 66 | cookieLightType: 0 67 | platformSettings: 68 | - serializedVersion: 3 69 | buildTarget: DefaultTexturePlatform 70 | maxTextureSize: 2048 71 | resizeAlgorithm: 0 72 | textureFormat: -1 73 | textureCompression: 1 74 | compressionQuality: 50 75 | crunchedCompression: 0 76 | allowsAlphaSplitting: 0 77 | overridden: 0 78 | androidETC2FallbackOverride: 0 79 | forceMaximumCompressionQuality_BC6H_BC7: 0 80 | - serializedVersion: 3 81 | buildTarget: Standalone 82 | maxTextureSize: 2048 83 | resizeAlgorithm: 0 84 | textureFormat: -1 85 | textureCompression: 1 86 | compressionQuality: 50 87 | crunchedCompression: 0 88 | allowsAlphaSplitting: 0 89 | overridden: 0 90 | androidETC2FallbackOverride: 0 91 | forceMaximumCompressionQuality_BC6H_BC7: 0 92 | - serializedVersion: 3 93 | buildTarget: WebGL 94 | maxTextureSize: 2048 95 | resizeAlgorithm: 0 96 | textureFormat: -1 97 | textureCompression: 1 98 | compressionQuality: 50 99 | crunchedCompression: 0 100 | allowsAlphaSplitting: 0 101 | overridden: 0 102 | androidETC2FallbackOverride: 0 103 | forceMaximumCompressionQuality_BC6H_BC7: 0 104 | - serializedVersion: 3 105 | buildTarget: Android 106 | maxTextureSize: 2048 107 | resizeAlgorithm: 0 108 | textureFormat: -1 109 | textureCompression: 1 110 | compressionQuality: 50 111 | crunchedCompression: 0 112 | allowsAlphaSplitting: 0 113 | overridden: 0 114 | androidETC2FallbackOverride: 0 115 | forceMaximumCompressionQuality_BC6H_BC7: 0 116 | spriteSheet: 117 | serializedVersion: 2 118 | sprites: [] 119 | outline: [] 120 | physicsShape: [] 121 | bones: [] 122 | spriteID: 123 | internalID: 0 124 | vertices: [] 125 | indices: 126 | edges: [] 127 | weights: [] 128 | secondaryTextures: [] 129 | nameFileIdTable: {} 130 | spritePackingTag: 131 | pSDRemoveMatte: 0 132 | pSDShowRemoveMatteOption: 0 133 | userData: 134 | assetBundleName: 135 | assetBundleVariant: 136 | -------------------------------------------------------------------------------- /Media/tlab-webview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TLabAltoh/TLabWebView/0323d5f672b79ce14676e321757f09328be1c343/Media/tlab-webview.png -------------------------------------------------------------------------------- /Media/tlab-webview.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bfcb64e86b0f42b4193a92fefbf781de 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | vTOnly: 0 27 | ignoreMasterTextureLimit: 0 28 | grayScaleToAlpha: 0 29 | generateCubemap: 6 30 | cubemapConvolution: 0 31 | seamlessCubemap: 0 32 | textureFormat: 1 33 | maxTextureSize: 2048 34 | textureSettings: 35 | serializedVersion: 2 36 | filterMode: 1 37 | aniso: 1 38 | mipBias: 0 39 | wrapU: 0 40 | wrapV: 0 41 | wrapW: 0 42 | nPOTScale: 1 43 | lightmap: 0 44 | compressionQuality: 50 45 | spriteMode: 0 46 | spriteExtrude: 1 47 | spriteMeshType: 1 48 | alignment: 0 49 | spritePivot: {x: 0.5, y: 0.5} 50 | spritePixelsToUnits: 100 51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 52 | spriteGenerateFallbackPhysicsShape: 1 53 | alphaUsage: 1 54 | alphaIsTransparency: 0 55 | spriteTessellationDetail: -1 56 | textureType: 0 57 | textureShape: 1 58 | singleChannelComponent: 0 59 | flipbookRows: 1 60 | flipbookColumns: 1 61 | maxTextureSizeSet: 0 62 | compressionQualitySet: 0 63 | textureFormatSet: 0 64 | ignorePngGamma: 0 65 | applyGammaDecoding: 0 66 | cookieLightType: 0 67 | platformSettings: 68 | - serializedVersion: 3 69 | buildTarget: DefaultTexturePlatform 70 | maxTextureSize: 2048 71 | resizeAlgorithm: 0 72 | textureFormat: -1 73 | textureCompression: 1 74 | compressionQuality: 50 75 | crunchedCompression: 0 76 | allowsAlphaSplitting: 0 77 | overridden: 0 78 | androidETC2FallbackOverride: 0 79 | forceMaximumCompressionQuality_BC6H_BC7: 0 80 | - serializedVersion: 3 81 | buildTarget: Standalone 82 | maxTextureSize: 2048 83 | resizeAlgorithm: 0 84 | textureFormat: -1 85 | textureCompression: 1 86 | compressionQuality: 50 87 | crunchedCompression: 0 88 | allowsAlphaSplitting: 0 89 | overridden: 0 90 | androidETC2FallbackOverride: 0 91 | forceMaximumCompressionQuality_BC6H_BC7: 0 92 | - serializedVersion: 3 93 | buildTarget: Server 94 | maxTextureSize: 2048 95 | resizeAlgorithm: 0 96 | textureFormat: -1 97 | textureCompression: 1 98 | compressionQuality: 50 99 | crunchedCompression: 0 100 | allowsAlphaSplitting: 0 101 | overridden: 0 102 | androidETC2FallbackOverride: 0 103 | forceMaximumCompressionQuality_BC6H_BC7: 0 104 | - serializedVersion: 3 105 | buildTarget: Android 106 | maxTextureSize: 2048 107 | resizeAlgorithm: 0 108 | textureFormat: -1 109 | textureCompression: 1 110 | compressionQuality: 50 111 | crunchedCompression: 0 112 | allowsAlphaSplitting: 0 113 | overridden: 0 114 | androidETC2FallbackOverride: 0 115 | forceMaximumCompressionQuality_BC6H_BC7: 0 116 | - serializedVersion: 3 117 | buildTarget: WebGL 118 | maxTextureSize: 2048 119 | resizeAlgorithm: 0 120 | textureFormat: -1 121 | textureCompression: 1 122 | compressionQuality: 50 123 | crunchedCompression: 0 124 | allowsAlphaSplitting: 0 125 | overridden: 0 126 | androidETC2FallbackOverride: 0 127 | forceMaximumCompressionQuality_BC6H_BC7: 0 128 | - serializedVersion: 3 129 | buildTarget: Windows Store Apps 130 | maxTextureSize: 2048 131 | resizeAlgorithm: 0 132 | textureFormat: -1 133 | textureCompression: 1 134 | compressionQuality: 50 135 | crunchedCompression: 0 136 | allowsAlphaSplitting: 0 137 | overridden: 0 138 | androidETC2FallbackOverride: 0 139 | forceMaximumCompressionQuality_BC6H_BC7: 0 140 | spriteSheet: 141 | serializedVersion: 2 142 | sprites: [] 143 | outline: [] 144 | physicsShape: [] 145 | bones: [] 146 | spriteID: 147 | internalID: 0 148 | vertices: [] 149 | indices: 150 | edges: [] 151 | weights: [] 152 | secondaryTextures: [] 153 | nameFileIdTable: {} 154 | spritePackingTag: 155 | pSDRemoveMatte: 0 156 | pSDShowRemoveMatteOption: 0 157 | userData: 158 | assetBundleName: 159 | assetBundleVariant: 160 | -------------------------------------------------------------------------------- /Plugins.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4a6dd89a1b1090f4281bdd3b7fabecd2 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Plugins/Android.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fbc52ad3b7e859b43a70296fa0135f3e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Plugins/Android/copy.bat: -------------------------------------------------------------------------------- 1 | copy "%USERPROFILE%\source\tmp\TLabWebViewPlugin\libTLabWebView\build\outputs\aar\libTLabWebView-release.aar" . 2 | clear 3 | ls -lia 4 | -------------------------------------------------------------------------------- /Plugins/Android/copy.bat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 32f7b1757940a77489f8347723c2b9e3 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Plugins/Android/libTLabWebView-release.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TLabAltoh/TLabWebView/0323d5f672b79ce14676e321757f09328be1c343/Plugins/Android/libTLabWebView-release.aar -------------------------------------------------------------------------------- /Plugins/Android/libTLabWebView-release.aar.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8927d9548af8466459a6c2ff0eed6662 3 | PluginImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | iconMap: {} 7 | executionOrder: {} 8 | defineConstraints: [] 9 | isPreloaded: 1 10 | isOverridable: 0 11 | isExplicitlyReferenced: 0 12 | validateReferences: 1 13 | platformData: 14 | - first: 15 | : Any 16 | second: 17 | enabled: 0 18 | settings: 19 | Exclude Android: 0 20 | Exclude Editor: 1 21 | Exclude Linux64: 1 22 | Exclude OSXUniversal: 1 23 | Exclude WebGL: 1 24 | Exclude Win: 1 25 | Exclude Win64: 1 26 | - first: 27 | Android: Android 28 | second: 29 | enabled: 1 30 | settings: 31 | CPU: ARMv7 32 | - first: 33 | Any: 34 | second: 35 | enabled: 0 36 | settings: {} 37 | - first: 38 | Editor: Editor 39 | second: 40 | enabled: 0 41 | settings: 42 | CPU: AnyCPU 43 | DefaultValueInitialized: true 44 | OS: AnyOS 45 | - first: 46 | Standalone: Linux64 47 | second: 48 | enabled: 0 49 | settings: 50 | CPU: AnyCPU 51 | - first: 52 | Standalone: OSXUniversal 53 | second: 54 | enabled: 0 55 | settings: 56 | CPU: AnyCPU 57 | - first: 58 | Standalone: Win 59 | second: 60 | enabled: 0 61 | settings: 62 | CPU: AnyCPU 63 | - first: 64 | Standalone: Win64 65 | second: 66 | enabled: 0 67 | settings: 68 | CPU: AnyCPU 69 | userData: 70 | assetBundleName: 71 | assetBundleVariant: 72 | -------------------------------------------------------------------------------- /README-ja.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # TLabWebView 4 | 5 | Android で使用可能なブラウザコンポーネント ([```WebView```](https://developer.android.com/reference/android/webkit/WebView) / [```GeckoView```](https://mozilla.github.io/geckoview/)) を uGUI (Texture2D) として利用するためのプラグイン.3Dウェブブラウザ (3D WebView) の実装が可能になります. 6 | 7 | - [x] キーボード入力 8 | - [x] タッチ操作 9 | - [x] ファイルダウンロード (blob, data urlを含む) 10 | - [x] リサイズ 11 | - [x] Javascriptの実行 12 | - [x] 複数インスタンスの同時実行をサポート 13 | - [x] 複数のブラウザエンジンをサポート 14 | - [x] [```WebView```](https://developer.android.com/reference/android/webkit/WebView): 安定していて,javascriptインターフェースが充実 15 | - [x] [```GeckoView```](https://mozilla.github.io/geckoview/): WebViewと比べて,拡張性が高い(ポップアップ等) 16 | - [x] 複数のレンダリング方法をサポート 17 | - [x] ```HardwareBudder```: Androidの低レイヤー機能を使用した実装.パフォーマンス⭕️ 18 | - [x] ```ByteBuffer```: C#側でbyte配列として結果を取得するためフレームへのアクセスが容易.安定性⭕️.**デフォルトではこのオプションが使用されています.** 19 | - [x] ```Surface```: Androidの ```Surface``` クラスに直接レンダリングを行う.[```CompositionLayers```](https://docs.unity3d.com/Packages/com.unity.xr.compositionlayers@0.5/manual/usage-guide.html)などを活用する際に利用することを想定 20 | 21 | [ドキュメントはこちら](https://tlabgames.gitbook.io/tlabwebview) 22 | [スニペットはこちら](https://gist.github.com/TLabAltoh/e0512b3367c25d3e1ec28ddbe95da497#file-tlabwebview-snippets-md) 23 | [Javaプラグインのソースコードはこちら](https://github.com/TLabAltoh/TLabWebViewPlugin) 24 | 25 | [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/tlabaltoh) 26 | 27 | ## 対応しているUnityのバージョン 28 | - [x] Unity 2021 29 | - [x] Unity 2022 30 | - [x] Unity 6000 (`WebView`のみテスト済みです.`GeckoView`はまだテストしていません.) 31 | 32 | ## 対応するグラフィックスAPI 33 | - [x] OpenGLES 34 | - [x] Vulkan 35 | 36 | ## スクリーンショット 37 | Android13, Adreno 619で実行した画面 38 | 39 | 40 | 41 | VR サンプル 42 | 43 | 44 | ## 動作環境 45 | 46 | | | | 47 | | ----- | ------------------------ | 48 | | OS | Android 10 ~ 14 | 49 | | GPU | Qualcomm Adreno 505, 619 | 50 | | Unity | 2021.3 | 51 | 52 | ## スタートガイド 53 | 54 | ### 依存するライブラリ 55 | 56 | - [TLabVKeyborad](https://github.com/TLabAltoh/TLabVKeyborad) ```v1.0.1``` 57 | 58 | ### インストール 59 |
こちらをご覧ください 60 | 61 | #### Submodule 62 | 以下のコマンドでリポジトリをクローンしてください 63 | ``` 64 | git clone https://github.com/TLabAltoh/TLabWebView.git 65 | ``` 66 | or 67 | ``` 68 | git submodule add https://github.com/TLabAltoh/TLabWebView.git 69 | ``` 70 | 71 | #### UPM 72 | Unity Package Managerで```add package from git ...```から以下のurlでパッケージをダウンロードしてください 73 | ``` 74 | https://github.com/TLabAltoh/TLabWebView.git#upm 75 | ``` 76 | 77 | #### アプリストア互換性 78 | このプラグインを使用したアプリをアプリストアに出した場合,以下のような警告を受ける場合があります. 79 | 80 | > ### Unsafe SSL override in WebViews 81 | > Your application may contain an unsafe implementation of the WebView's [onReceivedSslError() method](https://www.oculus.com/lynx/?u=https%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fwebkit%2FWebViewClient.html%23onReceivedSslError(android.webkit.WebView%2C%2520android.webkit.SslErrorHandler%2C%2520android.net.http.SslError)&e=AT0HN6RWgLynCRtwcCSOzSVvlpMDUhi7C5saZwaY5p4unt4S4-GxIACJX_OPzTQp1Fn4oADk7Q_rwvZvRiF5XstftUzyuAWAolfkkk_WAtDpvOgW0Llcn_BXIEpgYobFNELMZ31ntKzTQXflaLkeRA) with a call to `handler.proceed() with insufficient validations. This may cause the WebView to ignore SSL certificate validation errors, making the application vulnerable to man-in-the-middle attacks. 82 | > 83 | > https://www.meta.com/experiences/ 84 | 85 | 86 | > ### Security and trust 87 | > #### onReceivedSslError 88 | > your app is using an unsafe implementation of [```WebviewClient.onReceivedSslError```](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%20android.webkit.SslErrorHandler,%20android.net.http.SslError)) handler 89 | > 90 | > https://developer.android.com/distribute/console 91 | 92 | その場合は,アプリストアに対応したバージョンのパッケージに切り替えてください. 93 | 94 | ```add package from git URL ...``` 95 | ``` 96 | https://github.com/TLabAltoh/TLabWebView.git#appstore-compatible-upm 97 | ``` 98 | 99 | アプリストア対応バージョンはセキュアではないウェブサイトを読み込むことができない点をあらかじめご了承ください. 100 | 101 |
102 | 103 | ### セットアップ 104 | 105 |
こちらをご覧ください 106 | 107 | - Build Settings 108 | 109 | | Property | Value | 110 | | -------- | ------- | 111 | | Platform | Android | 112 | 113 | - Project Settings 114 | 115 | | Property | Value | 116 | | ----------------- | ------------------------------------- | 117 | | Color Space | Linear | 118 | | Minimum API Level | 26 | 119 | | Target API Level | 30 (Unity 2021), 31 ~ 32 (Unity 2022) | 120 | 121 | 122 | - Project Settings --> Player --> Other Settings に以下のシンボルを追加(ビルド時に使用) 123 | 124 | ``` 125 | UNITYWEBVIEW_ANDROID_USES_CLEARTEXT_TRAFFIC 126 | ``` 127 | ``` 128 | UNITYWEBVIEW_ANDROID_ENABLE_CAMERA 129 | ``` 130 | ``` 131 | UNITYWEBVIEW_ANDROID_ENABLE_MICROPHONE 132 | ``` 133 | 134 | - Scene 135 | 136 | ```BrowserManager```をシーン内のいずれかのGameObjectにアタッチしてください.(EventSystemにアタッチするのが一番望ましいかも ...). 137 | 138 | #### ```GeckoView``` をブラウザエンジンとして使用したい場合 139 | 140 | PluginsフォルダーをAssets以下に作成し,以下のファイルを置いてください.また,```BrowserContainer.browser```に```WebView```の代わりに```GeckoView```をアタッチしてください.また,```GeckoView```の使用には API level ```33``` ~ が求められます.```Project Settings```からターゲットAPIレベルを33以上に設定してください. 141 | 142 | 1. gradleTemplate.properties 143 | 144 | ```properties 145 | org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M 146 | org.gradle.parallel=true 147 | # android.enableR8=**MINIFY_WITH_R_EIGHT** 148 | unityStreamingAssets=**STREAMING_ASSETS** 149 | **ADDITIONAL_PROPERTIES** 150 | android.useAndroidX=true 151 | # android.enableJetifier=true 152 | ``` 153 | 154 | 2. mainTemplate.gradle 155 | 156 | ```gradle 157 | ... 158 | 159 | dependencies { 160 | implementation "androidx.annotation:annotation-jvm:1.9.1" 161 | 162 | def collection_version = "1.4.3" 163 | implementation "androidx.collection:collection:$collection_version" 164 | 165 | def lifecycle_version = "2.6.1" 166 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" 167 | implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version" 168 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" 169 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" 170 | implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version" 171 | implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version" 172 | implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" 173 | implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version" 174 | implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version" 175 | implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version" 176 | } 177 | 178 | ... 179 | ``` 180 | 181 | 3. GeckoView plugin (```.aar```) (現在 [125.0.20240425211020 version](https://mvnrepository.com/artifact/org.mozilla.geckoview/geckoview/125.0.20240425211020) のみで開発・テストを行っているので,同じバージョンのものをダウンロードしてください.) 182 | 183 |
184 | 185 | ### Prefab 186 | 以下に置いてあるプレハブをCanvasに追加することでWebViewを実装できます 187 | ``` 188 | /Resources/TLab/WebView/Browser.prefab 189 | ``` 190 | 191 | ### Keyborad 192 | このパッケージでは,デフォルトでuGUIをベースに実装されたバーチャルキーボードが利用できます.しかし,uGUIをベースにしたバーチャルキーボードはデザインなどの拡張性の点で利点がありますが,場合によってはOS標準のシステムキーボードを利用したい場面もあるかもしれません.その場合は,各プラットフォームの設定に従って,アプリ側でパーミッション等を使用してシステムキーボードを有効にしてください (例: Meta Quest での設定は[こちら](https://developers.meta.com/horizon/documentation/unity/unity-keyboard-overlay/)).アプリ側での設定が完了すると,WebViewでのシステムキーボードの利用が可能になります. 193 | 194 | ## 195 | > [!NOTE] 196 | > 外部ストレージ(```/Download```や```/Picture```など)にファイルをダウンロードしたい場合,以下のパーミッションを```AndroidManifest.xml```に追加してください.これは,Android 11以降のデバイスで必要になります (詳細は[こちら](https://developer.android.com/training/data-storage/manage-all-files?hl=ja)). 197 | > ```.xml 198 | > 199 | > ``` 200 | 201 | > [!WARNING] 202 | > このプラグインはAndroidデバイス上でのみ動作します.Unity Editor上で実行してもWebページは表示されないことに注意してください. 203 | 204 | > [!WARNING] 205 | > `HardwareBuffer` モードは,プラグインを実行するデバイスによっては正常に動作しない場合があるかもしれません. その場合は,プロジェクトの`Graphics API`を`Vulkan`から`OpenGLES`に変更する (`HardwareBuffer`モードにおける問題は,ほとんどが,プロジェクトで`Vulkan` APIを使用している場合に報告されています).もしくは,`HardwareBuffer` から `ByteBuffer` へ `CaptureMode` を切り替えてください (安定したレンダリングオプションです). 206 | > 207 | > 208 | 209 | > [!WARNING] 210 | > Android WebViewは [WebXR API](https://developer.mozilla.org/ja/docs/Web/API/WebXR_Device_API/Fundamentals) をサポートしません. 211 | 212 | > [!WARNING] 213 | > OculusQuestはいくつかのHTML5 input タグをサポートしていません(下記参照).それらを使用したい場合,```GeckoView```を```WebView```の代わりに```Browser```として使用してください.uGUIで実装したウィジェットを表示します.以下は,このプラグインによる,HTML5 inpu タグの対応状況です. 214 | > 215 | > - [x] [datetime-local](https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/datetime-local) 216 | > - [x] [date](https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/date) 217 | > - [x] [time](https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/time) 218 | > - [x] [color](https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/color) 219 | > - [ ] [week](https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/week) 220 | > - [ ] [month](https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/month) 221 | > - [ ] [image](https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/image) 222 | > - [ ] [file](https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/file) 223 | 224 | > [!WARNING] 225 | > このプラグインは,```OpenGLES```と```Vulkan```の両方をサポートしていますが,```Vulkan API``` を使用する場合は,デバイスが```OpenGLES 3.1```以上をサポートしている必要があることに留意してください. 226 | -------------------------------------------------------------------------------- /README-ja.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2a0ed03dbd353ec48b2b39d16f29ca96 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # TLabWebView 4 | 5 | [日本語版READMEはこちら](README-ja.md) 6 | 7 | A Unity plugin that enables the use of Android’s web browser component ([```WebView```](https://developer.android.com/reference/android/webkit/WebView) / [```GeckoView```](https://mozilla.github.io/geckoview/)) as a uGUI (Texture2D). This plugin makes it possible to implement a 3D web browser (3D WebView) within Unity. 8 | 9 | - [x] Keyboard Input 10 | - [x] Touch Interaction 11 | - [x] File download (include blob, data url) 12 | - [x] Resize 13 | - [x] Run Javascript 14 | - [x] Support for multiple instances running concurrently 15 | - [x] Support for multiple browser engines 16 | - [x] [```WebView```](https://developer.android.com/reference/android/webkit/WebView): Stable and powerful javascript interface 17 | - [x] [```GeckoView```](https://mozilla.github.io/geckoview/): Highly extensible to pop-ups and other areas that cannot be controlled by WebView 18 | - [x] Support for multiple rendering method 19 | - [x] ```HardwareBudder```: implemented using a low-level feature of Android. Best performance 20 | - [x] ```ByteBuffer```: Easy access to frames, since the results are obtained as byte arrays on the C# side. High stability. **This option is used by default**. 21 | - [x] ```Surface```: Direct rendering to Android's ```Surface``` class, used for active use of features such as [```CompositionLayers```](https://docs.unity3d.com/Packages/com.unity.xr.compositionlayers@0.5/manual/usage-guide.html) 22 | 23 | [Document is here](https://tlabgames.gitbook.io/tlabwebview) 24 | [Snippets is here](https://gist.github.com/TLabAltoh/e0512b3367c25d3e1ec28ddbe95da497#file-tlabwebview-snippets-md) 25 | [The Java plugin source code is here](https://github.com/TLabAltoh/TLabWebViewPlugin) 26 | 27 | [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/tlabaltoh) 28 | 29 | 30 | 31 | ## Unity version this plugin supports 32 | - [x] Unity 2021 33 | - [x] Unity 2022 34 | - [x] Unity 6000 (Only `WebView` tested, Not yet tested in `GeckoView`.) 35 | 36 | 37 | ## Graphics api this plugin supports 38 | - [x] OpenGLES 39 | - [x] Vulkan 40 | 41 | ## Screenshot 42 | Screenshot run on Android 13, Adreno 619 43 | 44 | 45 | 46 | VR sample 47 | 48 | 49 | ## Operating Environment 50 | 51 | | | | 52 | | ----- | ------------------------ | 53 | | OS | Android 10 ~ 14 | 54 | | GPU | Qualcomm Adreno 505, 619 | 55 | | Unity | 2021.3 | 56 | 57 | ## Getting Started 58 | 59 | ### Requirements 60 | - [TLabVKeyborad](https://github.com/TLabAltoh/TLabVKeyborad) ```v1.0.1+``` 61 | 62 | ### Installing 63 | 64 |
Please see here 65 | 66 | #### Submodule 67 | Clone this repository with the following command 68 | ``` 69 | git clone https://github.com/TLabAltoh/TLabWebView.git 70 | ``` 71 | or 72 | ``` 73 | git submodule add https://github.com/TLabAltoh/TLabWebView.git 74 | ``` 75 | 76 | #### UPM 77 | ```add package from git URL ...``` 78 | ``` 79 | https://github.com/TLabAltoh/TLabWebView.git#upm 80 | ``` 81 | 82 | #### For App-Store compatibility 83 | When you publishing your app that using this plugin. Sometimes you get bellow warning. 84 | 85 | > ### Unsafe SSL override in WebViews 86 | > Your application may contain an unsafe implementation of the WebView's [onReceivedSslError() method](https://www.oculus.com/lynx/?u=https%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fwebkit%2FWebViewClient.html%23onReceivedSslError(android.webkit.WebView%2C%2520android.webkit.SslErrorHandler%2C%2520android.net.http.SslError)&e=AT0HN6RWgLynCRtwcCSOzSVvlpMDUhi7C5saZwaY5p4unt4S4-GxIACJX_OPzTQp1Fn4oADk7Q_rwvZvRiF5XstftUzyuAWAolfkkk_WAtDpvOgW0Llcn_BXIEpgYobFNELMZ31ntKzTQXflaLkeRA) with a call to `handler.proceed() with insufficient validations. This may cause the WebView to ignore SSL certificate validation errors, making the application vulnerable to man-in-the-middle attacks. 87 | > 88 | > https://www.meta.com/experiences/ 89 | 90 | 91 | > ### Security and trust 92 | > #### onReceivedSslError 93 | > your app is using an unsafe implementation of [```WebviewClient.onReceivedSslError```](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%20android.webkit.SslErrorHandler,%20android.net.http.SslError)) handler 94 | > 95 | > https://developer.android.com/distribute/console 96 | 97 | Than, please switch repository's branch to app store compatible version. 98 | 99 | ```add package from git URL ...``` 100 | ``` 101 | https://github.com/TLabAltoh/TLabWebView.git#appstore-compatible-upm 102 | ``` 103 | 104 | Please note that this version will not be able to load insecure websites (URL starting with ```http://```). 105 | 106 |
107 | 108 | ### Set Up 109 | 110 |
Please see here 111 | 112 | - Build Settings 113 | 114 | | Property | Value | 115 | | -------- | ------- | 116 | | Platform | Android | 117 | 118 | - Project Settings 119 | 120 | | Property | Value | 121 | | ----------------- | ------------------------------------- | 122 | | Color Space | Linear | 123 | | Minimum API Level | 26 | 124 | | Target API Level | 30 (Unity 2021), 31 ~ 32 (Unity 2022) | 125 | 126 | - Add the following symbols to Project Settings --> Player --> Other Settings (to be used at build time) 127 | 128 | ``` 129 | UNITYWEBVIEW_ANDROID_USES_CLEARTEXT_TRAFFIC 130 | ``` 131 | ``` 132 | UNITYWEBVIEW_ANDROID_ENABLE_CAMERA 133 | ``` 134 | ``` 135 | UNITYWEBVIEW_ANDROID_ENABLE_MICROPHONE 136 | ``` 137 | 138 | - Scene 139 | 140 | Please add the ```BrowserManager``` to any GameObject (maybe EventSystem is best). 141 | 142 | #### If you want to use ```GeckoView``` as a browser engine. 143 | 144 | Please create a Plugins folder in your Assets folder and create files in it. And please set the ```BrowserContainer.browser``` to ```GeckoView``` instead of ```WebView```. Also ```GeckoView``` needs Android ```13``` ~ (API level ```33``` ~). Please set tartget level to API level ```33``` in the ```Project Settings```. 145 | 146 | 1. gradleTemplate.properties 147 | 148 | ```properties 149 | org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M 150 | org.gradle.parallel=true 151 | # android.enableR8=**MINIFY_WITH_R_EIGHT** 152 | unityStreamingAssets=**STREAMING_ASSETS** 153 | **ADDITIONAL_PROPERTIES** 154 | android.useAndroidX=true 155 | # android.enableJetifier=true 156 | ``` 157 | 158 | 2. mainTemplate.gradle 159 | 160 | ```gradle 161 | ... 162 | 163 | dependencies { 164 | implementation "androidx.annotation:annotation-jvm:1.9.1" 165 | 166 | def collection_version = "1.4.3" 167 | implementation "androidx.collection:collection:$collection_version" 168 | 169 | def lifecycle_version = "2.6.1" 170 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" 171 | implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version" 172 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" 173 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" 174 | implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version" 175 | implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version" 176 | implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" 177 | implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version" 178 | implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version" 179 | implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version" 180 | } 181 | 182 | ... 183 | ``` 184 | 185 | 3. GeckoView plugin (```.aar```) (please install the [125.0.20240425211020 version](https://mvnrepository.com/artifact/org.mozilla.geckoview/geckoview/125.0.20240425211020), as this package is only developed and tested with it) 186 | 187 |
188 | 189 | ### Prefab 190 | Prefab is here. Just add prefab to the canvas to implement webview 191 | ``` 192 | /Resources/TLab/WebView/Browser.prefab 193 | ``` 194 | 195 | ### Keyborad 196 | By default, a virtual keyboard based on uGUI is available in this package. However, while the uGUI-based virtual keyboard has advantages in terms of extensibility of design, etc., there may be situations where you prefer to use the OS standard system keyboard. In such a case, please enable the system keyboard using permissions, etc. on the app side according to the settings of each platform (e.g., see [here](https://developers.meta.com/horizon/documentation/unity/unity-keyboard-overlay/) for settings on Meta Quest). Once the app has been configured, the system keyboard can be used in WebView. 197 | 198 | ## 199 | > [!NOTE] 200 | > If you want to download the file to external storage (like ```/Download```, ```/Picture```), please add the following permission to the manifest. This is required for Android 11+ (see [here](https://developer.android.com/training/data-storage/manage-all-files?hl=en) for more details). 201 | > ```.xml 202 | > 203 | > ``` 204 | 205 | > [!WARNING] 206 | > Note that this plugin only works on Android devices; it will not display web pages when run on the Unity Editor. 207 | 208 | > [!WARNING] 209 | > `HardwareBuffer` mode may not work properly for some devices. In such cases, Please change the project's `Graphics API` from `Vulkan` to `OpenGLES` (Problems in `HardwareBuffer` mode are most often reported when using the `Vulkan` API in a project). Or switch `CaptureMode` from `HardwareBuffer` to `ByteBuffer` (This is a stable rendering option). 210 | > 211 | > 212 | 213 | > [!WARNING] 214 | > Android WebView doesn't support the [WebXR API](https://developer.mozilla.org/en-US/docs/Web/API/WebXR_Device_API/Fundamentals). 215 | 216 | > [!WARNING] 217 | > OculusQuest doesn't support some HTML5 input tags (see below). If you want to use them, please use ```GeckoView``` as ```Browser``` instead of ```WebView```. It will display a widget implemented by uGUI. Below is the status of HTML5 input tag support by this plugin's custom widget. 218 | > 219 | > - [x] [datetime-local](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local) 220 | > - [x] [date](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date) 221 | > - [x] [time](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/time) 222 | > - [x] [color](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/color) 223 | > - [ ] [week](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/week) 224 | > - [ ] [month](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/month) 225 | > - [ ] [image](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/image) 226 | > - [ ] [file](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file) 227 | 228 | > [!WARNING] 229 | > This plugin supports both ```Vulkan``` and ```OpenGLES```, but if you are building an application that uses a ```Vulkan``` graphics API, the Android device must support ```OpenGLES (3.1+)``` as well as ```Vulkan```. 230 | 231 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7f5779309827a6746bfb3c2cefbeb86c 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Resources.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5a7121755fc31864e860fc7593c48c8f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/TLab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 82dacb2949de873498e00534e5bb060c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/TLab/WebView.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8a7d4c8060ac1b746959cd465e0c79ca 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Browser.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cf5767a7a6501d04fb202c6ed3ef427c 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 80c6abf5cb71ad74386217c65038910b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Browser Sample.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4272bd6725bd64846b07be9b4d76ad90 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7932db4ef0752bb448b93699e3592c00 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Scripts/JS.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bb3df9691c09f0845a8fdcfb2ea54bcb 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Scripts/JS/disable-beforunload.txt: -------------------------------------------------------------------------------- 1 | const originalAddEventListener = window.addEventListener; 2 | window.addEventListener = function (type, listener, options) { 3 | if (type === 'beforeunload') { 4 | console.warn("Blocked a 'beforeunload' listener from being added."); 5 | return; 6 | } 7 | originalAddEventListener.call(this, type, listener, options); 8 | }; -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Scripts/JS/disable-beforunload.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 77170db0d6c43084ab19b7b12a55e1b2 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Scripts/JS/fetch-blob.txt: -------------------------------------------------------------------------------- 1 | function writeBuffer(buffer, bufferId, segmentSize, offset) { 2 | if (segmentSize === 0) return; 3 | var i = offset; 4 | while (i + segmentSize <= buffer.length) { 5 | window.tlab.write(bufferId, buffer.slice(i, i + segmentSize)); 6 | i += segmentSize 7 | } 8 | writeBuffer(buffer, bufferId, parseInt(segmentSize / 2), i); 9 | } 10 | var xhr = new XMLHttpRequest(); 11 | xhr.open("GET", url, true); 12 | xhr.setRequestHeader("Content-type", mimetype + ";charset=UTF-8"); 13 | xhr.responseType = "blob"; 14 | xhr.onload = function (e) { 15 | if (this.status == 200) { 16 | var blobFile = this.response; 17 | var reader = new FileReader(); 18 | reader.readAsDataURL(blobFile); 19 | reader.onloadend = function () { 20 | base64data = reader.result; 21 | bufferId = url; 22 | buffer = new TextEncoder().encode(base64data); 23 | window.tlab.malloc(bufferId, buffer.length); 24 | writeBuffer(buffer, bufferId, 500000, 0); 25 | window.tlab.unitySendMessage(go, method, argments); 26 | } 27 | } 28 | }; 29 | xhr.send(); -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Scripts/JS/fetch-blob.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b4964ae62ac4eb34ca2ad1e4b2801896 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Scripts/JS/focus-in-out-interaction.txt: -------------------------------------------------------------------------------- 1 | function searchShadowRoot(node, roots) { 2 | if (node == null) { 3 | return; 4 | } 5 | if (node.shadowRoot != undefined && node.shadowRoot != null) { 6 | roots.push(node.shadowRoot); 7 | searchShadowRoot(node.shadowRoot, roots); 8 | } 9 | for (var i = 0; i < node.childNodes.length; i++) { 10 | searchShadowRoot(node.childNodes[i], roots); 11 | } 12 | } 13 | 14 | function getAllRoot() { 15 | var roots = [document]; 16 | searchShadowRoot(document, roots); 17 | return roots; 18 | } 19 | 20 | var roots = getAllRoot(); 21 | 22 | function focusin (e) { 23 | const target = e.target; 24 | if (target.tagName == 'INPUT' || target.tagName == 'TEXTAREA') { 25 | window.tlab.unitySendMessage(go, method, 'Focusin'); 26 | } 27 | } 28 | 29 | function focusout (e) { 30 | const target = e.target; 31 | if (target.tagName == 'INPUT' || target.tagName == 'TEXTAREA') { 32 | window.tlab.unitySendMessage(go, method, 'Focusout'); 33 | } 34 | } 35 | 36 | for (var i = 0; i < roots.length; i++) { 37 | roots[i].removeEventListener('focusin', focusin); 38 | roots[i].removeEventListener('focusout', focusout); 39 | 40 | roots[i].addEventListener('focusin', focusin); 41 | roots[i].addEventListener('focusout', focusout); 42 | } -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Scripts/JS/focus-in-out-interaction.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8c945e4fa883ec548a91eba33e462a5c 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Widget.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f37fc890ddb3fdb4b980c8d9512f5792 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Widget/Dialog.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 413d4daf4d477ed47b66a273dc2937b0 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Widget/Number Picker.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6940f93adaa13ac4192b8da573396f85 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Widget/Select.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 61db13b0f91a53f49993cc1e89bf636e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Widget/Select/Group.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &1264867841882999603 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 2526483044092763727} 12 | - component: {fileID: 5319220898759178625} 13 | - component: {fileID: 2800390673303244714} 14 | m_Layer: 0 15 | m_Name: Text (TMP) 16 | m_TagString: Untagged 17 | m_Icon: {fileID: 0} 18 | m_NavMeshLayer: 0 19 | m_StaticEditorFlags: 0 20 | m_IsActive: 1 21 | --- !u!224 &2526483044092763727 22 | RectTransform: 23 | m_ObjectHideFlags: 0 24 | m_CorrespondingSourceObject: {fileID: 0} 25 | m_PrefabInstance: {fileID: 0} 26 | m_PrefabAsset: {fileID: 0} 27 | m_GameObject: {fileID: 1264867841882999603} 28 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 29 | m_LocalPosition: {x: 0, y: 0, z: 0} 30 | m_LocalScale: {x: 1, y: 1, z: 1} 31 | m_ConstrainProportionsScale: 0 32 | m_Children: [] 33 | m_Father: {fileID: 6541164512831261800} 34 | m_RootOrder: 0 35 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 36 | m_AnchorMin: {x: 0, y: 0} 37 | m_AnchorMax: {x: 1, y: 1} 38 | m_AnchoredPosition: {x: 0, y: 0} 39 | m_SizeDelta: {x: -6, y: -6} 40 | m_Pivot: {x: 0.5, y: 0.5} 41 | --- !u!222 &5319220898759178625 42 | CanvasRenderer: 43 | m_ObjectHideFlags: 0 44 | m_CorrespondingSourceObject: {fileID: 0} 45 | m_PrefabInstance: {fileID: 0} 46 | m_PrefabAsset: {fileID: 0} 47 | m_GameObject: {fileID: 1264867841882999603} 48 | m_CullTransparentMesh: 1 49 | --- !u!114 &2800390673303244714 50 | MonoBehaviour: 51 | m_ObjectHideFlags: 0 52 | m_CorrespondingSourceObject: {fileID: 0} 53 | m_PrefabInstance: {fileID: 0} 54 | m_PrefabAsset: {fileID: 0} 55 | m_GameObject: {fileID: 1264867841882999603} 56 | m_Enabled: 1 57 | m_EditorHideFlags: 0 58 | m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} 59 | m_Name: 60 | m_EditorClassIdentifier: 61 | m_Material: {fileID: 0} 62 | m_Color: {r: 1, g: 1, b: 1, a: 1} 63 | m_RaycastTarget: 1 64 | m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} 65 | m_Maskable: 1 66 | m_OnCullStateChanged: 67 | m_PersistentCalls: 68 | m_Calls: [] 69 | m_text: Text ... 70 | m_isRightToLeft: 0 71 | m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} 72 | m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} 73 | m_fontSharedMaterials: [] 74 | m_fontMaterial: {fileID: 0} 75 | m_fontMaterials: [] 76 | m_fontColor32: 77 | serializedVersion: 2 78 | rgba: 4278190080 79 | m_fontColor: {r: 0, g: 0, b: 0, a: 1} 80 | m_enableVertexGradient: 0 81 | m_colorMode: 3 82 | m_fontColorGradient: 83 | topLeft: {r: 1, g: 1, b: 1, a: 1} 84 | topRight: {r: 1, g: 1, b: 1, a: 1} 85 | bottomLeft: {r: 1, g: 1, b: 1, a: 1} 86 | bottomRight: {r: 1, g: 1, b: 1, a: 1} 87 | m_fontColorGradientPreset: {fileID: 0} 88 | m_spriteAsset: {fileID: 0} 89 | m_tintAllSprites: 0 90 | m_StyleSheet: {fileID: 0} 91 | m_TextStyleHashCode: -1183493901 92 | m_overrideHtmlColors: 0 93 | m_faceColor: 94 | serializedVersion: 2 95 | rgba: 4294967295 96 | m_fontSize: 30 97 | m_fontSizeBase: 30 98 | m_fontWeight: 400 99 | m_enableAutoSizing: 0 100 | m_fontSizeMin: 18 101 | m_fontSizeMax: 72 102 | m_fontStyle: 0 103 | m_HorizontalAlignment: 1 104 | m_VerticalAlignment: 512 105 | m_textAlignment: 65535 106 | m_characterSpacing: 0 107 | m_wordSpacing: 0 108 | m_lineSpacing: 0 109 | m_lineSpacingMax: 0 110 | m_paragraphSpacing: 0 111 | m_charWidthMaxAdj: 0 112 | m_enableWordWrapping: 1 113 | m_wordWrappingRatios: 0.4 114 | m_overflowMode: 1 115 | m_linkedTextComponent: {fileID: 0} 116 | parentLinkedComponent: {fileID: 0} 117 | m_enableKerning: 1 118 | m_enableExtraPadding: 0 119 | checkPaddingRequired: 0 120 | m_isRichText: 1 121 | m_parseCtrlCharacters: 1 122 | m_isOrthographic: 1 123 | m_isCullingEnabled: 0 124 | m_horizontalMapping: 0 125 | m_verticalMapping: 0 126 | m_uvLineOffset: 0 127 | m_geometrySortingOrder: 0 128 | m_IsTextObjectScaleStatic: 0 129 | m_VertexBufferAutoSizeReduction: 0 130 | m_useMaxVisibleDescender: 1 131 | m_pageToDisplay: 1 132 | m_margin: {x: 0, y: 0, z: 0, w: 0} 133 | m_isUsingLegacyAnimationComponent: 0 134 | m_isVolumetricText: 0 135 | m_hasFontAssetChanged: 0 136 | m_baseMaterial: {fileID: 0} 137 | m_maskOffset: {x: 0, y: 0, z: 0, w: 0} 138 | --- !u!1 &1651959583725008689 139 | GameObject: 140 | m_ObjectHideFlags: 0 141 | m_CorrespondingSourceObject: {fileID: 0} 142 | m_PrefabInstance: {fileID: 0} 143 | m_PrefabAsset: {fileID: 0} 144 | serializedVersion: 6 145 | m_Component: 146 | - component: {fileID: 6541164512831261800} 147 | - component: {fileID: 6235827200066371057} 148 | - component: {fileID: 4229614656884787150} 149 | m_Layer: 0 150 | m_Name: Group 151 | m_TagString: Untagged 152 | m_Icon: {fileID: 0} 153 | m_NavMeshLayer: 0 154 | m_StaticEditorFlags: 0 155 | m_IsActive: 1 156 | --- !u!224 &6541164512831261800 157 | RectTransform: 158 | m_ObjectHideFlags: 0 159 | m_CorrespondingSourceObject: {fileID: 0} 160 | m_PrefabInstance: {fileID: 0} 161 | m_PrefabAsset: {fileID: 0} 162 | m_GameObject: {fileID: 1651959583725008689} 163 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 164 | m_LocalPosition: {x: 0, y: 0, z: 0} 165 | m_LocalScale: {x: 1, y: 1, z: 1} 166 | m_ConstrainProportionsScale: 0 167 | m_Children: 168 | - {fileID: 2526483044092763727} 169 | m_Father: {fileID: 0} 170 | m_RootOrder: 0 171 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 172 | m_AnchorMin: {x: 0, y: 0} 173 | m_AnchorMax: {x: 0, y: 0} 174 | m_AnchoredPosition: {x: 0, y: 0} 175 | m_SizeDelta: {x: 500, y: 55} 176 | m_Pivot: {x: 0.5, y: 0.5} 177 | --- !u!222 &6235827200066371057 178 | CanvasRenderer: 179 | m_ObjectHideFlags: 0 180 | m_CorrespondingSourceObject: {fileID: 0} 181 | m_PrefabInstance: {fileID: 0} 182 | m_PrefabAsset: {fileID: 0} 183 | m_GameObject: {fileID: 1651959583725008689} 184 | m_CullTransparentMesh: 1 185 | --- !u!114 &4229614656884787150 186 | MonoBehaviour: 187 | m_ObjectHideFlags: 0 188 | m_CorrespondingSourceObject: {fileID: 0} 189 | m_PrefabInstance: {fileID: 0} 190 | m_PrefabAsset: {fileID: 0} 191 | m_GameObject: {fileID: 1651959583725008689} 192 | m_Enabled: 1 193 | m_EditorHideFlags: 0 194 | m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} 195 | m_Name: 196 | m_EditorClassIdentifier: 197 | m_Material: {fileID: 0} 198 | m_Color: {r: 1, g: 1, b: 1, a: 0.392} 199 | m_RaycastTarget: 1 200 | m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} 201 | m_Maskable: 1 202 | m_OnCullStateChanged: 203 | m_PersistentCalls: 204 | m_Calls: [] 205 | m_Sprite: {fileID: 0} 206 | m_Type: 1 207 | m_PreserveAspect: 0 208 | m_FillCenter: 1 209 | m_FillMethod: 4 210 | m_FillAmount: 1 211 | m_FillClockwise: 1 212 | m_FillOrigin: 0 213 | m_UseSpriteMesh: 0 214 | m_PixelsPerUnitMultiplier: 1 215 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Widget/Select/Group.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 373e78f16f78cfc4fb6c44f30db601b0 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Widget/Select/Selectable.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &5213916560862622005 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 8852358417840088137} 12 | - component: {fileID: 1155051106320089991} 13 | - component: {fileID: 9154348682113512876} 14 | m_Layer: 0 15 | m_Name: Text (TMP) 16 | m_TagString: Untagged 17 | m_Icon: {fileID: 0} 18 | m_NavMeshLayer: 0 19 | m_StaticEditorFlags: 0 20 | m_IsActive: 1 21 | --- !u!224 &8852358417840088137 22 | RectTransform: 23 | m_ObjectHideFlags: 0 24 | m_CorrespondingSourceObject: {fileID: 0} 25 | m_PrefabInstance: {fileID: 0} 26 | m_PrefabAsset: {fileID: 0} 27 | m_GameObject: {fileID: 5213916560862622005} 28 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 29 | m_LocalPosition: {x: 0, y: 0, z: 0} 30 | m_LocalScale: {x: 1, y: 1, z: 1} 31 | m_ConstrainProportionsScale: 0 32 | m_Children: [] 33 | m_Father: {fileID: 220915606408181358} 34 | m_RootOrder: 0 35 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 36 | m_AnchorMin: {x: 0, y: 0} 37 | m_AnchorMax: {x: 1, y: 1} 38 | m_AnchoredPosition: {x: 0, y: 0} 39 | m_SizeDelta: {x: -6, y: -6} 40 | m_Pivot: {x: 0.5, y: 0.5} 41 | --- !u!222 &1155051106320089991 42 | CanvasRenderer: 43 | m_ObjectHideFlags: 0 44 | m_CorrespondingSourceObject: {fileID: 0} 45 | m_PrefabInstance: {fileID: 0} 46 | m_PrefabAsset: {fileID: 0} 47 | m_GameObject: {fileID: 5213916560862622005} 48 | m_CullTransparentMesh: 1 49 | --- !u!114 &9154348682113512876 50 | MonoBehaviour: 51 | m_ObjectHideFlags: 0 52 | m_CorrespondingSourceObject: {fileID: 0} 53 | m_PrefabInstance: {fileID: 0} 54 | m_PrefabAsset: {fileID: 0} 55 | m_GameObject: {fileID: 5213916560862622005} 56 | m_Enabled: 1 57 | m_EditorHideFlags: 0 58 | m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} 59 | m_Name: 60 | m_EditorClassIdentifier: 61 | m_Material: {fileID: 0} 62 | m_Color: {r: 1, g: 1, b: 1, a: 1} 63 | m_RaycastTarget: 1 64 | m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} 65 | m_Maskable: 1 66 | m_OnCullStateChanged: 67 | m_PersistentCalls: 68 | m_Calls: [] 69 | m_text: Text ... 70 | m_isRightToLeft: 0 71 | m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} 72 | m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} 73 | m_fontSharedMaterials: [] 74 | m_fontMaterial: {fileID: 0} 75 | m_fontMaterials: [] 76 | m_fontColor32: 77 | serializedVersion: 2 78 | rgba: 4278190080 79 | m_fontColor: {r: 0, g: 0, b: 0, a: 1} 80 | m_enableVertexGradient: 0 81 | m_colorMode: 3 82 | m_fontColorGradient: 83 | topLeft: {r: 1, g: 1, b: 1, a: 1} 84 | topRight: {r: 1, g: 1, b: 1, a: 1} 85 | bottomLeft: {r: 1, g: 1, b: 1, a: 1} 86 | bottomRight: {r: 1, g: 1, b: 1, a: 1} 87 | m_fontColorGradientPreset: {fileID: 0} 88 | m_spriteAsset: {fileID: 0} 89 | m_tintAllSprites: 0 90 | m_StyleSheet: {fileID: 0} 91 | m_TextStyleHashCode: -1183493901 92 | m_overrideHtmlColors: 0 93 | m_faceColor: 94 | serializedVersion: 2 95 | rgba: 4294967295 96 | m_fontSize: 30 97 | m_fontSizeBase: 30 98 | m_fontWeight: 400 99 | m_enableAutoSizing: 0 100 | m_fontSizeMin: 18 101 | m_fontSizeMax: 72 102 | m_fontStyle: 0 103 | m_HorizontalAlignment: 1 104 | m_VerticalAlignment: 512 105 | m_textAlignment: 65535 106 | m_characterSpacing: 0 107 | m_wordSpacing: 0 108 | m_lineSpacing: 0 109 | m_lineSpacingMax: 0 110 | m_paragraphSpacing: 0 111 | m_charWidthMaxAdj: 0 112 | m_enableWordWrapping: 1 113 | m_wordWrappingRatios: 0.4 114 | m_overflowMode: 1 115 | m_linkedTextComponent: {fileID: 0} 116 | parentLinkedComponent: {fileID: 0} 117 | m_enableKerning: 1 118 | m_enableExtraPadding: 0 119 | checkPaddingRequired: 0 120 | m_isRichText: 1 121 | m_parseCtrlCharacters: 1 122 | m_isOrthographic: 1 123 | m_isCullingEnabled: 0 124 | m_horizontalMapping: 0 125 | m_verticalMapping: 0 126 | m_uvLineOffset: 0 127 | m_geometrySortingOrder: 0 128 | m_IsTextObjectScaleStatic: 0 129 | m_VertexBufferAutoSizeReduction: 0 130 | m_useMaxVisibleDescender: 1 131 | m_pageToDisplay: 1 132 | m_margin: {x: 0, y: 0, z: 0, w: 0} 133 | m_isUsingLegacyAnimationComponent: 0 134 | m_isVolumetricText: 0 135 | m_hasFontAssetChanged: 0 136 | m_baseMaterial: {fileID: 0} 137 | m_maskOffset: {x: 0, y: 0, z: 0, w: 0} 138 | --- !u!1 &5709099332327001399 139 | GameObject: 140 | m_ObjectHideFlags: 0 141 | m_CorrespondingSourceObject: {fileID: 0} 142 | m_PrefabInstance: {fileID: 0} 143 | m_PrefabAsset: {fileID: 0} 144 | serializedVersion: 6 145 | m_Component: 146 | - component: {fileID: 220915606408181358} 147 | - component: {fileID: 1106794971665713143} 148 | - component: {fileID: 7162034007823695304} 149 | - component: {fileID: 4919607304810742064} 150 | m_Layer: 0 151 | m_Name: Selectable 152 | m_TagString: Untagged 153 | m_Icon: {fileID: 0} 154 | m_NavMeshLayer: 0 155 | m_StaticEditorFlags: 0 156 | m_IsActive: 1 157 | --- !u!224 &220915606408181358 158 | RectTransform: 159 | m_ObjectHideFlags: 0 160 | m_CorrespondingSourceObject: {fileID: 0} 161 | m_PrefabInstance: {fileID: 0} 162 | m_PrefabAsset: {fileID: 0} 163 | m_GameObject: {fileID: 5709099332327001399} 164 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 165 | m_LocalPosition: {x: 0, y: 0, z: 0} 166 | m_LocalScale: {x: 1, y: 1, z: 1} 167 | m_ConstrainProportionsScale: 0 168 | m_Children: 169 | - {fileID: 8852358417840088137} 170 | m_Father: {fileID: 0} 171 | m_RootOrder: 0 172 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 173 | m_AnchorMin: {x: 0, y: 0} 174 | m_AnchorMax: {x: 0, y: 0} 175 | m_AnchoredPosition: {x: 0, y: 0} 176 | m_SizeDelta: {x: 500, y: 55} 177 | m_Pivot: {x: 0.5, y: 0.5} 178 | --- !u!222 &1106794971665713143 179 | CanvasRenderer: 180 | m_ObjectHideFlags: 0 181 | m_CorrespondingSourceObject: {fileID: 0} 182 | m_PrefabInstance: {fileID: 0} 183 | m_PrefabAsset: {fileID: 0} 184 | m_GameObject: {fileID: 5709099332327001399} 185 | m_CullTransparentMesh: 1 186 | --- !u!114 &7162034007823695304 187 | MonoBehaviour: 188 | m_ObjectHideFlags: 0 189 | m_CorrespondingSourceObject: {fileID: 0} 190 | m_PrefabInstance: {fileID: 0} 191 | m_PrefabAsset: {fileID: 0} 192 | m_GameObject: {fileID: 5709099332327001399} 193 | m_Enabled: 1 194 | m_EditorHideFlags: 0 195 | m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} 196 | m_Name: 197 | m_EditorClassIdentifier: 198 | m_Material: {fileID: 0} 199 | m_Color: {r: 1, g: 1, b: 1, a: 0.392} 200 | m_RaycastTarget: 1 201 | m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} 202 | m_Maskable: 1 203 | m_OnCullStateChanged: 204 | m_PersistentCalls: 205 | m_Calls: [] 206 | m_Sprite: {fileID: 0} 207 | m_Type: 1 208 | m_PreserveAspect: 0 209 | m_FillCenter: 1 210 | m_FillMethod: 4 211 | m_FillAmount: 1 212 | m_FillClockwise: 1 213 | m_FillOrigin: 0 214 | m_UseSpriteMesh: 0 215 | m_PixelsPerUnitMultiplier: 1 216 | --- !u!114 &4919607304810742064 217 | MonoBehaviour: 218 | m_ObjectHideFlags: 0 219 | m_CorrespondingSourceObject: {fileID: 0} 220 | m_PrefabInstance: {fileID: 0} 221 | m_PrefabAsset: {fileID: 0} 222 | m_GameObject: {fileID: 5709099332327001399} 223 | m_Enabled: 1 224 | m_EditorHideFlags: 0 225 | m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} 226 | m_Name: 227 | m_EditorClassIdentifier: 228 | m_Navigation: 229 | m_Mode: 3 230 | m_WrapAround: 0 231 | m_SelectOnUp: {fileID: 0} 232 | m_SelectOnDown: {fileID: 0} 233 | m_SelectOnLeft: {fileID: 0} 234 | m_SelectOnRight: {fileID: 0} 235 | m_Transition: 1 236 | m_Colors: 237 | m_NormalColor: {r: 1, g: 1, b: 1, a: 1} 238 | m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 239 | m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} 240 | m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 241 | m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} 242 | m_ColorMultiplier: 1 243 | m_FadeDuration: 0.1 244 | m_SpriteState: 245 | m_HighlightedSprite: {fileID: 0} 246 | m_PressedSprite: {fileID: 0} 247 | m_SelectedSprite: {fileID: 0} 248 | m_DisabledSprite: {fileID: 0} 249 | m_AnimationTriggers: 250 | m_NormalTrigger: Normal 251 | m_HighlightedTrigger: Highlighted 252 | m_PressedTrigger: Pressed 253 | m_SelectedTrigger: Selected 254 | m_DisabledTrigger: Disabled 255 | m_Interactable: 1 256 | m_TargetGraphic: {fileID: 7162034007823695304} 257 | m_OnClick: 258 | m_PersistentCalls: 259 | m_Calls: [] 260 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Widget/Select/Selectable.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 49e4bf32b87812e4aa4a235ce4caa5f8 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Widget/Select/Separator.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &314910016294810781 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 2082981106636589263} 12 | - component: {fileID: 7498795620532289602} 13 | - component: {fileID: 8418542230732811784} 14 | - component: {fileID: 649669995515007034} 15 | m_Layer: 0 16 | m_Name: Separator 17 | m_TagString: Untagged 18 | m_Icon: {fileID: 0} 19 | m_NavMeshLayer: 0 20 | m_StaticEditorFlags: 0 21 | m_IsActive: 1 22 | --- !u!224 &2082981106636589263 23 | RectTransform: 24 | m_ObjectHideFlags: 0 25 | m_CorrespondingSourceObject: {fileID: 0} 26 | m_PrefabInstance: {fileID: 0} 27 | m_PrefabAsset: {fileID: 0} 28 | m_GameObject: {fileID: 314910016294810781} 29 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 30 | m_LocalPosition: {x: 0, y: 0, z: 0} 31 | m_LocalScale: {x: 1, y: 1, z: 1} 32 | m_ConstrainProportionsScale: 0 33 | m_Children: [] 34 | m_Father: {fileID: 0} 35 | m_RootOrder: 0 36 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 37 | m_AnchorMin: {x: 0, y: 0} 38 | m_AnchorMax: {x: 0, y: 0} 39 | m_AnchoredPosition: {x: 0, y: 0} 40 | m_SizeDelta: {x: 500, y: 9.484} 41 | m_Pivot: {x: 0.5, y: 0.5} 42 | --- !u!222 &7498795620532289602 43 | CanvasRenderer: 44 | m_ObjectHideFlags: 0 45 | m_CorrespondingSourceObject: {fileID: 0} 46 | m_PrefabInstance: {fileID: 0} 47 | m_PrefabAsset: {fileID: 0} 48 | m_GameObject: {fileID: 314910016294810781} 49 | m_CullTransparentMesh: 1 50 | --- !u!114 &8418542230732811784 51 | MonoBehaviour: 52 | m_ObjectHideFlags: 0 53 | m_CorrespondingSourceObject: {fileID: 0} 54 | m_PrefabInstance: {fileID: 0} 55 | m_PrefabAsset: {fileID: 0} 56 | m_GameObject: {fileID: 314910016294810781} 57 | m_Enabled: 1 58 | m_EditorHideFlags: 0 59 | m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} 60 | m_Name: 61 | m_EditorClassIdentifier: 62 | m_Material: {fileID: 0} 63 | m_Color: {r: 0, g: 0, b: 0, a: 0.392} 64 | m_RaycastTarget: 1 65 | m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} 66 | m_Maskable: 1 67 | m_OnCullStateChanged: 68 | m_PersistentCalls: 69 | m_Calls: [] 70 | m_Sprite: {fileID: 0} 71 | m_Type: 1 72 | m_PreserveAspect: 0 73 | m_FillCenter: 1 74 | m_FillMethod: 4 75 | m_FillAmount: 1 76 | m_FillClockwise: 1 77 | m_FillOrigin: 0 78 | m_UseSpriteMesh: 0 79 | m_PixelsPerUnitMultiplier: 1 80 | --- !u!114 &649669995515007034 81 | MonoBehaviour: 82 | m_ObjectHideFlags: 0 83 | m_CorrespondingSourceObject: {fileID: 0} 84 | m_PrefabInstance: {fileID: 0} 85 | m_PrefabAsset: {fileID: 0} 86 | m_GameObject: {fileID: 314910016294810781} 87 | m_Enabled: 1 88 | m_EditorHideFlags: 0 89 | m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} 90 | m_Name: 91 | m_EditorClassIdentifier: 92 | m_Navigation: 93 | m_Mode: 3 94 | m_WrapAround: 0 95 | m_SelectOnUp: {fileID: 0} 96 | m_SelectOnDown: {fileID: 0} 97 | m_SelectOnLeft: {fileID: 0} 98 | m_SelectOnRight: {fileID: 0} 99 | m_Transition: 1 100 | m_Colors: 101 | m_NormalColor: {r: 1, g: 1, b: 1, a: 1} 102 | m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 103 | m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} 104 | m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 105 | m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} 106 | m_ColorMultiplier: 1 107 | m_FadeDuration: 0.1 108 | m_SpriteState: 109 | m_HighlightedSprite: {fileID: 0} 110 | m_PressedSprite: {fileID: 0} 111 | m_SelectedSprite: {fileID: 0} 112 | m_DisabledSprite: {fileID: 0} 113 | m_AnimationTriggers: 114 | m_NormalTrigger: Normal 115 | m_HighlightedTrigger: Highlighted 116 | m_PressedTrigger: Pressed 117 | m_SelectedTrigger: Selected 118 | m_DisabledTrigger: Disabled 119 | m_Interactable: 1 120 | m_TargetGraphic: {fileID: 8418542230732811784} 121 | m_OnClick: 122 | m_PersistentCalls: 123 | m_Calls: [] 124 | -------------------------------------------------------------------------------- /Resources/TLab/WebView/Samples/Widget/Select/Separator.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 046a49799505c2545a7e739e63a2f896 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0d1f0038cc22a3a46837eb43a8f33999 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/BaseInputListener.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.EventSystems; 3 | 4 | namespace TLab.WebView 5 | { 6 | public abstract class BaseInputListener : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler, IPointerExitHandler 7 | { 8 | private bool m_pointerDown = false; 9 | private int? m_pointerId = null; 10 | private RenderMode m_renderMode; 11 | private Vector2 m_current; 12 | private Vector2 m_prev; 13 | 14 | private InputEventData m_inputEventData = new InputEventData(); 15 | 16 | private string THIS_NAME => "[" + this.GetType() + "] "; 17 | 18 | public class InputEventData 19 | { 20 | public Vector2 position; 21 | public Vector2 delta; 22 | 23 | public InputEventData(Vector2 position, Vector2 delta) => Update(position, delta); 24 | 25 | public InputEventData() { } 26 | 27 | public void Update(Vector2 position, Vector2 delta) 28 | { 29 | this.position = position; 30 | this.delta = delta; 31 | } 32 | } 33 | 34 | protected bool GetInputPosition(PointerEventData eventData) 35 | { 36 | m_prev = m_current; 37 | 38 | var localPosition = Vector2.zero; 39 | 40 | var rectTransform = (RectTransform)transform; 41 | 42 | switch (m_renderMode) 43 | { 44 | case RenderMode.ScreenSpaceOverlay: 45 | localPosition = transform.InverseTransformPoint(eventData.position); 46 | break; 47 | case RenderMode.ScreenSpaceCamera: 48 | case RenderMode.WorldSpace: 49 | RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out localPosition); 50 | break; 51 | } 52 | 53 | var x = localPosition.x / rectTransform.rect.width + rectTransform.pivot.x; 54 | var y = 1f - (localPosition.y / rectTransform.rect.height + rectTransform.pivot.y); 55 | 56 | if (Range(x, 0, 1) && Range(y, 0, 1)) 57 | { 58 | m_current = new Vector2(x, y); 59 | 60 | return true; 61 | } 62 | 63 | m_current = Vector2.zero; 64 | 65 | return false; 66 | } 67 | 68 | protected abstract void OnPointerDown(PointerEventData pointerEventData, InputEventData inputEventData); 69 | 70 | public void OnPointerDown(PointerEventData eventData) 71 | { 72 | if (m_pointerId == null && !m_pointerDown && GetInputPosition(eventData)) 73 | { 74 | m_pointerId = eventData.pointerId; 75 | 76 | m_inputEventData.Update(m_current, Vector2.zero); 77 | OnPointerDown(eventData, m_inputEventData); 78 | 79 | m_pointerDown = true; 80 | } 81 | } 82 | 83 | protected abstract void OnDrag(PointerEventData pointerEventData, InputEventData inputEventData); 84 | 85 | public void OnDrag(PointerEventData eventData) 86 | { 87 | if ((m_pointerId == eventData.pointerId) && m_pointerDown && GetInputPosition(eventData)) 88 | { 89 | m_inputEventData.Update(m_current, m_current - m_prev); 90 | OnDrag(eventData, m_inputEventData); 91 | } 92 | } 93 | 94 | protected abstract void OnPointerUp(PointerEventData pointerEventData, InputEventData inputEventData); 95 | 96 | public void OnPointerUp(PointerEventData eventData) 97 | { 98 | if ((m_pointerId == eventData.pointerId) && m_pointerDown && GetInputPosition(eventData)) 99 | { 100 | m_inputEventData.Update(m_current, m_current - m_prev); 101 | OnPointerUp(eventData, m_inputEventData); 102 | 103 | m_pointerId = null; 104 | 105 | m_pointerDown = false; 106 | } 107 | } 108 | 109 | protected abstract void OnPointerExit(PointerEventData pointerEventData, InputEventData inputEventData); 110 | 111 | public void OnPointerExit(PointerEventData eventData) 112 | { 113 | if ((m_pointerId == eventData.pointerId) && m_pointerDown) 114 | { 115 | m_inputEventData.Update(m_current, m_current - m_prev); 116 | OnPointerExit(eventData, m_inputEventData); 117 | 118 | m_pointerId = null; 119 | 120 | m_pointerDown = false; 121 | } 122 | } 123 | 124 | protected virtual void OnEnable() 125 | { 126 | var canvas = GetComponentInParent(); 127 | 128 | if (canvas == null) 129 | { 130 | Debug.LogError(THIS_NAME + "canvas not found"); 131 | return; 132 | } 133 | 134 | m_renderMode = canvas.renderMode; 135 | 136 | m_pointerId = null; 137 | 138 | m_pointerDown = false; 139 | } 140 | 141 | protected virtual void OnDisable() 142 | { 143 | m_pointerId = null; 144 | 145 | m_pointerDown = false; 146 | } 147 | 148 | public static bool Range(float i, float min, float max) 149 | { 150 | if (min >= max) 151 | return false; 152 | 153 | return i >= min && i <= max; 154 | } 155 | } 156 | } -------------------------------------------------------------------------------- /Runtime/BaseInputListener.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: acbb9211f24c78d4fa12b9397dfe8296 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Browser.cs: -------------------------------------------------------------------------------- 1 | #define DEBUG 2 | #undef DEBUG 3 | 4 | using System.Collections.Generic; 5 | using UnityEngine; 6 | using TLab.WebView.Widget; 7 | 8 | namespace TLab.WebView 9 | { 10 | public abstract class Browser : FragmentCapture, IBrowser 11 | { 12 | [Header("Web Settings")] 13 | [SerializeField] private string m_url = "https://youtube.com"; 14 | [SerializeField] private Download.Option m_downloadOption; 15 | [SerializeField] private EventCallback m_eventCallback; 16 | [SerializeField] private string[] m_intentFilters; 17 | 18 | public string url => m_url; 19 | 20 | public Download.Option downloadOption => m_downloadOption; 21 | 22 | public string[] intentFilters => m_intentFilters; 23 | 24 | public EventCallback eventCallback => m_eventCallback; 25 | 26 | private string THIS_NAME => "[" + this.GetType() + "] "; 27 | 28 | /// 29 | /// 30 | /// 31 | /// URL that loads first 32 | /// Fps fo rendering 33 | /// The directory of the device to which the content is being downloaded. 34 | public void InitOption(string url, int fps, Download.Option downloadOption) 35 | { 36 | m_url = url; 37 | m_fps = fps; 38 | m_downloadOption = downloadOption; 39 | } 40 | 41 | /// 42 | /// Launch initialize task if WebView is not initialized yet. 43 | /// 44 | /// Web Size 45 | /// Tex Size 46 | /// URL that loads first 47 | /// Fps fo rendering 48 | /// The directory of the device to which the content is being downloaded 49 | public void Init(Vector2Int viewSize, Vector2Int texSize, string url, int fps, Download.Option downloadOption) 50 | { 51 | InitOption(url, fps, downloadOption); 52 | 53 | Init(viewSize, texSize); 54 | } 55 | 56 | protected override void InitNativePlugin() 57 | { 58 | SetDownloadOption(m_downloadOption); 59 | 60 | SetIntentFilters(m_intentFilters); 61 | 62 | SetFps(m_fps); 63 | 64 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 65 | m_NativePlugin.Call(nameof(InitNativePlugin), 66 | m_viewSize.x, m_viewSize.y, 67 | m_texSize.x, m_texSize.y, 68 | m_screenFullRes.x, m_screenFullRes.y, 69 | m_url, m_isVulkan, (int)m_captureMode); 70 | #endif 71 | } 72 | 73 | public void EvaluateJS(string js) 74 | { 75 | if (m_state != State.Initialized) 76 | return; 77 | 78 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 79 | m_NativePlugin.Call(nameof(EvaluateJS), js); 80 | #endif 81 | } 82 | 83 | public string GetUrl() 84 | { 85 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 86 | return m_NativePlugin.Call(nameof(GetUrl)); 87 | #else 88 | return null; 89 | #endif 90 | } 91 | 92 | /// 93 | /// Loads the given URL. 94 | /// 95 | /// The URL of the resource to load 96 | public void LoadUrl(string url) 97 | { 98 | if (m_state != State.Initialized) 99 | return; 100 | 101 | m_url = url; 102 | 103 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 104 | m_NativePlugin.Call(nameof(LoadUrl), url); 105 | #endif 106 | } 107 | 108 | public void SetIntentFilters(string[] filters) 109 | { 110 | m_intentFilters = filters; 111 | 112 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 113 | m_NativePlugin.Call(nameof(SetIntentFilters), filters); 114 | #endif 115 | } 116 | 117 | public string GetAsyncResult(int id) 118 | { 119 | if (m_state != State.Initialized) return ""; 120 | 121 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 122 | return m_NativePlugin.Call(nameof(GetAsyncResult), id); 123 | #else 124 | return ""; 125 | #endif 126 | } 127 | 128 | public void CancelAsyncResult(int id) 129 | { 130 | if (m_state != State.Initialized) return; 131 | 132 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 133 | m_NativePlugin.Call(nameof(CancelAsyncResult), id); 134 | #endif 135 | } 136 | 137 | public IEnumerator GetUserAgent() 138 | { 139 | if (m_state != State.Initialized) 140 | yield break; 141 | 142 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 143 | var id = m_NativePlugin.Call(nameof(GetUserAgent)); 144 | if (id == -1) yield break; 145 | while (true) 146 | { 147 | var @object = GetAsyncResult(id); 148 | if (@object == "") 149 | { 150 | yield return new AsyncString(null, JavaAsyncResult.Status.WAITING); 151 | continue; 152 | } 153 | var result = new JavaAsyncResult(@object); 154 | yield return new AsyncString(result.s, JavaAsyncResult.Status.COMPLETE); 155 | break; 156 | } 157 | yield break; 158 | #else 159 | yield break; 160 | #endif 161 | } 162 | 163 | public void SetUserAgent(string ua, bool reload) 164 | { 165 | if (m_state != State.Initialized) 166 | return; 167 | 168 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 169 | m_NativePlugin.Call(nameof(SetUserAgent), ua, reload); 170 | #endif 171 | } 172 | 173 | public int GetScrollX() 174 | { 175 | if (m_state != State.Initialized) 176 | return 0; 177 | 178 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 179 | return m_NativePlugin.Call(nameof(GetScrollX)); 180 | #else 181 | return 0; 182 | #endif 183 | } 184 | 185 | public int GetScrollY() 186 | { 187 | if (m_state != State.Initialized) 188 | return 0; 189 | 190 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 191 | return m_NativePlugin.Call(nameof(GetScrollY)); 192 | #else 193 | return 0; 194 | #endif 195 | } 196 | 197 | public void ScrollTo(int x, int y) 198 | { 199 | if (m_state != State.Initialized) 200 | return; 201 | 202 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 203 | m_NativePlugin.Call(nameof(ScrollTo), x, y); 204 | #endif 205 | } 206 | 207 | public void ScrollBy(int x, int y) 208 | { 209 | if (m_state != State.Initialized) 210 | return; 211 | 212 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 213 | m_NativePlugin.Call(nameof(ScrollBy), x, y); 214 | #endif 215 | } 216 | 217 | public void DispatchMessageQueue() 218 | { 219 | if (m_state != State.Initialized) 220 | return; 221 | 222 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 223 | var result = m_NativePlugin.Call(nameof(DispatchMessageQueue)); 224 | foreach (var @object in result) 225 | { 226 | var message = new EventCallback.Message(@object); 227 | switch ((EventCallback.Type)message.type) 228 | { 229 | case EventCallback.Type.OnPageStart: 230 | m_eventCallback.onPageStart.Invoke(message.payload); 231 | break; 232 | case EventCallback.Type.OnPageFinish: 233 | m_eventCallback.onPageFinish.Invoke(message.payload); 234 | break; 235 | case EventCallback.Type.OnDownload: 236 | { 237 | m_eventCallback.onDownload.Invoke(new Download.Request(message.payload)); 238 | } 239 | break; 240 | case EventCallback.Type.OnDownloadStart: 241 | { 242 | m_eventCallback.onDownloadStart.Invoke(new Download.EventInfo(message.payload)); 243 | } 244 | break; 245 | case EventCallback.Type.OnDownloadError: 246 | { 247 | m_eventCallback.onDownloadError.Invoke(new Download.EventInfo(message.payload)); 248 | } 249 | break; 250 | case EventCallback.Type.OnDownloadFinish: 251 | { 252 | m_eventCallback.onDownloadFinish.Invoke(new Download.EventInfo(message.payload)); 253 | } 254 | break; 255 | case EventCallback.Type.OnDialog: 256 | { 257 | m_eventCallback.onDialog.Invoke(new AlertDialog.Init(message.payload), null); 258 | } 259 | break; 260 | } 261 | } 262 | #endif 263 | } 264 | 265 | public void GoForward() 266 | { 267 | if (m_state != State.Initialized) 268 | return; 269 | 270 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 271 | m_NativePlugin.Call(nameof(GoForward)); 272 | #endif 273 | } 274 | 275 | public void GoBack() 276 | { 277 | if (m_state != State.Initialized) 278 | return; 279 | 280 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 281 | m_NativePlugin.Call(nameof(GoBack)); 282 | #endif 283 | } 284 | 285 | public long TouchEvent(int x, int y, int action, long downTime) 286 | { 287 | if (m_state != State.Initialized) 288 | return 0; 289 | 290 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 291 | return m_NativePlugin.Call(nameof(TouchEvent), x, y, action, downTime); 292 | #else 293 | return 0; 294 | #endif 295 | } 296 | 297 | public void KeyEvent(char key) 298 | { 299 | if (m_state != State.Initialized) 300 | return; 301 | 302 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 303 | m_NativePlugin.Call(nameof(KeyEvent), key); 304 | #endif 305 | } 306 | 307 | public void KeyEvent(int keyCode) 308 | { 309 | if (m_state != State.Initialized) 310 | return; 311 | 312 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 313 | m_NativePlugin.Call(nameof(KeyEvent), keyCode); 314 | #endif 315 | } 316 | 317 | public void DownloadFromUrl(string url, string userAgent, 318 | string contentDisposition, string mimetype) 319 | { 320 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 321 | m_NativePlugin.Call(nameof(DownloadFromUrl), url, userAgent, contentDisposition, mimetype); 322 | #endif 323 | } 324 | 325 | public void DownloadFromUrl(Download.Request request) => DownloadFromUrl(request.url, request.userAgent, request.contentDisposition, request.mimeType); 326 | 327 | public void SetDownloadOption(Download.Option downloadOption) 328 | { 329 | m_downloadOption = downloadOption; 330 | 331 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 332 | m_NativePlugin.Call(nameof(SetDownloadOption), (int)m_downloadOption.directory, m_downloadOption.subDirectory); 333 | #endif 334 | } 335 | 336 | public float GetDownloadProgress(long id) 337 | { 338 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 339 | return m_NativePlugin.Call(nameof(GetDownloadProgress), id); 340 | #else 341 | return 0.0f; 342 | #endif 343 | } 344 | 345 | public bool CheckForPermission(UnityEngine.Android.Permission permission) 346 | { 347 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 348 | return UnityEngine.Android.Permission.HasUserAuthorizedPermission(permission.ToString()); 349 | #else 350 | return false; 351 | #endif 352 | } 353 | 354 | public void PostDialogResult(AlertDialog.Result result, string json = "") 355 | { 356 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 357 | m_NativePlugin.Call(nameof(PostDialogResult), (int)result, json != null ? json : ""); 358 | #endif 359 | } 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /Runtime/Browser.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0a4a489b321ca4f67a45b6b2194bbf2d 3 | timeCreated: 1512117975 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Runtime/BrowserContainer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace TLab.WebView 4 | { 5 | public class BrowserContainer : MonoBehaviour 6 | { 7 | [SerializeField] private Browser m_browser; 8 | 9 | public Browser browser => m_browser; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Runtime/BrowserContainer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9d320940ed564ff40bf60e74b950a4cc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/BrowserInputField.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using TLab.VKeyborad; 3 | 4 | namespace TLab.WebView 5 | { 6 | public class BrowserInputField : BaseInputField 7 | { 8 | [SerializeField] private BrowserContainer m_container; 9 | 10 | #region KEY_EVENT 11 | 12 | public override void OnBackSpaceKey() 13 | { 14 | m_container.browser?.KeyEvent(67); 15 | 16 | AfterOnBackSpaceKey(); 17 | } 18 | 19 | public override void OnEnterKey() 20 | { 21 | AddKey("\n"); 22 | 23 | AfterOnEnterKey(); 24 | } 25 | 26 | public override void OnTabKey() 27 | { 28 | AddKey("\t"); 29 | 30 | AfterOnTabKey(); 31 | } 32 | 33 | #endregion KEY_EVENT 34 | 35 | public override void AddKey(string key) => m_container.browser.KeyEvent(key.ToCharArray()[0]); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Runtime/BrowserInputField.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 91686fb0930adab4087177278ad40c29 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/BrowserInputListener.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.EventSystems; 3 | 4 | namespace TLab.WebView 5 | { 6 | public class BrowserInputListener : BaseInputListener 7 | { 8 | [SerializeField] private BrowserContainer m_container; 9 | 10 | private long m_downTime; 11 | 12 | private string THIS_NAME => "[" + this.GetType() + "] "; 13 | 14 | public enum TouchEvent 15 | { 16 | Down, 17 | Up, 18 | Drag, 19 | }; 20 | 21 | protected override void OnPointerUp(PointerEventData pointerEventData, InputEventData inputEventData) 22 | { 23 | var position = inputEventData.position; 24 | position.x *= m_container.browser.viewSize.x; 25 | position.y *= m_container.browser.viewSize.y; 26 | m_container.browser.TouchEvent((int)position.x, (int)position.y, (int)TouchEvent.Up, m_downTime); 27 | } 28 | 29 | protected override void OnPointerExit(PointerEventData pointerEventData, InputEventData inputEventData) 30 | { 31 | var position = inputEventData.position; 32 | position.x *= m_container.browser.viewSize.x; 33 | position.y *= m_container.browser.viewSize.y; 34 | m_container.browser.TouchEvent((int)position.x, (int)position.y, (int)TouchEvent.Up, m_downTime); 35 | } 36 | 37 | protected override void OnPointerDown(PointerEventData pointerEventData, InputEventData inputEventData) 38 | { 39 | var position = inputEventData.position; 40 | position.x *= m_container.browser.viewSize.x; 41 | position.y *= m_container.browser.viewSize.y; 42 | m_downTime = m_container.browser.TouchEvent((int)position.x, (int)position.y, (int)TouchEvent.Down, m_downTime); 43 | } 44 | 45 | protected override void OnDrag(PointerEventData pointerEventData, InputEventData inputEventData) 46 | { 47 | var position = inputEventData.position; 48 | position.x *= m_container.browser.viewSize.x; 49 | position.y *= m_container.browser.viewSize.y; 50 | m_container.browser.TouchEvent((int)position.x, (int)position.y, (int)TouchEvent.Drag, m_downTime); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Runtime/BrowserInputListener.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e06f846e1e589444cb635fedc4ba9e4b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/BrowserManager.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace TLab.WebView 4 | { 5 | public class BrowserManager : MonoBehaviour 6 | { 7 | private void Update() 8 | { 9 | FragmentCapture.GarbageCollect(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Runtime/BrowserManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b838d34a939347147a5a851667e526bb 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Common.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Events; 3 | using TLab.WebView.Widget; 4 | 5 | namespace TLab.WebView 6 | { 7 | [System.Serializable] 8 | public class JavaAsyncResult : JSONSerialisable 9 | { 10 | public JavaAsyncResult() : base() { } 11 | public JavaAsyncResult(string json) : base(json) { } 12 | 13 | public static class Status 14 | { 15 | public const int WAITING = 0; 16 | public const int FAILED = 1; 17 | public const int CANCEL = 2; 18 | public const int COMPLETE = 3; 19 | } 20 | 21 | public int id; 22 | public int status; 23 | 24 | public int i; 25 | public double d; 26 | public bool b; 27 | public string s; 28 | } 29 | 30 | public struct AsyncInteger 31 | { 32 | public int value; 33 | public int status; 34 | 35 | public AsyncInteger(int value, int status) 36 | { 37 | this.value = value; 38 | this.status = status; 39 | } 40 | } 41 | 42 | public struct AsyncDouble 43 | { 44 | public int status; 45 | public double value; 46 | 47 | public AsyncDouble(double value, int status) 48 | { 49 | this.value = value; 50 | this.status = status; 51 | } 52 | } 53 | 54 | public struct AsyncBool 55 | { 56 | public int status; 57 | public bool value; 58 | 59 | public AsyncBool(bool value, int status) 60 | { 61 | this.value = value; 62 | this.status = status; 63 | } 64 | } 65 | 66 | public struct AsyncString 67 | { 68 | public int status; 69 | public string value; 70 | 71 | public AsyncString(string value, int status) 72 | { 73 | this.value = value; 74 | this.status = status; 75 | } 76 | } 77 | 78 | public enum CaptureMode 79 | { 80 | HardwareBuffer, 81 | ByteBuffer, 82 | Surface, 83 | } 84 | 85 | [System.Serializable] 86 | public class Download 87 | { 88 | public enum Directory 89 | { 90 | Applicaiton, 91 | Download, 92 | } 93 | 94 | [System.Serializable] 95 | public class Option 96 | { 97 | [SerializeField] private Directory m_directory = Directory.Download; 98 | [SerializeField] private string m_subDirectory = "downloads"; 99 | 100 | public Directory directory => m_directory; 101 | 102 | public string subDirectory => m_subDirectory; 103 | 104 | public void Update(Directory directory) => m_directory = directory; 105 | 106 | public void Update(string subDrectory) => m_subDirectory = subDrectory; 107 | 108 | public void Update(Directory directory, string subDirectory) 109 | { 110 | m_directory = directory; 111 | m_subDirectory = subDirectory; 112 | } 113 | } 114 | 115 | [System.Serializable] 116 | public class Request : JSONSerialisable 117 | { 118 | public Request() : base() { } 119 | public Request(string json) : base(json) { } 120 | 121 | public string url; 122 | public string userAgent; 123 | public string contentDisposition; 124 | public string mimeType; 125 | } 126 | 127 | [System.Serializable] 128 | public class EventInfo : JSONSerialisable 129 | { 130 | public EventInfo() : base() { } 131 | public EventInfo(string json) : base(json) { } 132 | 133 | public string url; 134 | public long id; 135 | } 136 | } 137 | 138 | [System.Serializable] 139 | public class EventCallback 140 | { 141 | public enum Type 142 | { 143 | Raw, 144 | OnPageStart, 145 | OnPageFinish, 146 | OnDownload, 147 | OnDownloadStart, 148 | OnDownloadError, 149 | OnDownloadFinish, 150 | OnDialog, 151 | }; 152 | 153 | [System.Serializable] 154 | public class Message : JSONSerialisable 155 | { 156 | public Message() : base() { } 157 | public Message(string json) : base(json) { } 158 | 159 | public int type; 160 | public string payload; 161 | } 162 | 163 | public UnityEvent onPageStart; 164 | public UnityEvent onPageFinish; 165 | public UnityEvent onDownload; 166 | public UnityEvent onDownloadError; 167 | public UnityEvent onDownloadStart; 168 | public UnityEvent onDownloadFinish; 169 | public UnityEvent onDialog; 170 | } 171 | 172 | public abstract class JSONSerialisable 173 | { 174 | public JSONSerialisable() { } 175 | 176 | public JSONSerialisable(string json) => UnMarshall(json); 177 | 178 | public virtual string Marshall() => JsonUtility.ToJson(this); 179 | 180 | public virtual void UnMarshall(string json) => JsonUtility.FromJsonOverwrite(json, this); 181 | } 182 | 183 | public static class JSUtil 184 | { 185 | public static string ToVariable(string name, string value) 186 | { 187 | return "var " + name + " = " + "'" + value + "';\n"; 188 | } 189 | 190 | public static string ToVariable(string name, int value) 191 | { 192 | return "var " + name + " = " + value + ";\n"; 193 | } 194 | 195 | public static string ToVariable(string name, float value) 196 | { 197 | return "var " + name + " = " + value + ";\n"; 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /Runtime/Common.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 49249167c51d84f45a116fb49ddce280 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/FragmentCapture.cs: -------------------------------------------------------------------------------- 1 | #define DEBUG 2 | #undef DEBUG 3 | 4 | #define TEST 5 | //#undef TEST 6 | 7 | using System; 8 | using System.Collections; 9 | using System.Collections.Generic; 10 | using UnityEngine; 11 | using UnityEngine.UI; 12 | 13 | namespace TLab.WebView 14 | { 15 | public abstract class FragmentCapture : MonoBehaviour, IOffscreen 16 | { 17 | public enum State 18 | { 19 | None, 20 | Initialising, 21 | Initialized, 22 | Destroyed, 23 | } 24 | 25 | [Header("Capture Settings")] 26 | [SerializeField] protected RawImage m_rawImage; 27 | [SerializeField] protected Vector2Int m_viewSize = new Vector2Int(1024, 1024); 28 | [SerializeField] protected Vector2Int m_texSize = new Vector2Int(512, 512); 29 | [SerializeField, Min(1)] protected int m_fps = 30; 30 | [SerializeField] protected CaptureMode m_captureMode = CaptureMode.HardwareBuffer; 31 | 32 | protected bool m_isVulkan; 33 | 34 | private string THIS_NAME => "[" + this.GetType() + "] "; 35 | 36 | public Vector2Int viewSize => m_viewSize; 37 | 38 | public Vector2Int texSize => m_texSize; 39 | 40 | public RawImage rawImage => m_rawImage; 41 | 42 | public int fps => m_fps; 43 | 44 | public CaptureMode captureMode => m_captureMode; 45 | 46 | protected State m_state = State.None; 47 | 48 | public State state => m_state; 49 | 50 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 51 | protected AndroidJavaObject m_NativePlugin; 52 | 53 | private class NativePluginRef 54 | { 55 | public AndroidJavaObject plugin; 56 | 57 | public NativePluginRef(AndroidJavaObject plugin) 58 | { 59 | this.plugin = plugin; 60 | } 61 | } 62 | 63 | private static List m_garbageCollectTargets = new List(); 64 | #endif 65 | 66 | public static void GarbageCollect() 67 | { 68 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 69 | var queue = new Queue(); 70 | 71 | foreach (var disposable in m_garbageCollectTargets) 72 | { 73 | if (disposable.plugin != null) 74 | { 75 | // Wait until Dispose() is completed on the plug-in side. 76 | // Then remove it from Unity management. 77 | if (NativePlugin.GetIsFragmentDisposed((int)disposable.plugin.GetRawObject())) 78 | { 79 | disposable.plugin.Dispose(); 80 | disposable.plugin = null; 81 | queue.Enqueue(disposable); 82 | } 83 | } 84 | } 85 | 86 | foreach (var disposed in queue) 87 | m_garbageCollectTargets.Remove(disposed); 88 | #endif 89 | } 90 | 91 | protected delegate void UpdateFrameFunc(); 92 | 93 | protected UpdateFrameFunc m_updateFrameFunc; 94 | 95 | protected Texture2D m_loadingView; 96 | protected Texture2D m_contentView; 97 | 98 | protected static Vector2Int m_screenFullRes; 99 | 100 | [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)] 101 | protected static void PreInit() 102 | { 103 | // https://github.com/TLabAltoh/TLabWebView/issues/6 104 | m_screenFullRes = new Vector2Int(Screen.width, Screen.height); 105 | } 106 | 107 | /// 108 | /// Launch initialize task if Fragment is not initialized yet. 109 | /// 110 | public virtual void Init() 111 | { 112 | if (m_state == State.None) 113 | StartCoroutine(InitTask()); 114 | } 115 | 116 | /// 117 | /// Set resolution for both View and Texture (called on initialization). 118 | /// 119 | /// View Size 120 | /// Tex Size 121 | public virtual void InitResolution(Vector2Int viewSize, Vector2Int texSize) 122 | { 123 | m_viewSize = viewSize; 124 | m_texSize = texSize; 125 | } 126 | 127 | public virtual void Init(Vector2Int viewSize, Vector2Int texSize) 128 | { 129 | InitResolution(viewSize, texSize); 130 | 131 | Init(); 132 | } 133 | 134 | public virtual string package => ""; 135 | 136 | public virtual IEnumerator InitTask() 137 | { 138 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 139 | // I cannot find the way to preload (load on startup) 140 | // jni shared library. so call library function and 141 | // load dinamically here. (call unity plugin on load) 142 | switch (SystemInfo.renderingThreadingMode) 143 | { 144 | case UnityEngine.Rendering.RenderingThreadingMode.MultiThreaded: 145 | GL.IssuePluginEvent(NativePlugin.DummyRenderEventFunc(), 0); 146 | break; 147 | default: 148 | NativePlugin.DummyRenderEvent(0); 149 | break; 150 | } 151 | #endif 152 | 153 | m_state = State.Initialising; 154 | 155 | yield return new WaitForEndOfFrame(); 156 | 157 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 158 | 159 | if ((m_captureMode != CaptureMode.Surface) && (m_rawImage != null)) 160 | { 161 | m_loadingView = Texture2D.linearGrayTexture; 162 | m_contentView = null; 163 | m_rawImage.texture = m_loadingView; 164 | } 165 | 166 | m_isVulkan = (SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Vulkan); 167 | 168 | m_NativePlugin = new AndroidJavaObject(package); 169 | 170 | switch (m_captureMode) 171 | { 172 | case CaptureMode.HardwareBuffer: 173 | m_updateFrameFunc = m_isVulkan ? UpdateVulkanFrame : UpdateGLESFrame; 174 | break; 175 | case CaptureMode.ByteBuffer: 176 | m_updateFrameFunc = UpdateFrameWithByteBuffer; 177 | break; 178 | case CaptureMode.Surface: 179 | m_updateFrameFunc = UpdateFrameDummy; 180 | break; 181 | } 182 | 183 | if (m_NativePlugin != null) InitNativePlugin(); 184 | 185 | while (!IsInitialized()) 186 | yield return new WaitForEndOfFrame(); 187 | 188 | m_state = State.Initialized; 189 | #endif 190 | } 191 | 192 | protected virtual void InitNativePlugin() { } 193 | 194 | /// 195 | /// If it returns true, this Fragment is already initialized. 196 | /// 197 | /// Whether or not this Fragment is initialized 198 | public bool IsInitialized() 199 | { 200 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 201 | if (m_NativePlugin != null) 202 | { 203 | var instance_ptr = m_NativePlugin.GetRawObject(); 204 | return NativePlugin.GetIsFragmentInitialized((int)instance_ptr); 205 | } 206 | return false; 207 | #else 208 | return false; 209 | #endif 210 | } 211 | 212 | /// 213 | /// Return the texture pointer of the View frame (NOTE: In Vulkan, the VkImage pointer returned by this function could not be used for UpdateExternalTexture. This issue has not been fixed). 214 | /// 215 | /// texture pointer of the view frame (Vulkan: VkImage, OpenGLES: TexID) 216 | public IntPtr GetPlatformTextureID() 217 | { 218 | if (m_state != State.Initialized) 219 | return IntPtr.Zero; 220 | 221 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 222 | return NativePlugin.GetPlatformTextureID((int)m_NativePlugin.GetRawObject()); 223 | #else 224 | return IntPtr.Zero; 225 | #endif 226 | } 227 | 228 | /// 229 | /// 230 | /// 231 | /// Fps fo rendering 232 | public void SetFps(int fps) 233 | { 234 | m_fps = fps; 235 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 236 | m_NativePlugin.Call(nameof(SetFps), fps); 237 | #endif 238 | } 239 | 240 | /// 241 | /// Update Texture resolution 242 | /// 243 | /// Tex Size 244 | public void ResizeTex(Vector2Int texSize) 245 | { 246 | if (m_state != State.Initialized) 247 | return; 248 | 249 | if (m_rawImage != null) 250 | m_rawImage.texture = m_loadingView; 251 | 252 | m_texSize = texSize; 253 | 254 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 255 | m_NativePlugin.Call(nameof(ResizeTex), texSize.x, texSize.y); 256 | #endif 257 | } 258 | 259 | /// 260 | /// Update View resolution 261 | /// 262 | /// 263 | public void ResizeView(Vector2Int viewSize) 264 | { 265 | if (m_state != State.Initialized) 266 | return; 267 | 268 | if (m_rawImage != null) 269 | m_rawImage.texture = m_loadingView; 270 | 271 | m_viewSize = viewSize; 272 | 273 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 274 | m_NativePlugin.Call(nameof(ResizeView), viewSize.x, viewSize.y); 275 | #endif 276 | } 277 | 278 | /// 279 | /// Update resolution for both View and Texture 280 | /// 281 | /// Tex Size 282 | /// Web Size 283 | public void Resize(Vector2Int texSize, Vector2Int viewSize) 284 | { 285 | if (m_state != State.Initialized) 286 | return; 287 | 288 | if (m_rawImage != null) 289 | m_rawImage.texture = m_loadingView; 290 | 291 | m_texSize = texSize; 292 | m_viewSize = viewSize; 293 | 294 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 295 | m_NativePlugin.Call(nameof(Resize), texSize.x, texSize.y, viewSize.x, viewSize.y); 296 | #endif 297 | } 298 | 299 | public void SetSurface(IntPtr surfce, int width, int height) 300 | { 301 | if (m_state != State.Initialized) 302 | return; 303 | 304 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 305 | var instance = m_NativePlugin.GetRawObject(); 306 | NativePlugin.SetSurface((int)instance, (int)surfce, width, height); 307 | #endif 308 | } 309 | 310 | public void RemoveSurface() 311 | { 312 | if (m_state != State.Initialized) 313 | return; 314 | 315 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 316 | var instance = m_NativePlugin.GetRawObject(); 317 | NativePlugin.RemoveSurface((int)instance); 318 | #endif 319 | } 320 | 321 | private void UpdateSurface() 322 | { 323 | // External texture update behaviour 324 | // OpenGLES: Use the same texture 325 | // Vulkan: Create new VkImage and copy buffer to new one, 326 | // Texture Buffer is not shared so in order to update 327 | // frame, need to call update frame every frame. (Maybe 328 | // this processing is too heavy) 329 | 330 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 331 | if (SystemInfo.renderingThreadingMode == UnityEngine.Rendering.RenderingThreadingMode.MultiThreaded) 332 | GL.IssuePluginEvent(NativePlugin.UpdateSharedTextureFunc(), (int)m_NativePlugin.GetRawObject()); 333 | else 334 | NativePlugin.UpdateSharedTexture((int)m_NativePlugin.GetRawObject()); 335 | #endif 336 | } 337 | 338 | protected void UpdateGLESFrame() 339 | { 340 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 341 | UpdateSurface(); 342 | 343 | int instance = (int)m_NativePlugin.GetRawObject(); 344 | 345 | var flag = NativePlugin.ContentExists(instance); 346 | if (!flag) 347 | return; 348 | 349 | flag = NativePlugin.GetSharedBufferUpdateFlag(instance); 350 | if (!flag) 351 | { 352 | var texID = GetPlatformTextureID(); 353 | 354 | // In OpenGLES API, the texture created by 355 | // CreateExternalTexture has same texture pointer 356 | // as function's arguments. And texture size is 357 | // not same with passed as arguments to display 358 | // (it seems same to native texture size). In 359 | // OpenGLES API, is buffer allocated for texture 360 | // size passed as arguments? If so, do I need to 361 | // pass zero (or one) for argments of texture size 362 | // to reduce overhead of memory allocation? 363 | 364 | var tmp = Texture2D.CreateExternalTexture(1, 1, TextureFormat.ARGB32, false, false, texID); 365 | 366 | //Debug.Log(THIS_NAME + $"[CreateExternalTexture] size: {tmp.width}, {tmp.height}, id: {texID}, {tmp.GetNativeTexturePtr()}"); 367 | 368 | NativePlugin.SetSharedBufferUpdateFlag(instance, true); 369 | 370 | m_rawImage.texture = tmp; 371 | var release = m_contentView; 372 | if (release != null) 373 | Destroy(release); 374 | m_contentView = tmp; 375 | } 376 | #endif 377 | } 378 | 379 | protected void UpdateVulkanFrame() 380 | { 381 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 382 | UpdateSurface(); 383 | 384 | int instance = (int)m_NativePlugin.GetRawObject(); 385 | 386 | var flag = NativePlugin.ContentExists(instance); 387 | if (!flag) 388 | return; 389 | 390 | flag = NativePlugin.GetSharedBufferUpdateFlag(instance); 391 | if (!flag) 392 | { 393 | // Destroy the shared texture and verify that 394 | // the native plugin no longer references the 395 | // Unity texture. Because in Vulkan API, native 396 | // plugin directly copied buffer to Unity texture. 397 | 398 | var tmp = new Texture2D(m_texSize.x, m_texSize.y, TextureFormat.RGBA32, false, true); 399 | 400 | NativePlugin.SetUnityTextureID(instance, (long)tmp.GetNativeTexturePtr()); 401 | NativePlugin.SetSharedBufferUpdateFlag(instance, true); 402 | 403 | m_rawImage.texture = tmp; 404 | var release = m_contentView; 405 | if (release != null) 406 | Destroy(release); 407 | m_contentView = tmp; 408 | } 409 | #endif 410 | } 411 | 412 | /// 413 | /// Update frame from CPU side. This function is for non-hardware buffer use case. 414 | /// 415 | protected void UpdateFrameWithByteBuffer() 416 | { 417 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 418 | var flag = NativePlugin.ContentExists((int)m_NativePlugin.GetRawObject()); 419 | if (!flag) 420 | return; 421 | 422 | var buf = (byte[])(Array)m_NativePlugin.Call("GetFrameBuffer"); 423 | // Because the content is already validated, there is 424 | // no need to buffer's null validation here. 425 | 426 | if (m_contentView == null) 427 | m_contentView = new Texture2D(m_texSize.x, m_texSize.y, TextureFormat.RGBA32, false, true); 428 | else 429 | { 430 | if (m_contentView.width * m_contentView.height * 4 != buf.Length) 431 | { 432 | Destroy(m_contentView); 433 | 434 | m_contentView = new Texture2D(m_texSize.x, m_texSize.y, TextureFormat.RGBA32, false, true); 435 | } 436 | } 437 | 438 | m_contentView.LoadRawTextureData(buf); 439 | m_contentView.Apply(); 440 | 441 | m_rawImage.texture = m_contentView; 442 | #endif 443 | } 444 | 445 | protected void UpdateFrameDummy() { } 446 | 447 | /// 448 | /// Request Webview to update frame. 449 | /// 450 | public void UpdateFrame() 451 | { 452 | if (m_state != State.Initialized) 453 | return; 454 | 455 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 456 | m_updateFrameFunc.Invoke(); 457 | #endif 458 | } 459 | 460 | protected virtual void Destroy() 461 | { 462 | if (m_state == State.Destroyed || m_state == State.None) 463 | return; 464 | 465 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 466 | 467 | m_garbageCollectTargets.Add(new NativePluginRef(m_NativePlugin)); 468 | 469 | // I need to call destroy in main thread 470 | // This may delete the external texture sooner than 471 | // the native plugin's destroy process, but this is 472 | // currently not a problem. 473 | Destroy(m_contentView); 474 | m_contentView = null; 475 | 476 | // I need to call this function on unity's render thread 477 | // because ReleaseSharedTexture() call GLES or Vulkan 478 | // function and it needs to be called on render thread. 479 | 480 | if (SystemInfo.renderingThreadingMode == UnityEngine.Rendering.RenderingThreadingMode.MultiThreaded) 481 | GL.IssuePluginEvent(NativePlugin.DisposeFunc(), (int)m_NativePlugin.GetRawObject()); 482 | else 483 | NativePlugin.Dispose((int)m_NativePlugin.GetRawObject()); 484 | 485 | m_state = State.Destroyed; 486 | #endif 487 | } 488 | 489 | #if TEST 490 | /// 491 | /// Test function of SetSurface 492 | /// 493 | public void RenderContent2TmpSurface() 494 | { 495 | if (m_state != State.Initialized) 496 | return; 497 | 498 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 499 | m_NativePlugin.Call(nameof(RenderContent2TmpSurface)); 500 | #endif 501 | } 502 | #endif 503 | 504 | protected void OnDestroy() => Destroy(); 505 | 506 | protected void OnApplicationQuit() => Destroy(); 507 | } 508 | } 509 | -------------------------------------------------------------------------------- /Runtime/FragmentCapture.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5a2103576e49d614f8a4a82123b354b3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/GeckoView.cs: -------------------------------------------------------------------------------- 1 | #define DEBUG 2 | #undef DEBUG 3 | 4 | namespace TLab.WebView 5 | { 6 | public class GeckoView : Browser 7 | { 8 | public override string package => "com.tlab.webkit.gecko.UnityConnect"; 9 | 10 | /// 11 | /// Loads the given HTML. 12 | /// 13 | /// The HTML of the resource to load 14 | public void LoadHTML(string html) 15 | { 16 | if (m_state != State.Initialized) 17 | return; 18 | 19 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 20 | m_NativePlugin.Call(nameof(LoadHTML), html); 21 | #endif 22 | } 23 | 24 | public void ClearData(int flag) 25 | { 26 | #if UNITY_ANDROID && !UNITY_EDITOR || DEBUG 27 | m_NativePlugin.Call(nameof(ClearData), flag); 28 | #endif 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Runtime/GeckoView.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7a30eeb3287654d40af425e3881b7cda 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/IBrowser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using TLab.WebView.Widget; 3 | 4 | namespace TLab.WebView 5 | { 6 | public interface IBrowser 7 | { 8 | /// 9 | /// Run javascript on the current web page. 10 | /// 11 | /// javascript 12 | void EvaluateJS(string js); 13 | 14 | void DispatchMessageQueue(); 15 | 16 | /// 17 | /// Request file download to Download Manager. 18 | /// 19 | /// The full url to the content that should be downloaded 20 | /// The user agent to be used for the download 21 | /// Content-disposition http header, if present 22 | /// The mimetype of the content reported by the server 23 | void DownloadFromUrl(string url, string userAgent, string contentDisposition, string mimetype); 24 | 25 | /// 26 | /// Set the directory in which the file will be downloaded. 27 | /// 28 | void SetDownloadOption(Download.Option downloadOption); 29 | 30 | /// 31 | /// Get the progress of the download event currently being recorded. 32 | /// 33 | /// Current download progress (0 ~ 1) 34 | float GetDownloadProgress(long id); 35 | 36 | /// 37 | /// Update userAgent with the given userAgent string. 38 | /// 39 | /// UserAgent string 40 | /// If true, reload web page when userAgent is updated. 41 | void SetUserAgent(string ua, bool reload); 42 | 43 | /// 44 | /// Capture current userAgent async. 45 | /// 46 | IEnumerator GetUserAgent(); 47 | 48 | string GetAsyncResult(int id); 49 | 50 | void CancelAsyncResult(int id); 51 | 52 | /// 53 | /// Get current url that the WebView instance is loading 54 | /// 55 | /// Current url that the WebView instance is loading 56 | string GetUrl(); 57 | 58 | /// 59 | /// Loads the given URL. 60 | /// 61 | /// The URL of the resource to load. 62 | void LoadUrl(string url); 63 | 64 | /// 65 | /// Register url patterns to treat as deep links 66 | /// 67 | /// Url patterns that are treated as deep links (regular expression) 68 | void SetIntentFilters(string[] intentFilters); 69 | 70 | /// 71 | /// Get content's scroll position x 72 | /// 73 | /// Page content's current scroll position x 74 | int GetScrollX(); 75 | 76 | /// 77 | /// Get content's scroll position y 78 | /// 79 | /// Page content's current scroll position y 80 | int GetScrollY(); 81 | 82 | /// 83 | /// Set content's scroll position. 84 | /// 85 | /// Scroll position x of the destination 86 | /// Scroll position y of the destination 87 | void ScrollTo(int x, int y); 88 | 89 | /// 90 | /// Move the scrolled position of WebView 91 | /// 92 | /// The amount of pixels to scroll by horizontally 93 | /// The amount of pixels to scroll by vertically 94 | void ScrollBy(int x, int y); 95 | 96 | /// 97 | /// Dispatch of a touch event. 98 | /// 99 | /// Touch position x 100 | /// Touch position y 101 | /// Touch event type (TOUCH_DOWN: 0, TOUCH_UP: 1, TOUCH_MOVE: 2) 102 | /// Start time of touch event 103 | public long TouchEvent(int x, int y, int action, long downTime); 104 | 105 | /// 106 | /// Dispatch of a basic keycode event. 107 | /// 108 | /// 'a', 'b', 'A' .... 109 | void KeyEvent(char key); 110 | 111 | void KeyEvent(int keyCode); 112 | 113 | /// 114 | /// Goes back in the history of this WebView. 115 | /// 116 | void GoBack(); 117 | 118 | /// 119 | /// Goes forward in the history of this WebView. 120 | /// 121 | void GoForward(); 122 | 123 | public void PostDialogResult(AlertDialog.Result result, string json = ""); 124 | } 125 | } -------------------------------------------------------------------------------- /Runtime/IBrowser.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2424e8c1ba8d3e94bb0ada422885f44f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/IOffscreen.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace TLab.WebView 4 | { 5 | public interface IOffscreen 6 | { 7 | void Resize(Vector2Int texSize, Vector2Int viewSize); 8 | 9 | void ResizeTex(Vector2Int texSize); 10 | 11 | void ResizeView(Vector2Int viewSize); 12 | 13 | void SetFps(int fps); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Runtime/IOffscreen.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: be1a70d2bd8a2264785e2cccd3175e26 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/NativePlugin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace TLab.WebView 5 | { 6 | public static class NativePlugin 7 | { 8 | public const string LIB_SHARED_TEXTURE = "shared-texture"; 9 | public const string LIB_NATIVE = "jni-libTLabWebView"; 10 | 11 | [DllImport(LIB_SHARED_TEXTURE)] 12 | public static extern IntPtr DummyRenderEventFunc(); 13 | 14 | [DllImport(LIB_SHARED_TEXTURE)] 15 | public static extern void DummyRenderEvent(int instance_ptr); 16 | 17 | [DllImport(LIB_NATIVE)] 18 | public static extern IntPtr UpdateSharedTextureFunc(); 19 | 20 | [DllImport(LIB_NATIVE)] 21 | public static extern void UpdateSharedTexture(int instance_ptr); 22 | 23 | [DllImport(LIB_NATIVE)] 24 | public static extern IntPtr DisposeFunc(); 25 | 26 | [DllImport(LIB_NATIVE)] 27 | public static extern void Dispose(int instance_ptr); 28 | 29 | [DllImport(LIB_NATIVE)] 30 | public static extern void ReleaseSharedTexture(int instance_ptr); 31 | 32 | [DllImport(LIB_NATIVE)] 33 | public static extern IntPtr GetPlatformTextureID(int instance_ptr); 34 | 35 | [DllImport(LIB_NATIVE)] 36 | public static extern void SetUnityTextureID(int instance_ptr, long unity_texture_id); 37 | 38 | [DllImport(LIB_NATIVE)] 39 | public static extern bool ContentExists(int instance_ptr); 40 | 41 | [DllImport(LIB_NATIVE)] 42 | public static extern void SetSurface(int instance_ptr, int surface_ptr, int width, int height); 43 | 44 | [DllImport(LIB_NATIVE)] 45 | public static extern void RemoveSurface(int instance_ptr); 46 | 47 | [DllImport(LIB_NATIVE)] 48 | public static extern bool GetIsFragmentInitialized(int instance_ptr); 49 | 50 | [DllImport(LIB_NATIVE)] 51 | public static extern bool GetIsFragmentDisposed(int instance_ptr); 52 | 53 | [DllImport(LIB_NATIVE)] 54 | public static extern bool GetSharedBufferUpdateFlag(int instance_ptr); 55 | 56 | [DllImport(LIB_NATIVE)] 57 | public static extern void SetSharedBufferUpdateFlag(int instance_ptr, bool value); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Runtime/NativePlugin.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dea4a79192d15a64fb3d28a9c265cf50 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Sample.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a1de803110f2af140b176e24749efc3f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Sample/BrowserBGSample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace TLab.WebView 4 | { 5 | public class BrowserBGSample : MonoBehaviour 6 | { 7 | [SerializeField] private BrowserContainer m_container; 8 | 9 | public string THIS_NAME => "[" + this.GetType() + "] "; 10 | 11 | public void StartWebView() 12 | { 13 | if (m_container.browser.state == FragmentCapture.State.None) 14 | StartCoroutine(m_container.browser.InitTask()); 15 | } 16 | 17 | void Start() => StartWebView(); 18 | 19 | void Update() 20 | { 21 | m_container.browser.UpdateFrame(); 22 | m_container.browser.DispatchMessageQueue(); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Runtime/Sample/BrowserBGSample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d28931cc0f22a8b4ab00e1abc6613722 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Sample/BrowserSample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace TLab.WebView 4 | { 5 | public class BrowserSample : MonoBehaviour 6 | { 7 | [SerializeField] private BrowserContainer m_container; 8 | 9 | private string THIS_NAME => "[" + this.GetType() + "] "; 10 | 11 | void Start() => m_container.browser.Init(); 12 | 13 | void Update() 14 | { 15 | m_container.browser.UpdateFrame(); 16 | m_container.browser.DispatchMessageQueue(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Runtime/Sample/BrowserSample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bf40c0688e3d64308af19095b03a055d 3 | timeCreated: 1511841092 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Runtime/Sample/CreateNewInRuntime.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace TLab.WebView.Sample 5 | { 6 | public class CreateNewInRuntime : MonoBehaviour 7 | { 8 | [SerializeField] private GameObject m_prefab; 9 | [SerializeField] private Transform m_anchor; 10 | 11 | private Queue m_instances = new Queue(); 12 | 13 | public void CreateNew() 14 | { 15 | var instance = (m_anchor == null) ? Instantiate(m_prefab) : Instantiate(m_prefab, m_anchor.position, m_anchor.rotation); 16 | 17 | instance.transform.parent = null; 18 | 19 | m_instances.Enqueue(instance); 20 | } 21 | 22 | public void Delete() 23 | { 24 | if (m_instances.Count > 0) 25 | { 26 | var instance = m_instances.Dequeue(); 27 | 28 | Destroy(instance); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Runtime/Sample/CreateNewInRuntime.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 71ab015f6dd5f4146be40b76eac2ff25 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Sample/DownloadHandlerSample.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Text; 3 | using System.Linq; 4 | using UnityEngine; 5 | 6 | namespace TLab.WebView.Sample 7 | { 8 | public class DownloadHandlerSample : MonoBehaviour 9 | { 10 | [SerializeField] private BrowserContainer m_container; 11 | 12 | private bool m_downloading = false; 13 | 14 | private string THIS_NAME => "[" + this.GetType() + "] "; 15 | 16 | private IEnumerator DownloadProgress(long id) 17 | { 18 | m_downloading = true; 19 | 20 | while (m_downloading) 21 | { 22 | Debug.Log(THIS_NAME + "progress: " + m_container.browser.GetDownloadProgress(id)); 23 | yield return null; 24 | } 25 | } 26 | 27 | public void OnDownloadError(Download.EventInfo info) => Debug.Log(THIS_NAME + $"Error !"); 28 | 29 | public void OnDownloadStart(Download.EventInfo info) 30 | { 31 | Debug.Log(THIS_NAME + $"Start !"); 32 | 33 | StartCoroutine(DownloadProgress(info.id)); 34 | } 35 | 36 | public void OnDownloadFinish(Download.EventInfo info) 37 | { 38 | m_downloading = false; 39 | 40 | Debug.Log(THIS_NAME + $"Finish !"); 41 | } 42 | 43 | private class BlobDataFechInfo : JSONSerialisable 44 | { 45 | public string id; 46 | public string mimeType; 47 | 48 | public BlobDataFechInfo(string id, string mimeType) 49 | { 50 | this.id = id; 51 | this.mimeType = mimeType; 52 | } 53 | } 54 | 55 | public void OnFetchBlobData(string argment) 56 | { 57 | const string THIS_NAME = nameof(OnFetchBlobData); 58 | 59 | var info = JsonUtility.FromJson(argment); 60 | Debug.Log(THIS_NAME + $"{nameof(BlobDataFechInfo)}: {info.id}, {info.mimeType}"); 61 | 62 | if (m_container.browser is not WebView) 63 | return; 64 | 65 | // data:[][;base64], 66 | 67 | var browser = m_container.browser as WebView; 68 | var buf = browser.GetJSBuffer(info.id); 69 | 70 | var index = buf.Select((x, i) => (x, i)).First((c) => c.x == ',').i + 1; 71 | 72 | Debug.Log(THIS_NAME + $"Buffer: {buf[0]}, {buf[1]}, {buf[2]}, {buf[3]}, {buf[4]}, {buf[buf.Length - 1]}, length: {buf.Length}"); 73 | 74 | var base64Encoded = System.Convert.FromBase64String(Encoding.UTF8.GetString(buf, index, buf.Length - index)); 75 | var stringEncoded = Encoding.UTF8.GetString(base64Encoded); 76 | const int OFFSET = 20; 77 | 78 | for (index = 0; index < stringEncoded.Length - OFFSET; index += OFFSET) 79 | Debug.Log(THIS_NAME + $"String: {stringEncoded.Substring(index, OFFSET)}"); 80 | Debug.Log(THIS_NAME + $"String: {stringEncoded.Substring(index, stringEncoded.Length - index)}"); 81 | 82 | var js = $"window.tlab.free('{info.id}');"; 83 | 84 | m_container.browser.EvaluateJS(js); 85 | } 86 | 87 | public void FetchBlobData(string url, string mimeType) 88 | { 89 | // https://developer.mozilla.org/ja/docs/Web/API/FileReader/readAsDataURL 90 | var js = JSUtil.ToVariable("url", url) + JSUtil.ToVariable("mimetype", mimeType); 91 | js += JSUtil.ToVariable("go", gameObject.name) + JSUtil.ToVariable("method", nameof(OnFetchBlobData)) + JSUtil.ToVariable("argments", new BlobDataFechInfo(url, mimeType).Marshall()); 92 | js += Resources.Load("TLab/WebView/Samples/Scripts/JS/fetch-blob")?.ToString(); 93 | 94 | m_container.browser.EvaluateJS(js); 95 | } 96 | 97 | public void Log(string message) => Debug.Log(THIS_NAME + $"message receive: {message}"); 98 | 99 | public void OnDownlaod(Download.Request request) 100 | { 101 | Debug.Log(THIS_NAME + $"OnDownload ... url:{request.url}, userAgent:{request.userAgent}, contentDisposition:{request.contentDisposition}, mimeType:{request.mimeType}"); 102 | 103 | #if true 104 | m_container.browser.DownloadFromUrl(request); 105 | #else 106 | if (request.url.StartsWith("blob:")) 107 | FetchBlobData(request.url, request.mimeType); 108 | else 109 | m_container.browser.DownloadFromUrl(request); 110 | #endif 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Runtime/Sample/DownloadHandlerSample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 02591813623dc3d4f888e789ed6a0709 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Sample/FocusInOutInteractionSample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.EventSystems; 3 | using TLab.VKeyborad; 4 | 5 | namespace TLab.WebView.Sample 6 | { 7 | public class FocusInOutInteractionSample : MonoBehaviour, IPointerDownHandler 8 | { 9 | [SerializeField] private SearchBar m_searchBar; 10 | [SerializeField] private BaseInputField m_inputField; 11 | [SerializeField] private BrowserContainer m_container; 12 | 13 | public void OnPageFinish(string url) 14 | { 15 | var js = JSUtil.ToVariable("go", gameObject.name) + JSUtil.ToVariable("method", nameof(OnMessage)); 16 | js += Resources.Load("TLab/WebView/Samples/Scripts/JS/focus-in-out-interaction")?.ToString(); 17 | 18 | m_container.browser.EvaluateJS(js); 19 | } 20 | 21 | public void OnMessage(string message) 22 | { 23 | Debug.Log("OnMessage: " + message); 24 | switch (message) 25 | { 26 | case "Focusin": 27 | m_inputField.OnFocus(true); 28 | break; 29 | case "Focusout": 30 | m_inputField.OnFocus(false); 31 | break; 32 | } 33 | } 34 | 35 | public void OnPointerDown(PointerEventData eventData) => m_searchBar.OnFocus(false); 36 | } 37 | } -------------------------------------------------------------------------------- /Runtime/Sample/FocusInOutInteractionSample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6f3f4b5e8c6e974419de27e162572523 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Sample/JSSnippets.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace TLab.WebView.Sample 4 | { 5 | public class JSSnippets : MonoBehaviour 6 | { 7 | [SerializeField] private BrowserContainer m_container; 8 | 9 | public void DisableBeforeUnload() 10 | { 11 | var js = Resources.Load("TLab/WebView/Samples/Scripts/JS/disable-beforunload")?.ToString(); 12 | m_container.browser.EvaluateJS(js); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Runtime/Sample/JSSnippets.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 962d2766778659747829f813f889d3b3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Sample/LoadLocalFileSample.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.IO.Compression; 3 | using UnityEngine; 4 | 5 | namespace TLab.WebView.Sample 6 | { 7 | public class LoadLocalFileSample : MonoBehaviour 8 | { 9 | [SerializeField] private BrowserContainer m_container; 10 | 11 | private string THIS_NAME => "[" + this.GetType() + "] "; 12 | 13 | public void RequestPermission() 14 | { 15 | // If you want to load a file from an external location 16 | // (e.g. download folder, image folder...), you need to 17 | // grant the MANAGE_EXTERNAL_STORAGE permission and 18 | // request the permission at runtime. 19 | 20 | // https://developer.android.com/training/data-storage/manage-all-files?hl=en 21 | 22 | // HasUserAuthorisedPermission may not reflect these 23 | // permission changes immediately, so this is not a 24 | // good implementation. 25 | 26 | if (!UnityEngine.Android.Permission.HasUserAuthorizedPermission("android.permission.MANAGE_EXTERNAL_STORAGE")) 27 | UnityEngine.Android.Permission.RequestUserPermission("android.permission.MANAGE_EXTERNAL_STORAGE"); 28 | 29 | // I recommended to use streaming asset to load HTML 30 | // content archived file (like zip) and extract to 31 | // it in application folder. 32 | } 33 | 34 | public void OpenLocalHTML(string filePath) 35 | { 36 | // jar:file:///xxxxxxxxxx 37 | var url = "file://" + Application.persistentDataPath + "/local-html"; 38 | 39 | url += ("/" + filePath); 40 | 41 | Debug.Log(THIS_NAME + $"url: {url}"); 42 | 43 | m_container.browser.LoadUrl(url); 44 | } 45 | 46 | private void Start() 47 | { 48 | var subPath = "/local-html"; 49 | 50 | var srcZip = Application.streamingAssetsPath + subPath + ".zip"; 51 | 52 | #if UNITY_EDITOR 53 | var dstPath = Application.dataPath; 54 | #else 55 | var dstPath = Application.persistentDataPath; 56 | #endif 57 | var dstZip = dstPath + $"{subPath}/{subPath}.zip"; 58 | 59 | var outputDir = dstPath + "/" + subPath; 60 | if (Directory.Exists(outputDir)) 61 | Directory.Delete(outputDir, true); 62 | 63 | Directory.CreateDirectory(outputDir); 64 | 65 | var request = new WWW(srcZip); 66 | while (!request.isDone) { } 67 | 68 | if (!File.Exists(dstZip)) 69 | File.WriteAllBytes(dstZip, request.bytes); 70 | else 71 | { 72 | File.Delete(dstZip); 73 | File.WriteAllBytes(dstZip, request.bytes); 74 | } 75 | 76 | ZipFile.ExtractToDirectory(dstZip, outputDir); 77 | 78 | Debug.Log(THIS_NAME + $"done !"); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Runtime/Sample/LoadLocalFileSample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3eb7acc0e3f89b14cb34b3c718c93105 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Sample/Util.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7e3c7ba2891f89a40ab79f1fb9c1d995 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Sample/Util/FPSCounter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using TMPro; 5 | 6 | namespace TLab.WebView.Sample 7 | { 8 | public class FPSCounter : MonoBehaviour 9 | { 10 | [SerializeField, HideInInspector] private TextMeshProUGUI m_counter; 11 | 12 | void Start() 13 | { 14 | m_counter = GetComponent(); 15 | Application.targetFrameRate = 60; 16 | } 17 | 18 | void Update() 19 | { 20 | m_counter.text = (1.0f / Time.deltaTime).ToString("0.000"); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Runtime/Sample/Util/FPSCounter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2894552dbe5e17949ae8a617c509d5cc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Sample/Widget.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 106c88d3f79db15429c569ccbb835075 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Sample/Widget/AlertDialogSample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using TMPro; 3 | 4 | namespace TLab.WebView.Widget.Sample 5 | { 6 | public class AlertDialogSample : MonoBehaviour, IAlertDialogHandler, IWidgetHandler 7 | { 8 | [SerializeField] private GameObject m_content; 9 | [SerializeField] private TextMeshProUGUI m_positive; 10 | [SerializeField] private TextMeshProUGUI m_neutral; 11 | [SerializeField] private TextMeshProUGUI m_negative; 12 | [SerializeField] private TextMeshProUGUI m_title; 13 | [SerializeField] private TextMeshProUGUI m_message; 14 | [SerializeField] private BrowserInputListener m_inputListener; 15 | 16 | private AlertDialog m_dialog; 17 | 18 | private string THIS_NAME => "[" + this.GetType() + "] "; 19 | 20 | private void ModifyTextMeshPro(TextMeshProUGUI textMesh, bool active, string text) 21 | { 22 | textMesh.text = text; 23 | textMesh.transform.parent.gameObject.SetActive(active); 24 | } 25 | 26 | public static string POSITIVE = "Positive"; 27 | public static string NEGATIVE = "Negative"; 28 | public static string NEUTRAL = "Neutral"; 29 | 30 | public void Close() 31 | { 32 | m_dialog = null; 33 | 34 | m_content.SetActive(false); 35 | 36 | m_inputListener.enabled = true; 37 | 38 | ModifyTextMeshPro(m_positive, false, POSITIVE); 39 | ModifyTextMeshPro(m_neutral, false, NEUTRAL); 40 | ModifyTextMeshPro(m_negative, false, NEGATIVE); 41 | } 42 | 43 | public void OnDialog(AlertDialog.Init init, AlertDialog dialog) 44 | { 45 | m_dialog = dialog; 46 | 47 | m_inputListener.enabled = false; 48 | 49 | ModifyTextMeshPro(m_positive, init.positive, init.positiveLabel); 50 | ModifyTextMeshPro(m_neutral, init.neutral, init.neutralLabel); 51 | ModifyTextMeshPro(m_negative, init.negative, init.negativeLabel); 52 | ModifyTextMeshPro(m_title, init.title != "", init.title); 53 | ModifyTextMeshPro(m_message, !m_dialog.hasOverlay && (init.message != ""), init.message); 54 | 55 | m_content.SetActive(true); 56 | } 57 | 58 | public void Positive() => m_dialog?.Post(AlertDialog.Result.POSITIVE); 59 | 60 | public void Neutral() => m_dialog?.Post(AlertDialog.Result.NEUTRAL); 61 | 62 | public void Negative() => m_dialog?.Post(AlertDialog.Result.NEGATIVE); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Runtime/Sample/Widget/AlertDialogSample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bef5629e74e2e9b4b9d1447fbc470c50 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Sample/Widget/AuthWidgetSample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using TLab.VKeyborad; 3 | 4 | namespace TLab.WebView.Widget.Sample 5 | { 6 | public class AuthWidgetSample : MonoBehaviour, IWidgetHandler 7 | { 8 | [SerializeField] private GameObject m_content; 9 | [SerializeField] private InputField m_username; 10 | [SerializeField] private InputField m_password; 11 | 12 | private void InitInputField(InputField target, string text, bool active) 13 | { 14 | target.text = text; 15 | 16 | var parent = target.transform.parent.gameObject; 17 | parent.SetActive(active); 18 | } 19 | 20 | public void Close() => m_content.SetActive(false); 21 | 22 | public void OnDialog(AuthWidget.Init init, AuthWidget widget) 23 | { 24 | InitInputField(m_username, init.username, !init.onlyPassword); 25 | InitInputField(m_password, init.password, true); 26 | 27 | m_content.SetActive(true); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Runtime/Sample/Widget/AuthWidgetSample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5724b6c5040a322489970385415f772b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Sample/Widget/ColorWidgetSample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.UI; 3 | 4 | namespace TLab.WebView.Widget.Sample 5 | { 6 | public class ColorWidgetSample : MonoBehaviour, IWidgetHandler 7 | { 8 | [SerializeField] private GameObject m_content; 9 | [SerializeField] private Slider m_r; 10 | [SerializeField] private Slider m_g; 11 | [SerializeField] private Slider m_b; 12 | [SerializeField] private Image m_image; 13 | 14 | private ColorWidget m_picker; 15 | 16 | public void OnSliderChanged() 17 | { 18 | m_picker.rgb = new Color(m_r.value, m_g.value, m_b.value); 19 | m_image.color = m_picker.rgb; 20 | } 21 | 22 | public void Close() 23 | { 24 | m_picker = null; 25 | m_content.SetActive(false); 26 | } 27 | 28 | public void OnDialog(ColorWidget.Init init, ColorWidget widget) 29 | { 30 | m_picker = widget; 31 | 32 | var rgb = ColorWidget.UnMarshall(init.color); 33 | m_r.value = rgb.r; 34 | m_g.value = rgb.g; 35 | m_b.value = rgb.b; 36 | 37 | OnSliderChanged(); 38 | 39 | m_content.SetActive(true); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Runtime/Sample/Widget/ColorWidgetSample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a455f2ec3a4aeae47b14959fadcb0dfd 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Sample/Widget/DateTimeWidgetSample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace TLab.WebView.Widget.Sample 4 | { 5 | public class DateTimeWidgetSample : MonoBehaviour, IWidgetHandler 6 | { 7 | [SerializeField] private GameObject m_content; 8 | 9 | [Header("Date")] 10 | [SerializeField] private NumberPicker m_year; 11 | [SerializeField] private NumberPicker m_month; 12 | [SerializeField] private NumberPicker m_dayOfMonth; 13 | 14 | [Header("Time")] 15 | [SerializeField] private NumberPicker m_hour; 16 | [SerializeField] private NumberPicker m_minutes; 17 | 18 | public static int[] monthOfYear = new int[] { 31, 28 /* 29 */, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 19 | 20 | private string THIS_NAME => "[" + this.GetType() + "] "; 21 | 22 | private DateTimeWidget m_widget; 23 | 24 | public void OnMonthChanged(int month) 25 | { 26 | var prev = m_dayOfMonth.value; 27 | m_dayOfMonth.range = new Vector2Int(1, monthOfYear[month - 1]); 28 | if (prev != m_dayOfMonth.value) 29 | m_widget?.OnDayOfMonthChanged(m_dayOfMonth.value); 30 | } 31 | 32 | public void Close() 33 | { 34 | m_widget = null; 35 | m_content.SetActive(false); 36 | } 37 | 38 | private void InitNumberPicker(NumberPicker picker, bool active, int value) 39 | { 40 | picker.gameObject.SetActive(active); 41 | picker.value = value; 42 | } 43 | 44 | public void OnDialog(DateTimeWidget.Init init, DateTimeWidget widget) 45 | { 46 | m_widget = widget; 47 | 48 | InitNumberPicker(m_year, init.date, init.year); 49 | InitNumberPicker(m_month, init.date, init.month); 50 | InitNumberPicker(m_dayOfMonth, init.date, init.dayOfMonth); 51 | 52 | InitNumberPicker(m_hour, init.time, init.hour); 53 | InitNumberPicker(m_minutes, init.time, init.minutes); 54 | 55 | m_content.SetActive(true); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Runtime/Sample/Widget/DateTimeWidgetSample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 850e378b05f5d594caa80403014d1156 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Sample/Widget/SelectWidgetSample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.UI; 3 | using UnityEngine.Events; 4 | 5 | namespace TLab.WebView.Widget.Sample 6 | { 7 | public class SelectWidgetSample : MonoBehaviour, IWidgetHandler 8 | { 9 | [SerializeField] private GameObject m_content; 10 | [SerializeField] private ScrollRect m_scroll; 11 | 12 | [Header("Item")] 13 | [SerializeField] private GameObject m_selectable; 14 | [SerializeField] private GameObject m_group; 15 | [SerializeField] private GameObject m_separator; 16 | 17 | private string THIS_NAME => "[" + this.GetType() + "] "; 18 | 19 | public void Close() => m_content.SetActive(false); 20 | 21 | private Button.ButtonClickedEvent Lambda2Event(UnityAction action) 22 | { 23 | var tmp = new Button.ButtonClickedEvent(); 24 | tmp.AddListener(action); 25 | return tmp; 26 | } 27 | 28 | private UnityAction m_clear; 29 | 30 | private void ClearChild(Transform target) 31 | { 32 | for (int i = 0; i < target.childCount; i++) 33 | Destroy(target.GetChild(i).gameObject); 34 | } 35 | 36 | public void OnDialog(SelectWidget.Init init, SelectWidget widget) 37 | { 38 | ClearChild(m_scroll.content); 39 | 40 | var items = new GameObject[init.options.Length]; 41 | 42 | m_clear = () => 43 | { 44 | for (int i = 0; i < init.options.Length; i++) 45 | { 46 | var option = init.options[i]; 47 | 48 | switch (option.type) 49 | { 50 | case SelectWidget.ModifiableChoice.Type.DEFAULT: 51 | { 52 | items[i].GetComponent().color = Color.white; 53 | } 54 | break; 55 | } 56 | } 57 | }; 58 | 59 | for (int i = 0; i < init.options.Length; i++) 60 | { 61 | var option = init.options[i]; 62 | 63 | switch (option.type) 64 | { 65 | case SelectWidget.ModifiableChoice.Type.DEFAULT: 66 | { 67 | var tmp = i; 68 | items[i] = Instantiate(m_selectable, m_scroll.content); 69 | items[i].GetComponentInChildren().text = option.label; 70 | items[i].GetComponent().color = option.selected ? Color.gray : Color.white; 71 | items[i].GetComponent