commit acb394e0cb6c714e7d21c682f19bbf1f8d41c198 Author: wsycarlos Date: Wed May 20 15:46:35 2026 +0800 Initial Commit WebView diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ff03d40 --- /dev/null +++ b/.gitignore @@ -0,0 +1,106 @@ +# This .gitignore file should be placed at the root of your Unity project directory +# +# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore +# +.utmp/ +/[Ll]ibrary/ +/[Tt]emp/ +/[Oo]bj/ +/[Bb]uild/ +/[Bb]uilds/ +/[Ll]ogs/ +/[Uu]ser[Ss]ettings/ +*.log + +# By default unity supports Blender asset imports, *.blend1 blender files do not need to be commited to version control. +*.blend1 +*.blend1.meta + +# MemoryCaptures can get excessive in size. +# They also could contain extremely sensitive data +/[Mm]emoryCaptures/ + +# Recordings can get excessive in size +/[Rr]ecordings/ + +# Uncomment this line if you wish to ignore the asset store tools plugin +# /[Aa]ssets/AssetStoreTools* + +# Autogenerated Jetbrains Rider plugin +/[Aa]ssets/Plugins/Editor/JetBrains* +# Jetbrains Rider personal-layer settings +*.DotSettings.user + +# Visual Studio cache directory +.vs/ + +# Gradle cache directory +.gradle/ + +# Autogenerated VS/MD/Consulo solution and project files +ExportedObj/ +.consulo/ +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.mdb +*.opendb +*.VC.db + +# Unity3D generated meta files +*.pidb.meta +*.pdb.meta +*.mdb.meta + +# Unity3D generated file on crash reports +sysinfo.txt + +# Mono auto generated files +mono_crash.* + +# Builds +*.apk +*.aab +*.unitypackage +*.unitypackage.meta + +# Crashlytics generated file +crashlytics-build.properties + +# TestRunner generated files +InitTestScene*.unity* + +# Addressables default ignores, before user customizations +/ServerData +/[Aa]ssets/StreamingAssets/aa* +/[Aa]ssets/AddressableAssetsData/link.xml* +/[Aa]ssets/Addressables_Temp* +# By default, Addressables content builds will generate addressables_content_state.bin +# files in platform-specific subfolders, for example: +# /Assets/AddressableAssetsData/OSX/addressables_content_state.bin +/[Aa]ssets/AddressableAssetsData/*/*.bin* + +# Visual Scripting auto-generated files +/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Flow/UnitOptions.db +/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Flow/UnitOptions.db.meta +/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Core/Property Providers +/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Core/Property Providers.meta + +# Auto-generated scenes by play mode tests +/[Aa]ssets/[Ii]nit[Tt]est[Ss]cene*.unity* + +.vsconfig + +Logs/ +[Aa]ssets/ +[Pp]ublish/ +.vscode/settings.json +*.dat \ No newline at end of file diff --git a/Packages/com.bywaystudios.webview/Plugins.meta b/Packages/com.bywaystudios.webview/Plugins.meta new file mode 100644 index 0000000..a772e93 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3fab9ef1899dc4bf18586c6a01172d62 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/Android.meta b/Packages/com.bywaystudios.webview/Plugins/Android.meta new file mode 100644 index 0000000..a55a2c2 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Android.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dbc2200488e7d45189f9a082caf5d637 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/Android/WebViewPlugin.aar.tmpl b/Packages/com.bywaystudios.webview/Plugins/Android/WebViewPlugin.aar.tmpl new file mode 100644 index 0000000..d93266d Binary files /dev/null and b/Packages/com.bywaystudios.webview/Plugins/Android/WebViewPlugin.aar.tmpl differ diff --git a/Packages/com.bywaystudios.webview/Plugins/Android/WebViewPlugin.aar.tmpl.meta b/Packages/com.bywaystudios.webview/Plugins/Android/WebViewPlugin.aar.tmpl.meta new file mode 100644 index 0000000..d4aa354 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Android/WebViewPlugin.aar.tmpl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 69e600137811c42778f78793d1022369 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/Editor.meta b/Packages/com.bywaystudios.webview/Plugins/Editor.meta new file mode 100644 index 0000000..4afc429 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5415c2bc4488c42739b36767bc7f8c83 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/Editor/UnityWebViewPostprocessBuild.cs b/Packages/com.bywaystudios.webview/Plugins/Editor/UnityWebViewPostprocessBuild.cs new file mode 100644 index 0000000..12c5668 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Editor/UnityWebViewPostprocessBuild.cs @@ -0,0 +1,202 @@ +using System; +using System.IO; +using System.Text; +using System.Xml; +using UnityEditor; +using UnityEditor.Android; +using UnityEditor.Callbacks; +using UnityEngine; + +namespace Byway.UnityWebView +{ + public class UnityWebViewPostprocessBuild : IPostGenerateGradleAndroidProject + { + public void OnPostGenerateGradleAndroidProject(string basePath) + { + var changed = false; + var androidManifest = new AndroidManifest(GetManifestPath(basePath)); + changed = (androidManifest.SetExported(true) || changed); + changed = (androidManifest.SetWindowSoftInputMode("adjustPan") || changed); + changed = (androidManifest.SetHardwareAccelerated(true) || changed); + if (changed) + { + androidManifest.Save(); + Debug.Log("unitywebview: adjusted AndroidManifest.xml."); + } + } + + public int callbackOrder + { + get + { + return 1; + } + } + + private string GetManifestPath(string basePath) + { + var pathBuilder = new StringBuilder(basePath); + pathBuilder.Append(Path.DirectorySeparatorChar).Append("src"); + pathBuilder.Append(Path.DirectorySeparatorChar).Append("main"); + pathBuilder.Append(Path.DirectorySeparatorChar).Append("AndroidManifest.xml"); + return pathBuilder.ToString(); + } + + [PostProcessBuild(100)] + public static void OnPostprocessBuild(BuildTarget buildTarget, string path) + { + if (buildTarget == BuildTarget.iOS) + { + string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj"; + var type = Type.GetType("UnityEditor.iOS.Xcode.PBXProject, UnityEditor.iOS.Extensions.Xcode"); + if (type == null) + { + Debug.LogError("unitywebview: failed to get PBXProject. please install iOS build support."); + return; + } + var src = File.ReadAllText(projPath); + //dynamic proj = type.GetConstructor(Type.EmptyTypes).Invoke(null); + var proj = type.GetConstructor(Type.EmptyTypes).Invoke(null); + //proj.ReadFromString(src); + { + var method = type.GetMethod("ReadFromString"); + method.Invoke(proj, new object[] { src }); + } + var target = ""; + //target = proj.GetUnityFrameworkTargetGuid(); + { + var method = type.GetMethod("GetUnityFrameworkTargetGuid"); + target = (string)method.Invoke(proj, null); + } + //proj.AddFrameworkToProject(target, "WebKit.framework", false); + { + var method = type.GetMethod("AddFrameworkToProject"); + method.Invoke(proj, new object[] { target, "WebKit.framework", false }); + } + var cflags = ""; + if (EditorUserBuildSettings.development) + { + cflags += " -DUNITYWEBVIEW_DEVELOPMENT"; + } + cflags = cflags.Trim(); + if (!string.IsNullOrEmpty(cflags)) + { + // proj.AddBuildProperty(target, "OTHER_LDFLAGS", cflags); + var method = type.GetMethod("AddBuildProperty", new Type[] { typeof(string), typeof(string), typeof(string) }); + method.Invoke(proj, new object[] { target, "OTHER_CFLAGS", cflags }); + } + var dst = ""; + //dst = proj.WriteToString(); + { + var method = type.GetMethod("WriteToString"); + dst = (string)method.Invoke(proj, null); + } + File.WriteAllText(projPath, dst); + } + } + } + + internal class AndroidXmlDocument : XmlDocument + { + private string m_Path; + protected XmlNamespaceManager nsMgr; + public readonly string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android"; + + public AndroidXmlDocument(string path) + { + m_Path = path; + using (var reader = new XmlTextReader(m_Path)) + { + reader.Read(); + Load(reader); + } + nsMgr = new XmlNamespaceManager(NameTable); + nsMgr.AddNamespace("android", AndroidXmlNamespace); + } + + public string Save() + { + return SaveAs(m_Path); + } + + public string SaveAs(string path) + { + using (var writer = new XmlTextWriter(path, new UTF8Encoding(false))) + { + writer.Formatting = Formatting.Indented; + Save(writer); + } + return path; + } + } + + internal class AndroidManifest : AndroidXmlDocument + { + private readonly XmlElement ManifestElement; + private readonly XmlElement ApplicationElement; + + public AndroidManifest(string path) : base(path) + { + ManifestElement = SelectSingleNode("/manifest") as XmlElement; + ApplicationElement = SelectSingleNode("/manifest/application") as XmlElement; + } + + internal XmlNode GetActivityWithLaunchIntent() + { + return + SelectSingleNode( + "/manifest/application/activity[intent-filter/action/@android:name='android.intent.action.MAIN' and " + + "intent-filter/category/@android:name='android.intent.category.LAUNCHER']", + nsMgr); + } + + // for api level 33 + internal bool SetExported(bool enabled) + { + bool changed = false; + var activity = GetActivityWithLaunchIntent() as XmlElement; + if (activity == null) + { + return false; + } + if (activity.GetAttribute("exported", AndroidXmlNamespace) != ((enabled) ? "true" : "false")) + { + activity.SetAttribute("exported", AndroidXmlNamespace, (enabled) ? "true" : "false"); + changed = true; + } + return changed; + } + + internal bool SetWindowSoftInputMode(string mode) + { + bool changed = false; + var activity = GetActivityWithLaunchIntent() as XmlElement; + if (activity == null) + { + return false; + } + if (activity.GetAttribute("windowSoftInputMode", AndroidXmlNamespace) != mode) + { + activity.SetAttribute("windowSoftInputMode", AndroidXmlNamespace, mode); + changed = true; + } + return changed; + } + + internal bool SetHardwareAccelerated(bool enabled) + { + bool changed = false; + var activity = GetActivityWithLaunchIntent() as XmlElement; + if (activity == null) + { + return false; + } + if (activity.GetAttribute("hardwareAccelerated", AndroidXmlNamespace) != ((enabled) ? "true" : "false")) + { + activity.SetAttribute("hardwareAccelerated", AndroidXmlNamespace, (enabled) ? "true" : "false"); + changed = true; + } + return changed; + } + } +} \ No newline at end of file diff --git a/Packages/com.bywaystudios.webview/Plugins/Editor/UnityWebViewPostprocessBuild.cs.meta b/Packages/com.bywaystudios.webview/Plugins/Editor/UnityWebViewPostprocessBuild.cs.meta new file mode 100644 index 0000000..2d862b5 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Editor/UnityWebViewPostprocessBuild.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0b2f5f306eb6e4afcbc074e6efccc188 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/WebView.bundle.meta b/Packages/com.bywaystudios.webview/Plugins/WebView.bundle.meta new file mode 100644 index 0000000..42a688b --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/WebView.bundle.meta @@ -0,0 +1,58 @@ +fileFormatVersion: 2 +guid: 60e7bf38137eb4950b2f02b7d57c1ad3 +folderAsset: yes +PluginImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + isPreloaded: 0 + platformData: + Android: + enabled: 0 + settings: + CPU: AnyCPU + Any: + enabled: 0 + settings: {} + Editor: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: OSX + Linux: + enabled: 0 + settings: + CPU: x86 + Linux64: + enabled: 0 + settings: + CPU: x86_64 + OSXIntel: + enabled: 1 + settings: + CPU: AnyCPU + OSXIntel64: + enabled: 1 + settings: + CPU: AnyCPU + OSXUniversal: + enabled: 1 + settings: + CPU: AnyCPU + Win: + enabled: 0 + settings: + CPU: AnyCPU + Win64: + enabled: 0 + settings: + CPU: AnyCPU + iOS: + enabled: 0 + settings: + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/WebView.bundle/Contents/Info.plist b/Packages/com.bywaystudios.webview/Plugins/WebView.bundle/Contents/Info.plist new file mode 100644 index 0000000..d5c20eb --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/WebView.bundle/Contents/Info.plist @@ -0,0 +1,48 @@ + + + + + BuildMachineOSBuild + 25E253 + CFBundleDevelopmentRegion + English + CFBundleExecutable + WebView + CFBundleIdentifier + net.gree.unitywebview.WebView + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + WebView + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 25E236 + DTPlatformName + macosx + DTPlatformVersion + 26.4 + DTSDKBuild + 25E236 + DTSDKName + macosx26.4 + DTXcode + 2640 + DTXcodeBuild + 17E192 + LSMinimumSystemVersion + 10.13 + + diff --git a/Packages/com.bywaystudios.webview/Plugins/WebView.bundle/Contents/MacOS/WebView b/Packages/com.bywaystudios.webview/Plugins/WebView.bundle/Contents/MacOS/WebView new file mode 100644 index 0000000..52c19cf Binary files /dev/null and b/Packages/com.bywaystudios.webview/Plugins/WebView.bundle/Contents/MacOS/WebView differ diff --git a/Packages/com.bywaystudios.webview/Plugins/WebView.bundle/Contents/Resources/InfoPlist.strings b/Packages/com.bywaystudios.webview/Plugins/WebView.bundle/Contents/Resources/InfoPlist.strings new file mode 100644 index 0000000..5e45963 Binary files /dev/null and b/Packages/com.bywaystudios.webview/Plugins/WebView.bundle/Contents/Resources/InfoPlist.strings differ diff --git a/Packages/com.bywaystudios.webview/Plugins/WebView.bundle/Contents/_CodeSignature/CodeResources b/Packages/com.bywaystudios.webview/Plugins/WebView.bundle/Contents/_CodeSignature/CodeResources new file mode 100644 index 0000000..f4d2e43 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/WebView.bundle/Contents/_CodeSignature/CodeResources @@ -0,0 +1,128 @@ + + + + + files + + Resources/InfoPlist.strings + + MiLKDDnrUKr4EmuvhS5VQwxHGK8= + + + files2 + + Resources/InfoPlist.strings + + hash2 + + Oc8u4Ht7Mz58F50L9NeYpbcq9qTlhPUeZCcDu/pPyCg= + + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Packages/com.bywaystudios.webview/Plugins/Windows.meta b/Packages/com.bywaystudios.webview/Plugins/Windows.meta new file mode 100644 index 0000000..4c07dfa --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Windows.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9a69d5fe42677e84b8100f858cb66df7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/Windows/x64.meta b/Packages/com.bywaystudios.webview/Plugins/Windows/x64.meta new file mode 100644 index 0000000..6d0d8b4 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Windows/x64.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0763797fead754049b4255cee0993cb7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/Windows/x64/README.txt b/Packages/com.bywaystudios.webview/Plugins/Windows/x64/README.txt new file mode 100644 index 0000000..51c69ca --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Windows/x64/README.txt @@ -0,0 +1,4 @@ +Place the Windows WebView plugin DLL here: + WebView.dll (64-bit, built from plugins/Windows) + +Build instructions: see plugins/Windows/README.md diff --git a/Packages/com.bywaystudios.webview/Plugins/Windows/x64/README.txt.meta b/Packages/com.bywaystudios.webview/Plugins/Windows/x64/README.txt.meta new file mode 100644 index 0000000..f1b77af --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Windows/x64/README.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f62969be983297d4aa100a72a1ef672d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/Windows/x64/WebView.dll b/Packages/com.bywaystudios.webview/Plugins/Windows/x64/WebView.dll new file mode 100644 index 0000000..34243f2 Binary files /dev/null and b/Packages/com.bywaystudios.webview/Plugins/Windows/x64/WebView.dll differ diff --git a/Packages/com.bywaystudios.webview/Plugins/Windows/x64/WebView.dll.meta b/Packages/com.bywaystudios.webview/Plugins/Windows/x64/WebView.dll.meta new file mode 100644 index 0000000..a9dd066 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Windows/x64/WebView.dll.meta @@ -0,0 +1,70 @@ +fileFormatVersion: 2 +guid: 8c2ac249de280af4ca5514719b5a934a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : + second: + enabled: 0 + settings: {} + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 0 + Exclude OSXUniversal: 0 + Exclude Win: 1 + Exclude Win64: 0 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: x86_64 + DefaultValueInitialized: true + OS: Windows + - first: + Standalone: Linux64 + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 1 + settings: + CPU: x86_64 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/Windows/x86.meta b/Packages/com.bywaystudios.webview/Plugins/Windows/x86.meta new file mode 100644 index 0000000..0dab753 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Windows/x86.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 796e71d62d97fe14fa9d58010d2976fb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/Windows/x86/README.txt b/Packages/com.bywaystudios.webview/Plugins/Windows/x86/README.txt new file mode 100644 index 0000000..ceabd77 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Windows/x86/README.txt @@ -0,0 +1,4 @@ +Place the Windows WebView plugin DLL here: + WebView.dll (32-bit, built from plugins/Windows) + +Build instructions: see plugins/Windows/README.md diff --git a/Packages/com.bywaystudios.webview/Plugins/Windows/x86/README.txt.meta b/Packages/com.bywaystudios.webview/Plugins/Windows/x86/README.txt.meta new file mode 100644 index 0000000..e8efe96 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Windows/x86/README.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ea032e1e67ffb2845b15ed2d0ca48d7a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/Windows/x86/WebView.dll b/Packages/com.bywaystudios.webview/Plugins/Windows/x86/WebView.dll new file mode 100644 index 0000000..908e4d9 Binary files /dev/null and b/Packages/com.bywaystudios.webview/Plugins/Windows/x86/WebView.dll differ diff --git a/Packages/com.bywaystudios.webview/Plugins/Windows/x86/WebView.dll.meta b/Packages/com.bywaystudios.webview/Plugins/Windows/x86/WebView.dll.meta new file mode 100644 index 0000000..7ce685e --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/Windows/x86/WebView.dll.meta @@ -0,0 +1,70 @@ +fileFormatVersion: 2 +guid: ed66942567a0baa4e99daba51749d1c5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : + second: + enabled: 0 + settings: {} + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 0 + Exclude OSXUniversal: 0 + Exclude Win: 0 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: x86 + DefaultValueInitialized: true + OS: Windows + - first: + Standalone: Linux64 + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Standalone: Win + second: + enabled: 1 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/iOS.meta b/Packages/com.bywaystudios.webview/Plugins/iOS.meta new file mode 100644 index 0000000..352898e --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/iOS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a53a54acdc5d64291aa49766bb494025 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/iOS/WebView.mm b/Packages/com.bywaystudios.webview/Plugins/iOS/WebView.mm new file mode 100644 index 0000000..01d6dfc --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/iOS/WebView.mm @@ -0,0 +1,1309 @@ +/* + * Copyright (C) 2011 Keijiro Takahashi + * Copyright (C) 2012 GREE, Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#if !(__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) + +#import +#import + +// NOTE: we need extern without "C" before unity 4.5 +//extern UIViewController *UnityGetGLViewController(); +extern "C" UIViewController *UnityGetGLViewController(); +extern "C" void UnitySendMessage(const char *, const char *, const char *); + +// cf. https://stackoverflow.com/questions/26383031/wkwebview-causes-my-view-controller-to-leak/33365424#33365424 +@interface WeakScriptMessageDelegate : NSObject + +@property (nonatomic, weak) id scriptDelegate; + +- (instancetype)initWithDelegate:(id)scriptDelegate; + +@end + +@implementation WeakScriptMessageDelegate + +- (instancetype)initWithDelegate:(id)scriptDelegate +{ + self = [super init]; + if (self) { + _scriptDelegate = scriptDelegate; + } + return self; +} + +- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message +{ + [self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message]; +} + +@end + +@protocol WebViewProtocol +@property (nonatomic, getter=isOpaque) BOOL opaque; +@property (nullable, nonatomic, copy) UIColor *backgroundColor UI_APPEARANCE_SELECTOR; +@property (nonatomic, getter=isHidden) BOOL hidden; +@property (nonatomic) CGRect frame; +@property (nullable, nonatomic, weak) id navigationDelegate; +@property (nullable, nonatomic, weak) id UIDelegate; +@property (nullable, nonatomic, readonly, copy) NSURL *URL; +- (void)load:(NSURLRequest *)request; +- (void)loadHTML:(NSString *)html baseURL:(NSURL *)baseUrl; +- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ __nullable)(__nullable id, NSError * __nullable error))completionHandler; +@property (nonatomic, readonly) BOOL canGoBack; +@property (nonatomic, readonly) BOOL canGoForward; +- (void)goBack; +- (void)goForward; +- (void)reload; +- (void)stopLoading; +- (void)setScrollbarsVisibility:(BOOL)visibility; +- (void)setScrollBounce:(BOOL)enable; +@end + +@interface WKWebView(WebViewProtocolConformed) +@end + +@implementation WKWebView(WebViewProtocolConformed) + +- (void)load:(NSURLRequest *)request +{ + WKWebView *webView = (WKWebView *)self; + NSURL *url = [request URL]; + if ([url.absoluteString hasPrefix:@"file:"]) { + NSURL *top = [NSURL URLWithString:[[url absoluteString] stringByDeletingLastPathComponent]]; + [webView loadFileURL:url allowingReadAccessToURL:top]; + } else { + [webView loadRequest:request]; + } +} + +- (NSURLRequest *)constructionCustomHeader:(NSURLRequest *)originalRequest with:(NSDictionary *)headerDictionary +{ + NSMutableURLRequest *convertedRequest = originalRequest.mutableCopy; + for (NSString *key in [headerDictionary allKeys]) { + [convertedRequest setValue:headerDictionary[key] forHTTPHeaderField:key]; + } + return (NSURLRequest *)[convertedRequest copy]; +} + +- (void)loadHTML:(NSString *)html baseURL:(NSURL *)baseUrl +{ + WKWebView *webView = (WKWebView *)self; + [webView loadHTMLString:html baseURL:baseUrl]; +} + +- (void)setScrollbarsVisibility:(BOOL)visibility +{ + WKWebView *webView = (WKWebView *)self; + webView.scrollView.showsHorizontalScrollIndicator = visibility; + webView.scrollView.showsVerticalScrollIndicator = visibility; +} + +- (void)setScrollBounce:(BOOL)enable +{ + WKWebView *webView = (WKWebView *)self; + webView.scrollView.bounces = enable; +} + +@end + +@interface CWebViewPlugin : NSObject +{ + UIView *webView; + NSString *gameObjectName; + NSMutableDictionary *customRequestHeader; + BOOL googleAppRedirectionEnabled; + BOOL alertDialogEnabled; + NSRegularExpression *allowRegex; + NSRegularExpression *denyRegex; + NSRegularExpression *hookRegex; + NSString *basicAuthUserName; + NSString *basicAuthPassword; +} +@end + +@implementation CWebViewPlugin + +static WKProcessPool *_sharedProcessPool; +static NSMutableArray *_instances = [[NSMutableArray alloc] init]; + +- (BOOL)isInitialized +{ + return webView != nil; +} + +- (id)initWithGameObjectName:(const char *)gameObjectName_ transparent:(BOOL)transparent zoom:(BOOL)zoom ua:(const char *)ua enableWKWebView:(BOOL)enableWKWebView contentMode:(WKContentMode)contentMode allowsLinkPreview:(BOOL)allowsLinkPreview allowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures radius:(int)radius +{ + self = [super init]; + + gameObjectName = [NSString stringWithUTF8String:gameObjectName_]; + customRequestHeader = [[NSMutableDictionary alloc] init]; + googleAppRedirectionEnabled = false; + alertDialogEnabled = true; + allowRegex = nil; + denyRegex = nil; + hookRegex = nil; + basicAuthUserName = nil; + basicAuthPassword = nil; + UIView *view = UnityGetGLViewController().view; + if (enableWKWebView && [WKWebView class]) { + if (_sharedProcessPool == NULL) { + _sharedProcessPool = [[WKProcessPool alloc] init]; + } + WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; + WKUserContentController *controller = [[WKUserContentController alloc] init]; + [controller addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:@"unityControl"]; + [controller addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:@"saveDataURL"]; + { + NSString *str = @"\ +window.Unity = { \ + call: function(msg) { \ + window.webkit.messageHandlers.unityControl.postMessage(msg); \ + }, \ + saveDataURL: function(fileName, dataURL) { \ + window.webkit.messageHandlers.saveDataURL.postMessage(fileName + '\t' + dataURL); \ + } \ +}; \ +"; + WKUserScript *script = [[WKUserScript alloc] initWithSource:str injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]; + [controller addUserScript:script]; + } + if (!zoom) { + NSString *str = @"\ +(function() { \ + var meta = document.querySelector('meta[name=viewport]'); \ + if (meta == null) { \ + meta = document.createElement('meta'); \ + meta.name = 'viewport'; \ + } \ + meta.content += ((meta.content.length > 0) ? ',' : '') + 'user-scalable=no'; \ + var head = document.getElementsByTagName('head')[0]; \ + head.appendChild(meta); \ +})(); \ +"; + WKUserScript *script = [[WKUserScript alloc] initWithSource:str injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; + [controller addUserScript:script]; + } + configuration.userContentController = controller; + configuration.allowsInlineMediaPlayback = true; + if (@available(iOS 10.0, *)) { + configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone; + } else { + if (@available(iOS 9.0, *)) { + configuration.requiresUserActionForMediaPlayback = NO; + } else { + configuration.mediaPlaybackRequiresUserAction = NO; + } + } + configuration.websiteDataStore = [WKWebsiteDataStore defaultDataStore]; + configuration.processPool = _sharedProcessPool; + if (@available(iOS 13.0, *)) { + configuration.defaultWebpagePreferences.preferredContentMode = contentMode; + } +#if UNITYWEBVIEW_IOS_ALLOW_FILE_URLS + // cf. https://stackoverflow.com/questions/35554814/wkwebview-xmlhttprequest-with-file-url/44365081#44365081 + try { + [configuration.preferences setValue:@TRUE forKey:@"allowFileAccessFromFileURLs"]; + } + catch (NSException *ex) { + } + try { + [configuration setValue:@TRUE forKey:@"allowUniversalAccessFromFileURLs"]; + } + catch (NSException *ex) { + } +#endif + WKWebView *wkwebView = [[WKWebView alloc] initWithFrame:view.frame configuration:configuration]; +#if UNITYWEBVIEW_DEVELOPMENT + NSOperatingSystemVersion version = { 16, 4, 0 }; + if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:version]) { + wkwebView.inspectable = true; + } +#endif + wkwebView.allowsLinkPreview = allowsLinkPreview; + wkwebView.allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures; + webView = wkwebView; + webView.UIDelegate = self; + webView.navigationDelegate = self; + if (radius > 0) { + webView.layer.cornerRadius = radius; + webView.layer.masksToBounds = YES; + } + if (ua != NULL && strcmp(ua, "") != 0) { + ((WKWebView *)webView).customUserAgent = [[NSString alloc] initWithUTF8String:ua]; + } + // cf. https://rick38yip.medium.com/wkwebview-weird-spacing-issue-in-ios-13-54a4fc686f72 + // cf. https://stackoverflow.com/questions/44390971/automaticallyadjustsscrollviewinsets-was-deprecated-in-ios-11-0 + if (@available(iOS 11.0, *)) { + ((WKWebView *)webView).scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } else { + //UnityGetGLViewController().automaticallyAdjustsScrollViewInsets = false; + } + } else { + webView = nil; + return self; + } + if (transparent) { + webView.opaque = NO; + webView.backgroundColor = [UIColor clearColor]; + } + webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + webView.hidden = YES; + + [webView addObserver:self forKeyPath: @"loading" options: NSKeyValueObservingOptionNew context:nil]; + + [view addSubview:webView]; + + //set webview for Unity 6 accessibility hierarchy + NSMutableArray *accessibilityElements + = view.accessibilityElements ? [view.accessibilityElements mutableCopy] : [NSMutableArray array]; + [accessibilityElements addObject:(UIAccessibilityElement *)webView]; + view.accessibilityElements = accessibilityElements; + + return self; +} + +- (void)dispose +{ + if (webView != nil) { + UIView *webView0 = webView; + webView = nil; + if ([webView0 isKindOfClass:[WKWebView class]]) { + webView0.UIDelegate = nil; + webView0.navigationDelegate = nil; + [((WKWebView *)webView0).configuration.userContentController removeScriptMessageHandlerForName:@"saveDataURL"]; + [((WKWebView *)webView0).configuration.userContentController removeScriptMessageHandlerForName:@"unityControl"]; + } + [webView0 stopLoading]; + [webView0 removeFromSuperview]; + [webView0 removeObserver:self forKeyPath:@"loading"]; + + //remove the WebViewObject from Unity hierarchy tree + UIView *view = UnityGetGLViewController().view; + NSMutableArray *accessibilityElements + = view.accessibilityElements ? [view.accessibilityElements mutableCopy] : [NSMutableArray array]; + [accessibilityElements removeObject: (UIAccessibilityElement *)webView0]; + view.accessibilityElements = accessibilityElements; + } + basicAuthPassword = nil; + basicAuthUserName = nil; + hookRegex = nil; + denyRegex = nil; + allowRegex = nil; + customRequestHeader = nil; + gameObjectName = nil; +} + ++ (void)resetSharedProcessPool +{ + // cf. https://stackoverflow.com/questions/33156567/getting-all-cookies-from-wkwebview/49744695#49744695 + _sharedProcessPool = [[WKProcessPool alloc] init]; + [_instances enumerateObjectsUsingBlock:^(CWebViewPlugin *obj, NSUInteger idx, BOOL *stop) { + if ([obj->webView isKindOfClass:[WKWebView class]]) { + WKWebView *webView = (WKWebView *)obj->webView; + webView.configuration.processPool = _sharedProcessPool; + } + }]; +} + ++ (void)clearCookie:(const char *)name of:(const char *)url +{ + NSURL *nsurl = [NSURL URLWithString:[[NSString alloc] initWithUTF8String:url]]; + if (nsurl == nil) { + return; + } + NSString *nsname = [NSString stringWithUTF8String:name]; + if (@available(iOS 9.0, *)) { + WKHTTPCookieStore *cookieStore = WKWebsiteDataStore.defaultDataStore.httpCookieStore; + [cookieStore + getAllCookies:^(NSArray *array) { + [array + enumerateObjectsUsingBlock:^(NSHTTPCookie *cookie, NSUInteger idx, BOOL *stop) { + if ([cookie.name isEqualToString:nsname] + && [cookie.domain isEqualToString:nsurl.host] + && [cookie.path isEqualToString:nsurl.path]) { + [cookieStore deleteCookie:cookie completionHandler:^{}]; + } + }]; + }]; + } else { + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + if (cookieStorage == nil) { + // cf. https://stackoverflow.com/questions/33876295/nshttpcookiestorage-sharedhttpcookiestorage-comes-up-empty-in-10-11 + cookieStorage = [NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:@"Cookies"]; + } + [[cookieStorage cookies] enumerateObjectsUsingBlock:^(NSHTTPCookie *cookie, NSUInteger idx, BOOL *stop) { + if ([cookie.name isEqualToString:nsname] + && [cookie.domain isEqualToString:nsurl.host] + && [cookie.path isEqualToString:nsurl.path]) { + [cookieStorage deleteCookie:cookie]; + } + }]; + } +} + ++ (void)clearCookies +{ + [CWebViewPlugin resetSharedProcessPool]; + + // cf. https://dev.classmethod.jp/smartphone/remove-webview-cookies/ + NSString *libraryPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject; + NSString *cookiesPath = [libraryPath stringByAppendingPathComponent:@"Cookies"]; + NSString *webKitPath = [libraryPath stringByAppendingPathComponent:@"WebKit"]; + [[NSFileManager defaultManager] removeItemAtPath:cookiesPath error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:webKitPath error:nil]; + + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + if (cookieStorage == nil) { + // cf. https://stackoverflow.com/questions/33876295/nshttpcookiestorage-sharedhttpcookiestorage-comes-up-empty-in-10-11 + cookieStorage = [NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:@"Cookies"]; + } + [[cookieStorage cookies] enumerateObjectsUsingBlock:^(NSHTTPCookie *cookie, NSUInteger idx, BOOL *stop) { + [cookieStorage deleteCookie:cookie]; + }]; + + NSOperatingSystemVersion version = { 9, 0, 0 }; + if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:version]) { + // cf. https://stackoverflow.com/questions/46465070/how-to-delete-cookies-from-wkhttpcookiestore/47928399#47928399 + NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; + NSDate *date = [NSDate dateWithTimeIntervalSince1970:0]; + [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes + modifiedSince:date + completionHandler:^{}]; + } +} + ++ saveCookies +{ + [CWebViewPlugin resetSharedProcessPool]; +} + +- (void)getCookies:(const char *)url +{ + NSOperatingSystemVersion version = { 9, 0, 0 }; + if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:version]) { + NSURL *nsurl = [NSURL URLWithString:[[NSString alloc] initWithUTF8String:url]]; + WKHTTPCookieStore *cookieStore = WKWebsiteDataStore.defaultDataStore.httpCookieStore; + [cookieStore + getAllCookies:^(NSArray *array) { + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; + [formatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss zzz"]; + NSMutableString *result = [NSMutableString string]; + [array enumerateObjectsUsingBlock:^(NSHTTPCookie *cookie, NSUInteger idx, BOOL *stop) { + if ([cookie.domain isEqualToString:nsurl.host]) { + [result appendString:[NSString stringWithFormat:@"%@=%@", cookie.name, cookie.value]]; + if ([cookie.domain length] > 0) { + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Domain=%@", cookie.domain]]; + } + if ([cookie.path length] > 0) { + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Path=%@", cookie.path]]; + } + if (cookie.expiresDate != nil) { + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Expires=%@", [formatter stringFromDate:cookie.expiresDate]]]; + } + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Version=%zd", cookie.version]]; + [result appendString:[NSString stringWithFormat:@"\n"]]; + } + }]; + UnitySendMessage([gameObjectName UTF8String], "CallOnCookies", [result UTF8String]); + }]; + } else { + [CWebViewPlugin resetSharedProcessPool]; + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; + [formatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss zzz"]; + NSMutableString *result = [NSMutableString string]; + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + if (cookieStorage == nil) { + // cf. https://stackoverflow.com/questions/33876295/nshttpcookiestorage-sharedhttpcookiestorage-comes-up-empty-in-10-11 + cookieStorage = [NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:@"Cookies"]; + } + [[cookieStorage cookiesForURL:[NSURL URLWithString:[[NSString alloc] initWithUTF8String:url]]] + enumerateObjectsUsingBlock:^(NSHTTPCookie *cookie, NSUInteger idx, BOOL *stop) { + [result appendString:[NSString stringWithFormat:@"%@=%@", cookie.name, cookie.value]]; + if ([cookie.domain length] > 0) { + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Domain=%@", cookie.domain]]; + } + if ([cookie.path length] > 0) { + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Path=%@", cookie.path]]; + } + if (cookie.expiresDate != nil) { + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Expires=%@", [formatter stringFromDate:cookie.expiresDate]]]; + } + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Version=%zd", cookie.version]]; + [result appendString:[NSString stringWithFormat:@"\n"]]; + }]; + UnitySendMessage([gameObjectName UTF8String], "CallOnCookies", [result UTF8String]); + } +} + +- (void)userContentController:(WKUserContentController *)userContentController + didReceiveScriptMessage:(WKScriptMessage *)message { + + // Log out the message received + //NSLog(@"Received event %@", message.body); + if ([message.name isEqualToString:@"unityControl"]) { + UnitySendMessage([gameObjectName UTF8String], "CallFromJS", [[NSString stringWithFormat:@"%@", message.body] UTF8String]); + } else if ([message.name isEqualToString:@"saveDataURL"]) { + NSRange range = [message.body rangeOfString:@"\t"]; + if (range.location == NSNotFound) { + return; + } + NSString *fileName = [[message.body substringWithRange:NSMakeRange(0, range.location)] lastPathComponent]; + NSString *dataURL = [message.body substringFromIndex:(range.location + 1)]; + range = [dataURL rangeOfString:@"data:"]; + if (range.location != 0) { + return; + } + NSString *tmp = [dataURL substringFromIndex:[@"data:" length]]; + range = [tmp rangeOfString:@";"]; + if (range.location == NSNotFound) { + return; + } + NSString *base64data = [tmp substringFromIndex:(range.location + 1 + [@"base64," length])]; + NSString *type = [tmp substringWithRange:NSMakeRange(0, range.location)]; + NSData *data = [[NSData alloc] initWithBase64EncodedString:base64data options:0]; + NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; + path = [path stringByAppendingString:@"/Downloads"]; + BOOL isDir; + NSError *err = nil; + if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) { + if (!isDir) { + return; + } + } else { + [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&err]; + if (err != nil) { + return; + } + } + NSString *prefix = [path stringByAppendingString:@"/"]; + path = [prefix stringByAppendingString:fileName]; + int count = 0; + while ([[NSFileManager defaultManager] fileExistsAtPath:path]) { + count++; + NSString *name = [fileName stringByDeletingPathExtension]; + NSString *ext = [fileName pathExtension]; + if (ext.length == 0) { + path = [NSString stringWithFormat:@"%@%@ (%d)", prefix, name, count]; + } else { + path = [NSString stringWithFormat:@"%@%@ (%d).%@", prefix, name, count, ext]; + } + } + [data writeToFile:path atomically:YES]; + } + + /* + // Then pull something from the device using the message body + NSString *version = [[UIDevice currentDevice] valueForKey:message.body]; + + // Execute some JavaScript using the result? + NSString *exec_template = @"set_headline(\"received: %@\");"; + NSString *exec = [NSString stringWithFormat:exec_template, version]; + [webView evaluateJavaScript:exec completionHandler:nil]; + */ +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + if (webView == nil) + return; + + if ([keyPath isEqualToString:@"loading"] && [[change objectForKey:NSKeyValueChangeNewKey] intValue] == 0 + && [webView URL] != nil) { + UnitySendMessage( + [gameObjectName UTF8String], + "CallOnLoaded", + [[[webView URL] absoluteString] UTF8String]); + + } +} + +- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView +{ + UnitySendMessage([gameObjectName UTF8String], "CallOnError", "webViewWebContentProcessDidTerminate"); +} + +- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error +{ + UnitySendMessage([gameObjectName UTF8String], "CallOnError", [[error description] UTF8String]); +} + +- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error +{ + UnitySendMessage([gameObjectName UTF8String], "CallOnError", [[error description] UTF8String]); +} + +- (WKWebView *)webView:(WKWebView *)wkWebView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures +{ + // cf. for target="_blank", cf. http://qiita.com/ShingoFukuyama/items/b3a1441025a36ab7659c + if (!navigationAction.targetFrame.isMainFrame) { + [wkWebView loadRequest:navigationAction.request]; + } + return nil; +} + +- (void)webView:(WKWebView *)wkWebView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler +{ + if (webView == nil) { + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + NSURL *nsurl = [navigationAction.request URL]; + NSString *url = [nsurl absoluteString]; + BOOL pass = YES; + if (allowRegex != nil && [allowRegex firstMatchInString:url options:0 range:NSMakeRange(0, url.length)]) { + pass = YES; + } else if (denyRegex != nil && [denyRegex firstMatchInString:url options:0 range:NSMakeRange(0, url.length)]) { + pass = NO; + } + if (!pass) { + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + if ([url rangeOfString:@"//itunes.apple.com/"].location != NSNotFound) { + if (@available(iOS 10.0, *)) { + [[UIApplication sharedApplication] openURL:nsurl options:@{} completionHandler:nil]; + } else { + [[UIApplication sharedApplication] openURL:nsurl]; + } + decisionHandler(WKNavigationActionPolicyCancel); + return; + } else if ([url hasPrefix:@"unity:"]) { + UnitySendMessage([gameObjectName UTF8String], "CallFromJS", [[url substringFromIndex:6] UTF8String]); + decisionHandler(WKNavigationActionPolicyCancel); + return; + } else if (hookRegex != nil && [hookRegex firstMatchInString:url options:0 range:NSMakeRange(0, url.length)]) { + UnitySendMessage([gameObjectName UTF8String], "CallOnHooked", [url UTF8String]); + decisionHandler(WKNavigationActionPolicyCancel); + return; + } else if (![url hasPrefix:@"about:blank"] // for loadHTML(), cf. #365 + && ![url hasPrefix:@"about:srcdoc"] // for iframe srcdoc attribute + && ![url hasPrefix:@"file:"] + && ![url hasPrefix:@"http:"] + && ![url hasPrefix:@"https:"]) { + if([[UIApplication sharedApplication] canOpenURL:nsurl]) { + if (@available(iOS 10.0, *)) { + [[UIApplication sharedApplication] openURL:nsurl options:@{} completionHandler:nil]; + } else { + [[UIApplication sharedApplication] openURL:nsurl]; + } + } + decisionHandler(WKNavigationActionPolicyCancel); + return; + } else if (navigationAction.navigationType == WKNavigationTypeLinkActivated + && (!navigationAction.targetFrame || !navigationAction.targetFrame.isMainFrame)) { + // cf. for target="_blank", cf. http://qiita.com/ShingoFukuyama/items/b3a1441025a36ab7659c + [webView load:navigationAction.request]; + decisionHandler(WKNavigationActionPolicyCancel); + return; + } else { + if (navigationAction.targetFrame != nil && navigationAction.targetFrame.isMainFrame) { + // If the custom header is not attached, give it and make a request again. + if (![self isSetupedCustomHeader:[navigationAction request]]) { + //NSLog(@"navi ... %@", navigationAction); + [wkWebView loadRequest:[self constructionCustomHeader:navigationAction.request]]; + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + } + } + UnitySendMessage([gameObjectName UTF8String], "CallOnStarted", [url UTF8String]); + // cf. https://stackoverflow.com/questions/37086605/disable-wkwebview-for-opening-links-to-redirect-to-apps-installed-on-my-iphone/76948270#76948270 + if (!googleAppRedirectionEnabled + && [url hasPrefix:@"https://www.google.com/"] + && navigationAction.navigationType == WKNavigationTypeLinkActivated) { + [webView load:navigationAction.request]; + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + decisionHandler(WKNavigationActionPolicyAllow); +} + +- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { + + if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]]) { + + NSHTTPURLResponse * response = (NSHTTPURLResponse *)navigationResponse.response; + if (response.statusCode >= 400) { + UnitySendMessage([gameObjectName UTF8String], "CallOnHttpError", [[NSString stringWithFormat:@"%d", response.statusCode] UTF8String]); + } + + } + decisionHandler(WKNavigationResponsePolicyAllow); +} + +// alert +- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler +{ + if (!alertDialogEnabled) { + completionHandler(); + return; + } + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" + message:message + preferredStyle:UIAlertControllerStyleAlert]; + [alertController addAction: [UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *action) { + completionHandler(); + }]]; + [UnityGetGLViewController() presentViewController:alertController animated:YES completion:^{}]; +} + +// confirm +- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler +{ + if (!alertDialogEnabled) { + completionHandler(NO); + return; + } + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" + message:message + preferredStyle:UIAlertControllerStyleAlert]; + [alertController addAction:[UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + completionHandler(YES); + }]]; + [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *action) { + completionHandler(NO); + }]]; + [UnityGetGLViewController() presentViewController:alertController animated:YES completion:^{}]; +} + +// prompt +- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler +{ + if (!alertDialogEnabled) { + completionHandler(nil); + return; + } + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" + message:prompt + preferredStyle:UIAlertControllerStyleAlert]; + [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) { + textField.text = defaultText; + }]; + [alertController addAction:[UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + NSString *input = ((UITextField *)alertController.textFields.firstObject).text; + completionHandler(input); + }]]; + [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *action) { + completionHandler(nil); + }]]; + [UnityGetGLViewController() presentViewController:alertController animated:YES completion:^{}]; +} + +- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler +{ + NSURLSessionAuthChallengeDisposition disposition; + NSURLCredential *credential; + if (basicAuthUserName && basicAuthPassword && [challenge previousFailureCount] == 0) { + disposition = NSURLSessionAuthChallengeUseCredential; + credential = [NSURLCredential credentialWithUser:basicAuthUserName password:basicAuthPassword persistence:NSURLCredentialPersistenceForSession]; + } else { + disposition = NSURLSessionAuthChallengePerformDefaultHandling; + credential = nil; + } + completionHandler(disposition, credential); +} + +- (BOOL)isSetupedCustomHeader:(NSURLRequest *)targetRequest +{ + // Check for additional custom header. + for (NSString *key in [customRequestHeader allKeys]) { + if (![[[targetRequest allHTTPHeaderFields] objectForKey:key] isEqualToString:[customRequestHeader objectForKey:key]]) { + return NO; + } + } + return YES; +} + +- (NSURLRequest *)constructionCustomHeader:(NSURLRequest *)originalRequest +{ + NSMutableURLRequest *convertedRequest = originalRequest.mutableCopy; + for (NSString *key in [customRequestHeader allKeys]) { + [convertedRequest setValue:customRequestHeader[key] forHTTPHeaderField:key]; + } + return (NSURLRequest *)[convertedRequest copy]; +} + +- (void)setMargins:(float)left top:(float)top right:(float)right bottom:(float)bottom relative:(BOOL)relative +{ + if (webView == nil) + return; + UIView *view = UnityGetGLViewController().view; + CGRect frame = webView.frame; + CGRect screen = view.bounds; + if (relative) { + frame.size.width = floor(screen.size.width * (1.0f - left - right)); + frame.size.height = floor(screen.size.height * (1.0f - top - bottom)); + frame.origin.x = floor(screen.size.width * left); + frame.origin.y = floor(screen.size.height * top); + } else { + CGFloat scale = 1.0f / [self getScale:view]; + frame.size.width = floor(screen.size.width - scale * (left + right)); + frame.size.height = floor(screen.size.height - scale * (top + bottom)); + frame.origin.x = floor(scale * left); + frame.origin.y = floor(scale * top); + } + webView.frame = frame; +} + +- (CGFloat)getScale:(UIView *)view +{ + if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) + return view.window.screen.nativeScale; + return view.contentScaleFactor; +} + +- (void)setVisibility:(BOOL)visibility +{ + if (webView == nil) + return; + webView.hidden = visibility ? NO : YES; +} + +- (void)setInteractionEnabled:(BOOL)enabled +{ + if (webView == nil) + return; + webView.userInteractionEnabled = enabled; +} + +- (void)setGoogleAppRedirectionEnabled:(BOOL)enabled +{ + if (webView == nil) + return; + googleAppRedirectionEnabled = enabled; +} + +- (void)setAlertDialogEnabled:(BOOL)enabled +{ + alertDialogEnabled = enabled; +} + +- (void)setScrollbarsVisibility:(BOOL)visibility +{ + if (webView == nil) + return; + [webView setScrollbarsVisibility:visibility]; +} + +- (void)setScrollBounceEnabled:(BOOL)enabled +{ + if (webView == nil) + return; + [webView setScrollBounce:enabled]; +} + +- (BOOL)setURLPattern:(const char *)allowPattern and:(const char *)denyPattern and:(const char *)hookPattern +{ + NSError *err = nil; + NSRegularExpression *allow = nil; + NSRegularExpression *deny = nil; + NSRegularExpression *hook = nil; + if (allowPattern == nil || *allowPattern == '\0') { + allow = nil; + } else { + allow + = [NSRegularExpression + regularExpressionWithPattern:[NSString stringWithUTF8String:allowPattern] + options:0 + error:&err]; + if (err != nil) { + return NO; + } + } + if (denyPattern == nil || *denyPattern == '\0') { + deny = nil; + } else { + deny + = [NSRegularExpression + regularExpressionWithPattern:[NSString stringWithUTF8String:denyPattern] + options:0 + error:&err]; + if (err != nil) { + return NO; + } + } + if (hookPattern == nil || *hookPattern == '\0') { + hook = nil; + } else { + hook + = [NSRegularExpression + regularExpressionWithPattern:[NSString stringWithUTF8String:hookPattern] + options:0 + error:&err]; + if (err != nil) { + return NO; + } + } + allowRegex = allow; + denyRegex = deny; + hookRegex = hook; + return YES; +} + +- (void)loadURL:(const char *)url +{ + if (webView == nil) + return; + NSString *urlStr = [NSString stringWithUTF8String:url]; + NSURL *nsurl = [NSURL URLWithString:urlStr]; + NSURLRequest *request = [NSURLRequest requestWithURL:nsurl]; + [webView load:request]; +} + +- (void)loadHTML:(const char *)html baseURL:(const char *)baseUrl +{ + if (webView == nil) + return; + NSString *htmlStr = [NSString stringWithUTF8String:html]; + NSString *baseStr = [NSString stringWithUTF8String:baseUrl]; + NSURL *baseNSUrl = [NSURL URLWithString:baseStr]; + [webView loadHTML:htmlStr baseURL:baseNSUrl]; +} + +- (void)evaluateJS:(const char *)js +{ + if (webView == nil) + return; + NSString *jsStr = [NSString stringWithUTF8String:js]; + [webView evaluateJavaScript:jsStr completionHandler:^(NSString *result, NSError *error) {}]; +} + +- (int)progress +{ + if (webView == nil) + return 0; + if ([webView isKindOfClass:[WKWebView class]]) { + return (int)([(WKWebView *)webView estimatedProgress] * 100); + } else { + return 0; + } +} + +- (BOOL)canGoBack +{ + if (webView == nil) + return false; + return [webView canGoBack]; +} + +- (BOOL)canGoForward +{ + if (webView == nil) + return false; + return [webView canGoForward]; +} + +- (void)goBack +{ + if (webView == nil) + return; + [webView goBack]; +} + +- (void)goForward +{ + if (webView == nil) + return; + [webView goForward]; +} + +- (void)reload +{ + if (webView == nil) + return; + [webView reload]; +} + +- (void)addCustomRequestHeader:(const char *)headerKey value:(const char *)headerValue +{ + NSString *keyString = [NSString stringWithUTF8String:headerKey]; + NSString *valueString = [NSString stringWithUTF8String:headerValue]; + + [customRequestHeader setObject:valueString forKey:keyString]; +} + +- (void)removeCustomRequestHeader:(const char *)headerKey +{ + NSString *keyString = [NSString stringWithUTF8String:headerKey]; + + if ([[customRequestHeader allKeys]containsObject:keyString]) { + [customRequestHeader removeObjectForKey:keyString]; + } +} + +- (void)clearCustomRequestHeader +{ + [customRequestHeader removeAllObjects]; +} + +- (const char *)getCustomRequestHeaderValue:(const char *)headerKey +{ + NSString *keyString = [NSString stringWithUTF8String:headerKey]; + NSString *result = [customRequestHeader objectForKey:keyString]; + if (!result) { + return NULL; + } + + const char *s = [result UTF8String]; + char *r = (char *)malloc(strlen(s) + 1); + strcpy(r, s); + return r; +} + +- (void)setBasicAuthInfo:(const char *)userName password:(const char *)password +{ + basicAuthUserName = [NSString stringWithUTF8String:userName]; + basicAuthPassword = [NSString stringWithUTF8String:password]; +} + +- (void)clearCache:(BOOL)includeDiskFiles +{ + if (webView == nil) + return; + NSMutableSet *types = [NSMutableSet setWithArray:@[WKWebsiteDataTypeMemoryCache]]; + if (includeDiskFiles) { + [types addObject:WKWebsiteDataTypeDiskCache]; + } + NSDate *date = [NSDate dateWithTimeIntervalSince1970:0]; + [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:types modifiedSince:date completionHandler:^{}]; +} + +- (void)setAllMediaPlaybackSuspended:(BOOL)suspended +{ + NSOperatingSystemVersion version = { 15, 0, 0 }; + if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:version]) { + if ([webView isKindOfClass:[WKWebView class]]) { + [(WKWebView *)webView setAllMediaPlaybackSuspended:suspended completionHandler:nil]; + } + } +} +@end + +extern "C" { + BOOL _CWebViewPlugin_IsInitialized(void *instance); + void *_CWebViewPlugin_Init(const char *gameObjectName, BOOL transparent, BOOL zoom, const char *ua, BOOL enableWKWebView, int contentMode, BOOL allowsLinkPreview, BOOL allowsBackForwardNavigationGestures, int radius); + void _CWebViewPlugin_Destroy(void *instance); + void _CWebViewPlugin_SetMargins( + void *instance, float left, float top, float right, float bottom, BOOL relative); + void _CWebViewPlugin_SetVisibility(void *instance, BOOL visibility); + void _CWebViewPlugin_SetInteractionEnabled(void *instance, BOOL enabled); + void _CWebViewPlugin_SetGoogleAppRedirectionEnabled(void *instance, BOOL enabled); + void _CWebViewPlugin_SetAlertDialogEnabled(void *instance, BOOL visibility); + void _CWebViewPlugin_SetScrollbarsVisibility(void *instance, BOOL visibility); + void _CWebViewPlugin_SetScrollBounceEnabled(void *instance, BOOL enabled); + BOOL _CWebViewPlugin_SetURLPattern(void *instance, const char *allowPattern, const char *denyPattern, const char *hookPattern); + void _CWebViewPlugin_LoadURL(void *instance, const char *url); + void _CWebViewPlugin_LoadHTML(void *instance, const char *html, const char *baseUrl); + void _CWebViewPlugin_EvaluateJS(void *instance, const char *url); + int _CWebViewPlugin_Progress(void *instance); + BOOL _CWebViewPlugin_CanGoBack(void *instance); + BOOL _CWebViewPlugin_CanGoForward(void *instance); + void _CWebViewPlugin_GoBack(void *instance); + void _CWebViewPlugin_GoForward(void *instance); + void _CWebViewPlugin_Reload(void *instance); + void _CWebViewPlugin_AddCustomHeader(void *instance, const char *headerKey, const char *headerValue); + void _CWebViewPlugin_RemoveCustomHeader(void *instance, const char *headerKey); + void _CWebViewPlugin_ClearCustomHeader(void *instance); + void _CWebViewPlugin_ClearCookie(const char *url, const char *name); + void _CWebViewPlugin_ClearCookies(); + void _CWebViewPlugin_SaveCookies(); + void _CWebViewPlugin_GetCookies(void *instance, const char *url); + const char *_CWebViewPlugin_GetCustomHeaderValue(void *instance, const char *headerKey); + void _CWebViewPlugin_SetBasicAuthInfo(void *instance, const char *userName, const char *password); + void _CWebViewPlugin_ClearCache(void *instance, BOOL includeDiskFiles); + void _CWebViewPlugin_SetSuspended(void *instance, BOOL suspended); +} + +BOOL _CWebViewPlugin_IsInitialized(void *instance) +{ + if (instance == NULL) + return NO; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + return [webViewPlugin isInitialized]; +} + +void *_CWebViewPlugin_Init(const char *gameObjectName, BOOL transparent, BOOL zoom, const char *ua, BOOL enableWKWebView, int contentMode, BOOL allowsLinkPreview, BOOL allowsBackForwardNavigationGestures, int radius) +{ + if (! (enableWKWebView && [WKWebView class])) + return nil; + WKContentMode wkContentMode = WKContentModeRecommended; + switch (contentMode) { + case 1: + wkContentMode = WKContentModeMobile; + break; + case 2: + wkContentMode = WKContentModeDesktop; + break; + default: + wkContentMode = WKContentModeRecommended; + break; + } + CWebViewPlugin *webViewPlugin = [[CWebViewPlugin alloc] initWithGameObjectName:gameObjectName transparent:transparent zoom:zoom ua:ua enableWKWebView:enableWKWebView contentMode:wkContentMode allowsLinkPreview:allowsLinkPreview allowsBackForwardNavigationGestures:allowsBackForwardNavigationGestures radius:radius]; + [_instances addObject:webViewPlugin]; + return (__bridge_retained void *)webViewPlugin; +} + +void _CWebViewPlugin_Destroy(void *instance) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge_transfer CWebViewPlugin *)instance; + [_instances removeObject:webViewPlugin]; + [webViewPlugin dispose]; + webViewPlugin = nil; +} + +void _CWebViewPlugin_SetMargins( + void *instance, float left, float top, float right, float bottom, BOOL relative) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setMargins:left top:top right:right bottom:bottom relative:relative]; +} + +void _CWebViewPlugin_SetVisibility(void *instance, BOOL visibility) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setVisibility:visibility]; +} + +void _CWebViewPlugin_SetInteractionEnabled(void *instance, BOOL enabled) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setInteractionEnabled:enabled]; +} + +void _CWebViewPlugin_SetGoogleAppRedirectionEnabled(void *instance, BOOL enabled) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setGoogleAppRedirectionEnabled:enabled]; +} + +void _CWebViewPlugin_SetAlertDialogEnabled(void *instance, BOOL enabled) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setAlertDialogEnabled:enabled]; +} + +void _CWebViewPlugin_SetScrollbarsVisibility(void *instance, BOOL visibility) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setScrollbarsVisibility:visibility]; +} + +void _CWebViewPlugin_SetScrollBounceEnabled(void *instance, BOOL enabled) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setScrollBounceEnabled:enabled]; +} + +BOOL _CWebViewPlugin_SetURLPattern(void *instance, const char *allowPattern, const char *denyPattern, const char *hookPattern) +{ + if (instance == NULL) + return NO; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + return [webViewPlugin setURLPattern:allowPattern and:denyPattern and:hookPattern]; +} + +void _CWebViewPlugin_LoadURL(void *instance, const char *url) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin loadURL:url]; +} + +void _CWebViewPlugin_LoadHTML(void *instance, const char *html, const char *baseUrl) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin loadHTML:html baseURL:baseUrl]; +} + +void _CWebViewPlugin_EvaluateJS(void *instance, const char *js) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin evaluateJS:js]; +} + +int _CWebViewPlugin_Progress(void *instance) +{ + if (instance == NULL) + return 0; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + return [webViewPlugin progress]; +} + +BOOL _CWebViewPlugin_CanGoBack(void *instance) +{ + if (instance == NULL) + return false; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + return [webViewPlugin canGoBack]; +} + +BOOL _CWebViewPlugin_CanGoForward(void *instance) +{ + if (instance == NULL) + return false; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + return [webViewPlugin canGoForward]; +} + +void _CWebViewPlugin_GoBack(void *instance) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin goBack]; +} + +void _CWebViewPlugin_GoForward(void *instance) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin goForward]; +} + +void _CWebViewPlugin_Reload(void *instance) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin reload]; +} + +void _CWebViewPlugin_AddCustomHeader(void *instance, const char *headerKey, const char *headerValue) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin addCustomRequestHeader:headerKey value:headerValue]; +} + +void _CWebViewPlugin_RemoveCustomHeader(void *instance, const char *headerKey) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin removeCustomRequestHeader:headerKey]; +} + +void _CWebViewPlugin_ClearCustomHeader(void *instance) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin clearCustomRequestHeader]; +} + +void _CWebViewPlugin_ClearCookie(const char *url, const char *name) +{ + [CWebViewPlugin clearCookie:name of:url]; +} + +void _CWebViewPlugin_ClearCookies() +{ + [CWebViewPlugin clearCookies]; +} + +void _CWebViewPlugin_SaveCookies() +{ + [CWebViewPlugin saveCookies]; +} + +void _CWebViewPlugin_GetCookies(void *instance, const char *url) +{ + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin getCookies:url]; +} + +const char *_CWebViewPlugin_GetCustomHeaderValue(void *instance, const char *headerKey) +{ + if (instance == NULL) + return NULL; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + return [webViewPlugin getCustomRequestHeaderValue:headerKey]; +} + +void _CWebViewPlugin_SetBasicAuthInfo(void *instance, const char *userName, const char *password) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setBasicAuthInfo:userName password:password]; +} + +void _CWebViewPlugin_ClearCache(void *instance, BOOL includeDiskFiles) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin clearCache:includeDiskFiles]; +} + +void _CWebViewPlugin_SetSuspended(void *instance, BOOL suspended) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setAllMediaPlaybackSuspended:suspended]; +} +#endif // !(__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) diff --git a/Packages/com.bywaystudios.webview/Plugins/iOS/WebView.mm.meta b/Packages/com.bywaystudios.webview/Plugins/iOS/WebView.mm.meta new file mode 100644 index 0000000..fb91d13 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/iOS/WebView.mm.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 2cabb4f60971742a28f3bd04e65de504 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/iOS/WebViewWithUIWebView.mm b/Packages/com.bywaystudios.webview/Plugins/iOS/WebViewWithUIWebView.mm new file mode 100644 index 0000000..7909a36 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/iOS/WebViewWithUIWebView.mm @@ -0,0 +1,1385 @@ +/* + * Copyright (C) 2011 Keijiro Takahashi + * Copyright (C) 2012 GREE, Inc. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0 + +#import +#import + +// NOTE: we need extern without "C" before unity 4.5 +//extern UIViewController *UnityGetGLViewController(); +extern "C" UIViewController *UnityGetGLViewController(); +extern "C" void UnitySendMessage(const char *, const char *, const char *); + +// cf. https://stackoverflow.com/questions/26383031/wkwebview-causes-my-view-controller-to-leak/33365424#33365424 +@interface WeakScriptMessageDelegate : NSObject + +@property (nonatomic, weak) id scriptDelegate; + +- (instancetype)initWithDelegate:(id)scriptDelegate; + +@end + +@implementation WeakScriptMessageDelegate + +- (instancetype)initWithDelegate:(id)scriptDelegate +{ + self = [super init]; + if (self) { + _scriptDelegate = scriptDelegate; + } + return self; +} + +- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message +{ + [self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message]; +} + +@end + +@protocol WebViewProtocol +@property (nonatomic, getter=isOpaque) BOOL opaque; +@property (nullable, nonatomic, copy) UIColor *backgroundColor UI_APPEARANCE_SELECTOR; +@property (nonatomic, getter=isHidden) BOOL hidden; +@property (nonatomic, getter=isUserInteractionEnabled) BOOL userInteractionEnabled; +@property (nonatomic) CGRect frame; +@property (nonatomic, readonly, strong) UIScrollView *scrollView; +@property (nullable, nonatomic, assign) id delegate; +@property (nullable, nonatomic, weak) id navigationDelegate; +@property (nullable, nonatomic, weak) id UIDelegate; +@property (nullable, nonatomic, readonly, copy) NSURL *URL; +- (void)load:(NSURLRequest *)request; +- (void)loadHTML:(NSString *)html baseURL:(NSURL *)baseUrl; +- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ __nullable)(__nullable id, NSError * __nullable error))completionHandler; +@property (nonatomic, readonly) BOOL canGoBack; +@property (nonatomic, readonly) BOOL canGoForward; +- (void)goBack; +- (void)goForward; +- (void)reload; +- (void)stopLoading; +- (void)setScrollbarsVisibility:(BOOL)visibility; +- (void)setScrollBounce:(BOOL)enable; +@end + +@interface WKWebView(WebViewProtocolConformed) +@end + +@implementation WKWebView(WebViewProtocolConformed) + +@dynamic delegate; + +- (void)load:(NSURLRequest *)request +{ + WKWebView *webView = (WKWebView *)self; + NSURL *url = [request URL]; + if ([url.absoluteString hasPrefix:@"file:"]) { + NSURL *top = [NSURL URLWithString:[[url absoluteString] stringByDeletingLastPathComponent]]; + [webView loadFileURL:url allowingReadAccessToURL:top]; + } else { + [webView loadRequest:request]; + } +} + +- (NSURLRequest *)constructionCustomHeader:(NSURLRequest *)originalRequest with:(NSDictionary *)headerDictionary +{ + NSMutableURLRequest *convertedRequest = originalRequest.mutableCopy; + for (NSString *key in [headerDictionary allKeys]) { + [convertedRequest setValue:headerDictionary[key] forHTTPHeaderField:key]; + } + return (NSURLRequest *)[convertedRequest copy]; +} + +- (void)loadHTML:(NSString *)html baseURL:(NSURL *)baseUrl +{ + WKWebView *webView = (WKWebView *)self; + [webView loadHTMLString:html baseURL:baseUrl]; +} + +- (void)setScrollbarsVisibility:(BOOL)visibility +{ + WKWebView *webView = (WKWebView *)self; + webView.scrollView.showsHorizontalScrollIndicator = visibility; + webView.scrollView.showsVerticalScrollIndicator = visibility; +} + +- (void)setScrollBounce:(BOOL)enable +{ + WKWebView *webView = (WKWebView *)self; + webView.scrollView.bounces = enable; +} + +@end + +@interface UIWebView(WebViewProtocolConformed) +@end + +@implementation UIWebView(WebViewProtocolConformed) + +@dynamic navigationDelegate; +@dynamic UIDelegate; + +- (NSURL *)URL +{ + return [NSURL URLWithString:[self stringByEvaluatingJavaScriptFromString:@"document.URL"]]; +} + +- (void)load:(NSURLRequest *)request +{ + UIWebView *webView = (UIWebView *)self; + [webView loadRequest:request]; +} + +- (void)loadHTML:(NSString *)html baseURL:(NSURL *)baseUrl +{ + UIWebView *webView = (UIWebView *)self; + [webView loadHTMLString:html baseURL:baseUrl]; +} + +- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ __nullable)(__nullable id, NSError * __nullable error))completionHandler +{ + NSString *result = [self stringByEvaluatingJavaScriptFromString:javaScriptString]; + if (completionHandler) { + completionHandler(result, nil); + } +} + +- (void)setScrollbarsVisibility:(BOOL)visibility +{ + UIWebView *webView = (UIWebView *)self; + webView.scrollView.showsHorizontalScrollIndicator = visibility; + webView.scrollView.showsVerticalScrollIndicator = visibility; +} + +- (void)setScrollBounce:(BOOL)enable +{ + UIWebView *webView = (UIWebView *)self; + webView.scrollView.bounces = enable; +} + +@end + +@interface CWebViewPlugin : NSObject +{ + UIView *webView; + NSString *gameObjectName; + NSMutableDictionary *customRequestHeader; + BOOL googleAppRedirectionEnabled; + BOOL alertDialogEnabled; + NSRegularExpression *allowRegex; + NSRegularExpression *denyRegex; + NSRegularExpression *hookRegex; + NSString *basicAuthUserName; + NSString *basicAuthPassword; +} +@end + +@implementation CWebViewPlugin + +static WKProcessPool *_sharedProcessPool; +static NSMutableArray *_instances = [[NSMutableArray alloc] init]; + +- (BOOL)isInitialized +{ + return webView != nil; +} + +- (id)initWithGameObjectName:(const char *)gameObjectName_ transparent:(BOOL)transparent zoom:(BOOL)zoom ua:(const char *)ua enableWKWebView:(BOOL)enableWKWebView contentMode:(WKContentMode)contentMode allowsLinkPreview:(BOOL)allowsLinkPreview allowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures radius:(int)radius +{ + self = [super init]; + + gameObjectName = [NSString stringWithUTF8String:gameObjectName_]; + customRequestHeader = [[NSMutableDictionary alloc] init]; + googleAppRedirectionEnabled = false; + alertDialogEnabled = true; + allowRegex = nil; + denyRegex = nil; + hookRegex = nil; + basicAuthUserName = nil; + basicAuthPassword = nil; + UIView *view = UnityGetGLViewController().view; + if (enableWKWebView && [WKWebView class]) { + if (_sharedProcessPool == NULL) { + _sharedProcessPool = [[WKProcessPool alloc] init]; + } + WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; + WKUserContentController *controller = [[WKUserContentController alloc] init]; + [controller addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:@"unityControl"]; + [controller addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:@"saveDataURL"]; + { + NSString *str = @"\ +window.Unity = { \ + call: function(msg) { \ + window.webkit.messageHandlers.unityControl.postMessage(msg); \ + }, \ + saveDataURL: function(fileName, dataURL) { \ + window.webkit.messageHandlers.saveDataURL.postMessage(fileName + '\t' + dataURL); \ + } \ +}; \ +"; + WKUserScript *script = [[WKUserScript alloc] initWithSource:str injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]; + [controller addUserScript:script]; + } + if (!zoom) { + NSString *str = @"\ +(function() { \ + var meta = document.querySelector('meta[name=viewport]'); \ + if (meta == null) { \ + meta = document.createElement('meta'); \ + meta.name = 'viewport'; \ + } \ + meta.content += ((meta.content.length > 0) ? ',' : '') + 'user-scalable=no'; \ + var head = document.getElementsByTagName('head')[0]; \ + head.appendChild(meta); \ +})(); \ +"; + WKUserScript *script = [[WKUserScript alloc] initWithSource:str injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; + [controller addUserScript:script]; + } + configuration.userContentController = controller; + configuration.allowsInlineMediaPlayback = true; + if (@available(iOS 10.0, *)) { + configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone; + } else { + if (@available(iOS 9.0, *)) { + configuration.requiresUserActionForMediaPlayback = NO; + } else { + configuration.mediaPlaybackRequiresUserAction = NO; + } + } + configuration.websiteDataStore = [WKWebsiteDataStore defaultDataStore]; + configuration.processPool = _sharedProcessPool; + if (@available(iOS 13.0, *)) { + configuration.defaultWebpagePreferences.preferredContentMode = contentMode; + } +#if UNITYWEBVIEW_IOS_ALLOW_FILE_URLS + // cf. https://stackoverflow.com/questions/35554814/wkwebview-xmlhttprequest-with-file-url/44365081#44365081 + try { + [configuration.preferences setValue:@TRUE forKey:@"allowFileAccessFromFileURLs"]; + } + catch (NSException *ex) { + } + try { + [configuration setValue:@TRUE forKey:@"allowUniversalAccessFromFileURLs"]; + } + catch (NSException *ex) { + } +#endif + WKWebView *wkwebView = [[WKWebView alloc] initWithFrame:view.frame configuration:configuration]; +#if UNITYWEBVIEW_DEVELOPMENT + NSOperatingSystemVersion version = { 16, 4, 0 }; + if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:version]) { + wkwebView.inspectable = true; + } +#endif + wkwebView.allowsLinkPreview = allowsLinkPreview; + wkwebView.allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures; + webView = wkwebView; + webView.UIDelegate = self; + webView.navigationDelegate = self; + if (ua != NULL && strcmp(ua, "") != 0) { + ((WKWebView *)webView).customUserAgent = [[NSString alloc] initWithUTF8String:ua]; + } + // cf. https://rick38yip.medium.com/wkwebview-weird-spacing-issue-in-ios-13-54a4fc686f72 + // cf. https://stackoverflow.com/questions/44390971/automaticallyadjustsscrollviewinsets-was-deprecated-in-ios-11-0 + if (@available(iOS 11.0, *)) { + ((WKWebView *)webView).scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } else { + //UnityGetGLViewController().automaticallyAdjustsScrollViewInsets = false; + } + } else { + if (ua != NULL && strcmp(ua, "") != 0) { + [[NSUserDefaults standardUserDefaults] + registerDefaults:@{ @"UserAgent": [[NSString alloc] initWithUTF8String:ua] }]; + } + UIWebView *uiwebview = [[UIWebView alloc] initWithFrame:view.frame]; + uiwebview.allowsInlineMediaPlayback = YES; + uiwebview.mediaPlaybackRequiresUserAction = NO; + webView = uiwebview; + webView.delegate = self; + } + if (transparent) { + webView.opaque = NO; + webView.backgroundColor = [UIColor clearColor]; + } + if (radius > 0) { + webView.layer.cornerRadius = radius; + webView.layer.masksToBounds = YES; + } + webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + webView.hidden = YES; + + [webView addObserver:self forKeyPath: @"loading" options: NSKeyValueObservingOptionNew context:nil]; + + [view addSubview:webView]; + + return self; +} + +- (void)dispose +{ + if (webView != nil) { + UIView *webView0 = webView; + webView = nil; + if ([webView0 isKindOfClass:[WKWebView class]]) { + webView0.UIDelegate = nil; + webView0.navigationDelegate = nil; + [((WKWebView *)webView0).configuration.userContentController removeScriptMessageHandlerForName:@"saveDataURL"]; + [((WKWebView *)webView0).configuration.userContentController removeScriptMessageHandlerForName:@"unityControl"]; + } else { + webView0.delegate = nil; + } + [webView0 stopLoading]; + [webView0 removeFromSuperview]; + [webView0 removeObserver:self forKeyPath:@"loading"]; + } + basicAuthPassword = nil; + basicAuthUserName = nil; + hookRegex = nil; + denyRegex = nil; + allowRegex = nil; + customRequestHeader = nil; + gameObjectName = nil; +} + ++ (void)resetSharedProcessPool +{ + // cf. https://stackoverflow.com/questions/33156567/getting-all-cookies-from-wkwebview/49744695#49744695 + _sharedProcessPool = [[WKProcessPool alloc] init]; + [_instances enumerateObjectsUsingBlock:^(CWebViewPlugin *obj, NSUInteger idx, BOOL *stop) { + if ([obj->webView isKindOfClass:[WKWebView class]]) { + WKWebView *webView = (WKWebView *)obj->webView; + webView.configuration.processPool = _sharedProcessPool; + } + }]; +} + ++ (void)clearCookie:(const char *)name of:(const char *)url +{ + NSURL *nsurl = [NSURL URLWithString:[[NSString alloc] initWithUTF8String:url]]; + if (nsurl == nil) { + return; + } + NSString *nsname = [NSString stringWithUTF8String:name]; + if (@available(iOS 9.0, *)) { + WKHTTPCookieStore *cookieStore = WKWebsiteDataStore.defaultDataStore.httpCookieStore; + [cookieStore + getAllCookies:^(NSArray *array) { + [array + enumerateObjectsUsingBlock:^(NSHTTPCookie *cookie, NSUInteger idx, BOOL *stop) { + if ([cookie.name isEqualToString:nsname] + && [cookie.domain isEqualToString:nsurl.host] + && [cookie.path isEqualToString:nsurl.path]) { + [cookieStore deleteCookie:cookie completionHandler:^{}]; + } + }]; + }]; + } else { + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + if (cookieStorage == nil) { + // cf. https://stackoverflow.com/questions/33876295/nshttpcookiestorage-sharedhttpcookiestorage-comes-up-empty-in-10-11 + cookieStorage = [NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:@"Cookies"]; + } + [[cookieStorage cookies] enumerateObjectsUsingBlock:^(NSHTTPCookie *cookie, NSUInteger idx, BOOL *stop) { + if ([cookie.name isEqualToString:nsname] + && [cookie.domain isEqualToString:nsurl.host] + && [cookie.path isEqualToString:nsurl.path]) { + [cookieStorage deleteCookie:cookie]; + } + }]; + } +} + ++ (void)clearCookies +{ + [CWebViewPlugin resetSharedProcessPool]; + + // cf. https://dev.classmethod.jp/smartphone/remove-webview-cookies/ + NSString *libraryPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject; + NSString *cookiesPath = [libraryPath stringByAppendingPathComponent:@"Cookies"]; + NSString *webKitPath = [libraryPath stringByAppendingPathComponent:@"WebKit"]; + [[NSFileManager defaultManager] removeItemAtPath:cookiesPath error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:webKitPath error:nil]; + + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + if (cookieStorage == nil) { + // cf. https://stackoverflow.com/questions/33876295/nshttpcookiestorage-sharedhttpcookiestorage-comes-up-empty-in-10-11 + cookieStorage = [NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:@"Cookies"]; + } + [[cookieStorage cookies] enumerateObjectsUsingBlock:^(NSHTTPCookie *cookie, NSUInteger idx, BOOL *stop) { + [cookieStorage deleteCookie:cookie]; + }]; + + NSOperatingSystemVersion version = { 9, 0, 0 }; + if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:version]) { + // cf. https://stackoverflow.com/questions/46465070/how-to-delete-cookies-from-wkhttpcookiestore/47928399#47928399 + NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; + NSDate *date = [NSDate dateWithTimeIntervalSince1970:0]; + [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes + modifiedSince:date + completionHandler:^{}]; + } +} + ++ saveCookies +{ + [CWebViewPlugin resetSharedProcessPool]; +} + +- (void)getCookies:(const char *)url +{ + NSOperatingSystemVersion version = { 9, 0, 0 }; + if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:version]) { + NSURL *nsurl = [NSURL URLWithString:[[NSString alloc] initWithUTF8String:url]]; + WKHTTPCookieStore *cookieStore = WKWebsiteDataStore.defaultDataStore.httpCookieStore; + [cookieStore + getAllCookies:^(NSArray *array) { + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; + [formatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss zzz"]; + NSMutableString *result = [NSMutableString string]; + [array enumerateObjectsUsingBlock:^(NSHTTPCookie *cookie, NSUInteger idx, BOOL *stop) { + if ([cookie.domain isEqualToString:nsurl.host]) { + [result appendString:[NSString stringWithFormat:@"%@=%@", cookie.name, cookie.value]]; + if ([cookie.domain length] > 0) { + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Domain=%@", cookie.domain]]; + } + if ([cookie.path length] > 0) { + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Path=%@", cookie.path]]; + } + if (cookie.expiresDate != nil) { + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Expires=%@", [formatter stringFromDate:cookie.expiresDate]]]; + } + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Version=%zd", cookie.version]]; + [result appendString:[NSString stringWithFormat:@"\n"]]; + } + }]; + UnitySendMessage([gameObjectName UTF8String], "CallOnCookies", [result UTF8String]); + }]; + } else { + [CWebViewPlugin resetSharedProcessPool]; + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; + [formatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss zzz"]; + NSMutableString *result = [NSMutableString string]; + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + if (cookieStorage == nil) { + // cf. https://stackoverflow.com/questions/33876295/nshttpcookiestorage-sharedhttpcookiestorage-comes-up-empty-in-10-11 + cookieStorage = [NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:@"Cookies"]; + } + [[cookieStorage cookiesForURL:[NSURL URLWithString:[[NSString alloc] initWithUTF8String:url]]] + enumerateObjectsUsingBlock:^(NSHTTPCookie *cookie, NSUInteger idx, BOOL *stop) { + [result appendString:[NSString stringWithFormat:@"%@=%@", cookie.name, cookie.value]]; + if ([cookie.domain length] > 0) { + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Domain=%@", cookie.domain]]; + } + if ([cookie.path length] > 0) { + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Path=%@", cookie.path]]; + } + if (cookie.expiresDate != nil) { + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Expires=%@", [formatter stringFromDate:cookie.expiresDate]]]; + } + [result appendString:[NSString stringWithFormat:@"; "]]; + [result appendString:[NSString stringWithFormat:@"Version=%zd", cookie.version]]; + [result appendString:[NSString stringWithFormat:@"\n"]]; + }]; + UnitySendMessage([gameObjectName UTF8String], "CallOnCookies", [result UTF8String]); + } +} + +- (void)userContentController:(WKUserContentController *)userContentController + didReceiveScriptMessage:(WKScriptMessage *)message { + + // Log out the message received + //NSLog(@"Received event %@", message.body); + if ([message.name isEqualToString:@"unityControl"]) { + UnitySendMessage([gameObjectName UTF8String], "CallFromJS", [[NSString stringWithFormat:@"%@", message.body] UTF8String]); + } else if ([message.name isEqualToString:@"saveDataURL"]) { + NSRange range = [message.body rangeOfString:@"\t"]; + if (range.location == NSNotFound) { + return; + } + NSString *fileName = [[message.body substringWithRange:NSMakeRange(0, range.location)] lastPathComponent]; + NSString *dataURL = [message.body substringFromIndex:(range.location + 1)]; + range = [dataURL rangeOfString:@"data:"]; + if (range.location != 0) { + return; + } + NSString *tmp = [dataURL substringFromIndex:[@"data:" length]]; + range = [tmp rangeOfString:@";"]; + if (range.location == NSNotFound) { + return; + } + NSString *base64data = [tmp substringFromIndex:(range.location + 1 + [@"base64," length])]; + NSString *type = [tmp substringWithRange:NSMakeRange(0, range.location)]; + NSData *data = [[NSData alloc] initWithBase64EncodedString:base64data options:0]; + NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; + path = [path stringByAppendingString:@"/Downloads"]; + BOOL isDir; + NSError *err = nil; + if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) { + if (!isDir) { + return; + } + } else { + [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&err]; + if (err != nil) { + return; + } + } + NSString *prefix = [path stringByAppendingString:@"/"]; + path = [prefix stringByAppendingString:fileName]; + int count = 0; + while ([[NSFileManager defaultManager] fileExistsAtPath:path]) { + count++; + NSString *name = [fileName stringByDeletingPathExtension]; + NSString *ext = [fileName pathExtension]; + if (ext.length == 0) { + path = [NSString stringWithFormat:@"%@%@ (%d)", prefix, name, count]; + } else { + path = [NSString stringWithFormat:@"%@%@ (%d).%@", prefix, name, count, ext]; + } + } + [data writeToFile:path atomically:YES]; + } + + /* + // Then pull something from the device using the message body + NSString *version = [[UIDevice currentDevice] valueForKey:message.body]; + + // Execute some JavaScript using the result? + NSString *exec_template = @"set_headline(\"received: %@\");"; + NSString *exec = [NSString stringWithFormat:exec_template, version]; + [webView evaluateJavaScript:exec completionHandler:nil]; + */ +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + if (webView == nil) + return; + + if ([keyPath isEqualToString:@"loading"] && [[change objectForKey:NSKeyValueChangeNewKey] intValue] == 0 + && [webView URL] != nil) { + UnitySendMessage( + [gameObjectName UTF8String], + "CallOnLoaded", + [[[webView URL] absoluteString] UTF8String]); + + } +} + +- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView +{ + UnitySendMessage([gameObjectName UTF8String], "CallOnError", "webViewWebContentProcessDidTerminate"); +} + +- (void)webView:(UIWebView *)uiWebView didFailLoadWithError:(NSError *)error +{ + UnitySendMessage([gameObjectName UTF8String], "CallOnError", [[error description] UTF8String]); +} + +- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error +{ + UnitySendMessage([gameObjectName UTF8String], "CallOnError", [[error description] UTF8String]); +} + +- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error +{ + UnitySendMessage([gameObjectName UTF8String], "CallOnError", [[error description] UTF8String]); +} + +- (void)webViewDidFinishLoad:(UIWebView *)uiWebView { + if (webView == nil) + return; + // cf. http://stackoverflow.com/questions/10996028/uiwebview-when-did-a-page-really-finish-loading/15916853#15916853 + if ([[uiWebView stringByEvaluatingJavaScriptFromString:@"document.readyState"] isEqualToString:@"complete"] + && [webView URL] != nil) { + UnitySendMessage( + [gameObjectName UTF8String], + "CallOnLoaded", + [[[webView URL] absoluteString] UTF8String]); + } +} + +- (BOOL)webView:(UIWebView *)uiWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType +{ + if (webView == nil) + return YES; + + NSURL *nsurl = [request URL]; + NSString *url = [nsurl absoluteString]; + BOOL pass = YES; + if (allowRegex != nil && [allowRegex firstMatchInString:url options:0 range:NSMakeRange(0, url.length)]) { + pass = YES; + } else if (denyRegex != nil && [denyRegex firstMatchInString:url options:0 range:NSMakeRange(0, url.length)]) { + pass = NO; + } + if (!pass) { + return NO; + } + if ([url rangeOfString:@"//itunes.apple.com/"].location != NSNotFound) { + if (@available(iOS 10.0, *)) { + [[UIApplication sharedApplication] openURL:nsurl options:@{} completionHandler:nil]; + } else { + [[UIApplication sharedApplication] openURL:nsurl]; + } + return NO; + } else if ([url hasPrefix:@"unity:"]) { + UnitySendMessage([gameObjectName UTF8String], "CallFromJS", [[url substringFromIndex:6] UTF8String]); + return NO; + } else if (hookRegex != nil && [hookRegex firstMatchInString:url options:0 range:NSMakeRange(0, url.length)]) { + UnitySendMessage([gameObjectName UTF8String], "CallOnHooked", [url UTF8String]); + return NO; + } else { + if (![self isSetupedCustomHeader:request]) { + [uiWebView loadRequest:[self constructionCustomHeader:request]]; + return NO; + } + UnitySendMessage([gameObjectName UTF8String], "CallOnStarted", [url UTF8String]); + return YES; + } +} + +- (WKWebView *)webView:(WKWebView *)wkWebView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures +{ + // cf. for target="_blank", cf. http://qiita.com/ShingoFukuyama/items/b3a1441025a36ab7659c + if (!navigationAction.targetFrame.isMainFrame) { + [wkWebView loadRequest:navigationAction.request]; + } + return nil; +} + +- (void)webView:(WKWebView *)wkWebView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler +{ + if (webView == nil) { + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + NSURL *nsurl = [navigationAction.request URL]; + NSString *url = [nsurl absoluteString]; + BOOL pass = YES; + if (allowRegex != nil && [allowRegex firstMatchInString:url options:0 range:NSMakeRange(0, url.length)]) { + pass = YES; + } else if (denyRegex != nil && [denyRegex firstMatchInString:url options:0 range:NSMakeRange(0, url.length)]) { + pass = NO; + } + if (!pass) { + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + if ([url rangeOfString:@"//itunes.apple.com/"].location != NSNotFound) { + if (@available(iOS 10.0, *)) { + [[UIApplication sharedApplication] openURL:nsurl options:@{} completionHandler:nil]; + } else { + [[UIApplication sharedApplication] openURL:nsurl]; + } + decisionHandler(WKNavigationActionPolicyCancel); + return; + } else if ([url hasPrefix:@"unity:"]) { + UnitySendMessage([gameObjectName UTF8String], "CallFromJS", [[url substringFromIndex:6] UTF8String]); + decisionHandler(WKNavigationActionPolicyCancel); + return; + } else if (hookRegex != nil && [hookRegex firstMatchInString:url options:0 range:NSMakeRange(0, url.length)]) { + UnitySendMessage([gameObjectName UTF8String], "CallOnHooked", [url UTF8String]); + decisionHandler(WKNavigationActionPolicyCancel); + return; + } else if (![url hasPrefix:@"about:blank"] // for loadHTML(), cf. #365 + && ![url hasPrefix:@"about:srcdoc"] // for iframe srcdoc attribute + && ![url hasPrefix:@"file:"] + && ![url hasPrefix:@"http:"] + && ![url hasPrefix:@"https:"]) { + if([[UIApplication sharedApplication] canOpenURL:nsurl]) { + if (@available(iOS 10.0, *)) { + [[UIApplication sharedApplication] openURL:nsurl options:@{} completionHandler:nil]; + } else { + [[UIApplication sharedApplication] openURL:nsurl]; + } + } + decisionHandler(WKNavigationActionPolicyCancel); + return; + } else if (navigationAction.navigationType == WKNavigationTypeLinkActivated + && (!navigationAction.targetFrame || !navigationAction.targetFrame.isMainFrame)) { + // cf. for target="_blank", cf. http://qiita.com/ShingoFukuyama/items/b3a1441025a36ab7659c + [webView load:navigationAction.request]; + decisionHandler(WKNavigationActionPolicyCancel); + return; + } else { + if (navigationAction.targetFrame != nil && navigationAction.targetFrame.isMainFrame) { + // If the custom header is not attached, give it and make a request again. + if (![self isSetupedCustomHeader:[navigationAction request]]) { + //NSLog(@"navi ... %@", navigationAction); + [wkWebView loadRequest:[self constructionCustomHeader:navigationAction.request]]; + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + } + } + UnitySendMessage([gameObjectName UTF8String], "CallOnStarted", [url UTF8String]); + // cf. https://stackoverflow.com/questions/37086605/disable-wkwebview-for-opening-links-to-redirect-to-apps-installed-on-my-iphone/76948270#76948270 + if (!googleAppRedirectionEnabled + && [url hasPrefix:@"https://www.google.com/"] + && navigationAction.navigationType == WKNavigationTypeLinkActivated) { + [webView load:navigationAction.request]; + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + decisionHandler(WKNavigationActionPolicyAllow); +} + +- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { + + if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]]) { + + NSHTTPURLResponse * response = (NSHTTPURLResponse *)navigationResponse.response; + if (response.statusCode >= 400) { + UnitySendMessage([gameObjectName UTF8String], "CallOnHttpError", [[NSString stringWithFormat:@"%d", response.statusCode] UTF8String]); + } + + } + decisionHandler(WKNavigationResponsePolicyAllow); +} + +// alert +- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler +{ + if (!alertDialogEnabled) { + completionHandler(); + return; + } + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" + message:message + preferredStyle:UIAlertControllerStyleAlert]; + [alertController addAction: [UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *action) { + completionHandler(); + }]]; + [UnityGetGLViewController() presentViewController:alertController animated:YES completion:^{}]; +} + +// confirm +- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler +{ + if (!alertDialogEnabled) { + completionHandler(NO); + return; + } + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" + message:message + preferredStyle:UIAlertControllerStyleAlert]; + [alertController addAction:[UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + completionHandler(YES); + }]]; + [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *action) { + completionHandler(NO); + }]]; + [UnityGetGLViewController() presentViewController:alertController animated:YES completion:^{}]; +} + +// prompt +- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler +{ + if (!alertDialogEnabled) { + completionHandler(nil); + return; + } + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" + message:prompt + preferredStyle:UIAlertControllerStyleAlert]; + [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) { + textField.text = defaultText; + }]; + [alertController addAction:[UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + NSString *input = ((UITextField *)alertController.textFields.firstObject).text; + completionHandler(input); + }]]; + [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *action) { + completionHandler(nil); + }]]; + [UnityGetGLViewController() presentViewController:alertController animated:YES completion:^{}]; +} + +- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler +{ + NSURLSessionAuthChallengeDisposition disposition; + NSURLCredential *credential; + if (basicAuthUserName && basicAuthPassword && [challenge previousFailureCount] == 0) { + disposition = NSURLSessionAuthChallengeUseCredential; + credential = [NSURLCredential credentialWithUser:basicAuthUserName password:basicAuthPassword persistence:NSURLCredentialPersistenceForSession]; + } else { + disposition = NSURLSessionAuthChallengePerformDefaultHandling; + credential = nil; + } + completionHandler(disposition, credential); +} + +- (BOOL)isSetupedCustomHeader:(NSURLRequest *)targetRequest +{ + // Check for additional custom header. + for (NSString *key in [customRequestHeader allKeys]) { + if (![[[targetRequest allHTTPHeaderFields] objectForKey:key] isEqualToString:[customRequestHeader objectForKey:key]]) { + return NO; + } + } + return YES; +} + +- (NSURLRequest *)constructionCustomHeader:(NSURLRequest *)originalRequest +{ + NSMutableURLRequest *convertedRequest = originalRequest.mutableCopy; + for (NSString *key in [customRequestHeader allKeys]) { + [convertedRequest setValue:customRequestHeader[key] forHTTPHeaderField:key]; + } + return (NSURLRequest *)[convertedRequest copy]; +} + +- (void)setMargins:(float)left top:(float)top right:(float)right bottom:(float)bottom relative:(BOOL)relative +{ + if (webView == nil) + return; + UIView *view = UnityGetGLViewController().view; + CGRect frame = webView.frame; + CGRect screen = view.bounds; + if (relative) { + frame.size.width = floor(screen.size.width * (1.0f - left - right)); + frame.size.height = floor(screen.size.height * (1.0f - top - bottom)); + frame.origin.x = floor(screen.size.width * left); + frame.origin.y = floor(screen.size.height * top); + } else { + CGFloat scale = 1.0f / [self getScale:view]; + frame.size.width = floor(screen.size.width - scale * (left + right)); + frame.size.height = floor(screen.size.height - scale * (top + bottom)); + frame.origin.x = floor(scale * left); + frame.origin.y = floor(scale * top); + } + webView.frame = frame; +} + +- (CGFloat)getScale:(UIView *)view +{ + if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) + return view.window.screen.nativeScale; + return view.contentScaleFactor; +} + +- (void)setVisibility:(BOOL)visibility +{ + if (webView == nil) + return; + webView.hidden = visibility ? NO : YES; +} + +- (void)setInteractionEnabled:(BOOL)enabled +{ + if (webView == nil) + return; + webView.userInteractionEnabled = enabled; +} + +- (void)setGoogleAppRedirectionEnabled:(BOOL)enabled +{ + if (webView == nil) + return; + googleAppRedirectionEnabled = enabled; +} + +- (void)setAlertDialogEnabled:(BOOL)enabled +{ + alertDialogEnabled = enabled; +} + +- (void)setScrollbarsVisibility:(BOOL)visibility +{ + if (webView == nil) + return; + [webView setScrollbarsVisibility:visibility]; +} + +- (void)setScrollBounceEnabled:(BOOL)enabled +{ + if (webView == nil) + return; + [webView setScrollBounce:enabled]; +} + +- (BOOL)setURLPattern:(const char *)allowPattern and:(const char *)denyPattern and:(const char *)hookPattern +{ + NSError *err = nil; + NSRegularExpression *allow = nil; + NSRegularExpression *deny = nil; + NSRegularExpression *hook = nil; + if (allowPattern == nil || *allowPattern == '\0') { + allow = nil; + } else { + allow + = [NSRegularExpression + regularExpressionWithPattern:[NSString stringWithUTF8String:allowPattern] + options:0 + error:&err]; + if (err != nil) { + return NO; + } + } + if (denyPattern == nil || *denyPattern == '\0') { + deny = nil; + } else { + deny + = [NSRegularExpression + regularExpressionWithPattern:[NSString stringWithUTF8String:denyPattern] + options:0 + error:&err]; + if (err != nil) { + return NO; + } + } + if (hookPattern == nil || *hookPattern == '\0') { + hook = nil; + } else { + hook + = [NSRegularExpression + regularExpressionWithPattern:[NSString stringWithUTF8String:hookPattern] + options:0 + error:&err]; + if (err != nil) { + return NO; + } + } + allowRegex = allow; + denyRegex = deny; + hookRegex = hook; + return YES; +} + +- (void)loadURL:(const char *)url +{ + if (webView == nil) + return; + NSString *urlStr = [NSString stringWithUTF8String:url]; + NSURL *nsurl = [NSURL URLWithString:urlStr]; + NSURLRequest *request = [NSURLRequest requestWithURL:nsurl]; + [webView load:request]; +} + +- (void)loadHTML:(const char *)html baseURL:(const char *)baseUrl +{ + if (webView == nil) + return; + NSString *htmlStr = [NSString stringWithUTF8String:html]; + NSString *baseStr = [NSString stringWithUTF8String:baseUrl]; + NSURL *baseNSUrl = [NSURL URLWithString:baseStr]; + [webView loadHTML:htmlStr baseURL:baseNSUrl]; +} + +- (void)evaluateJS:(const char *)js +{ + if (webView == nil) + return; + NSString *jsStr = [NSString stringWithUTF8String:js]; + [webView evaluateJavaScript:jsStr completionHandler:^(NSString *result, NSError *error) {}]; +} + +- (int)progress +{ + if (webView == nil) + return 0; + if ([webView isKindOfClass:[WKWebView class]]) { + return (int)([(WKWebView *)webView estimatedProgress] * 100); + } else { + return 0; + } +} + +- (BOOL)canGoBack +{ + if (webView == nil) + return false; + return [webView canGoBack]; +} + +- (BOOL)canGoForward +{ + if (webView == nil) + return false; + return [webView canGoForward]; +} + +- (void)goBack +{ + if (webView == nil) + return; + [webView goBack]; +} + +- (void)goForward +{ + if (webView == nil) + return; + [webView goForward]; +} + +- (void)reload +{ + if (webView == nil) + return; + [webView reload]; +} + +- (void)addCustomRequestHeader:(const char *)headerKey value:(const char *)headerValue +{ + NSString *keyString = [NSString stringWithUTF8String:headerKey]; + NSString *valueString = [NSString stringWithUTF8String:headerValue]; + + [customRequestHeader setObject:valueString forKey:keyString]; +} + +- (void)removeCustomRequestHeader:(const char *)headerKey +{ + NSString *keyString = [NSString stringWithUTF8String:headerKey]; + + if ([[customRequestHeader allKeys]containsObject:keyString]) { + [customRequestHeader removeObjectForKey:keyString]; + } +} + +- (void)clearCustomRequestHeader +{ + [customRequestHeader removeAllObjects]; +} + +- (const char *)getCustomRequestHeaderValue:(const char *)headerKey +{ + NSString *keyString = [NSString stringWithUTF8String:headerKey]; + NSString *result = [customRequestHeader objectForKey:keyString]; + if (!result) { + return NULL; + } + + const char *s = [result UTF8String]; + char *r = (char *)malloc(strlen(s) + 1); + strcpy(r, s); + return r; +} + +- (void)setBasicAuthInfo:(const char *)userName password:(const char *)password +{ + basicAuthUserName = [NSString stringWithUTF8String:userName]; + basicAuthPassword = [NSString stringWithUTF8String:password]; +} +@end + +extern "C" { + BOOL _CWebViewPlugin_IsInitialized(void *instance); + void *_CWebViewPlugin_Init(const char *gameObjectName, BOOL transparent, BOOL zoom, const char *ua, BOOL enableWKWebView, int contentMode, BOOL allowsLinkPreview, BOOL allowsBackForwardNavigationGestures, int radius); + void _CWebViewPlugin_Destroy(void *instance); + void _CWebViewPlugin_SetMargins( + void *instance, float left, float top, float right, float bottom, BOOL relative); + void _CWebViewPlugin_SetVisibility(void *instance, BOOL visibility); + void _CWebViewPlugin_SetInteractionEnabled(void *instance, BOOL enabled); + void _CWebViewPlugin_SetGoogleAppRedirectionEnabled(void *instance, BOOL enabled); + void _CWebViewPlugin_SetAlertDialogEnabled(void *instance, BOOL visibility); + void _CWebViewPlugin_SetScrollbarsVisibility(void *instance, BOOL visibility); + void _CWebViewPlugin_SetScrollBounceEnabled(void *instance, BOOL enabled); + BOOL _CWebViewPlugin_SetURLPattern(void *instance, const char *allowPattern, const char *denyPattern, const char *hookPattern); + void _CWebViewPlugin_LoadURL(void *instance, const char *url); + void _CWebViewPlugin_LoadHTML(void *instance, const char *html, const char *baseUrl); + void _CWebViewPlugin_EvaluateJS(void *instance, const char *url); + int _CWebViewPlugin_Progress(void *instance); + BOOL _CWebViewPlugin_CanGoBack(void *instance); + BOOL _CWebViewPlugin_CanGoForward(void *instance); + void _CWebViewPlugin_GoBack(void *instance); + void _CWebViewPlugin_GoForward(void *instance); + void _CWebViewPlugin_Reload(void *instance); + void _CWebViewPlugin_AddCustomHeader(void *instance, const char *headerKey, const char *headerValue); + void _CWebViewPlugin_RemoveCustomHeader(void *instance, const char *headerKey); + void _CWebViewPlugin_ClearCustomHeader(void *instance); + void _CWebViewPlugin_ClearCookie(const char *url, const char *name); + void _CWebViewPlugin_ClearCookies(); + void _CWebViewPlugin_SaveCookies(); + void _CWebViewPlugin_GetCookies(void *instance, const char *url); + const char *_CWebViewPlugin_GetCustomHeaderValue(void *instance, const char *headerKey); + void _CWebViewPlugin_SetBasicAuthInfo(void *instance, const char *userName, const char *password); + void _CWebViewPlugin_ClearCache(void *instance, BOOL includeDiskFiles); + void _CWebViewPlugin_SetSuspended(void *instance, BOOL suspended); +} + +BOOL _CWebViewPlugin_IsInitialized(void *instance) +{ + if (instance == NULL) + return NO; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + return [webViewPlugin isInitialized]; +} + +void *_CWebViewPlugin_Init(const char *gameObjectName, BOOL transparent, BOOL zoom, const char *ua, BOOL enableWKWebView, int contentMode, BOOL allowsLinkPreview, BOOL allowsBackForwardNavigationGestures, int radius) +{ + WKContentMode wkContentMode = WKContentModeRecommended; + switch (contentMode) { + case 1: + wkContentMode = WKContentModeMobile; + break; + case 2: + wkContentMode = WKContentModeDesktop; + break; + default: + wkContentMode = WKContentModeRecommended; + break; + } + CWebViewPlugin *webViewPlugin = [[CWebViewPlugin alloc] initWithGameObjectName:gameObjectName transparent:transparent zoom:zoom ua:ua enableWKWebView:enableWKWebView contentMode:wkContentMode allowsLinkPreview:allowsLinkPreview allowsBackForwardNavigationGestures:allowsBackForwardNavigationGestures radius:radius]; + [_instances addObject:webViewPlugin]; + return (__bridge_retained void *)webViewPlugin; +} + +void _CWebViewPlugin_Destroy(void *instance) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge_transfer CWebViewPlugin *)instance; + [_instances removeObject:webViewPlugin]; + [webViewPlugin dispose]; + webViewPlugin = nil; +} + +void _CWebViewPlugin_SetMargins( + void *instance, float left, float top, float right, float bottom, BOOL relative) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setMargins:left top:top right:right bottom:bottom relative:relative]; +} + +void _CWebViewPlugin_SetVisibility(void *instance, BOOL visibility) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setVisibility:visibility]; +} + +void _CWebViewPlugin_SetInteractionEnabled(void *instance, BOOL enabled) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setInteractionEnabled:enabled]; +} + +void _CWebViewPlugin_SetGoogleAppRedirectionEnabled(void *instance, BOOL enabled) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setGoogleAppRedirectionEnabled:enabled]; +} + +void _CWebViewPlugin_SetAlertDialogEnabled(void *instance, BOOL enabled) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setAlertDialogEnabled:enabled]; +} + +void _CWebViewPlugin_SetScrollbarsVisibility(void *instance, BOOL visibility) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setScrollbarsVisibility:visibility]; +} + +void _CWebViewPlugin_SetScrollBounceEnabled(void *instance, BOOL enabled) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setScrollBounceEnabled:enabled]; +} + +BOOL _CWebViewPlugin_SetURLPattern(void *instance, const char *allowPattern, const char *denyPattern, const char *hookPattern) +{ + if (instance == NULL) + return NO; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + return [webViewPlugin setURLPattern:allowPattern and:denyPattern and:hookPattern]; +} + +void _CWebViewPlugin_LoadURL(void *instance, const char *url) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin loadURL:url]; +} + +void _CWebViewPlugin_LoadHTML(void *instance, const char *html, const char *baseUrl) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin loadHTML:html baseURL:baseUrl]; +} + +void _CWebViewPlugin_EvaluateJS(void *instance, const char *js) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin evaluateJS:js]; +} + +int _CWebViewPlugin_Progress(void *instance) +{ + if (instance == NULL) + return 0; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + return [webViewPlugin progress]; +} + +BOOL _CWebViewPlugin_CanGoBack(void *instance) +{ + if (instance == NULL) + return false; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + return [webViewPlugin canGoBack]; +} + +BOOL _CWebViewPlugin_CanGoForward(void *instance) +{ + if (instance == NULL) + return false; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + return [webViewPlugin canGoForward]; +} + +void _CWebViewPlugin_GoBack(void *instance) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin goBack]; +} + +void _CWebViewPlugin_GoForward(void *instance) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin goForward]; +} + +void _CWebViewPlugin_Reload(void *instance) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin reload]; +} + +void _CWebViewPlugin_AddCustomHeader(void *instance, const char *headerKey, const char *headerValue) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin addCustomRequestHeader:headerKey value:headerValue]; +} + +void _CWebViewPlugin_RemoveCustomHeader(void *instance, const char *headerKey) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin removeCustomRequestHeader:headerKey]; +} + +void _CWebViewPlugin_ClearCustomHeader(void *instance) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin clearCustomRequestHeader]; +} + +void _CWebViewPlugin_ClearCookie(const char *url, const char *name) +{ + [CWebViewPlugin clearCookie:name of:url]; +} + +void _CWebViewPlugin_ClearCookies() +{ + [CWebViewPlugin clearCookies]; +} + +void _CWebViewPlugin_SaveCookies() +{ + [CWebViewPlugin saveCookies]; +} + +void _CWebViewPlugin_GetCookies(void *instance, const char *url) +{ + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin getCookies:url]; +} + +const char *_CWebViewPlugin_GetCustomHeaderValue(void *instance, const char *headerKey) +{ + if (instance == NULL) + return NULL; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + return [webViewPlugin getCustomRequestHeaderValue:headerKey]; +} + +void _CWebViewPlugin_SetBasicAuthInfo(void *instance, const char *userName, const char *password) +{ + if (instance == NULL) + return; + CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance; + [webViewPlugin setBasicAuthInfo:userName password:password]; +} + +void _CWebViewPlugin_ClearCache(void *instance, BOOL includeDiskFiles) +{ + // no op +} + +void _CWebViewPlugin_SetSuspended(void *instance, BOOL suspended) +{ + // no op +} +#endif // __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0 diff --git a/Packages/com.bywaystudios.webview/Plugins/iOS/WebViewWithUIWebView.mm.meta b/Packages/com.bywaystudios.webview/Plugins/iOS/WebViewWithUIWebView.mm.meta new file mode 100644 index 0000000..f5e0fb8 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/iOS/WebViewWithUIWebView.mm.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fc99cbfa2b53248b18d60e327b478581 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Plugins/unity-webview-webgl-plugin.jslib b/Packages/com.bywaystudios.webview/Plugins/unity-webview-webgl-plugin.jslib new file mode 100644 index 0000000..9577738 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/unity-webview-webgl-plugin.jslib @@ -0,0 +1,31 @@ +mergeInto(LibraryManager.library, { + _gree_unity_webview_init: function(name) { + var stringify = (UTF8ToString === undefined) ? Pointer_stringify : UTF8ToString; + unityWebView.init(stringify(name)); + }, + + _gree_unity_webview_setMargins: function (name, left, top, right, bottom) { + var stringify = (UTF8ToString === undefined) ? Pointer_stringify : UTF8ToString; + unityWebView.setMargins(stringify(name), left, top, right, bottom); + }, + + _gree_unity_webview_setVisibility: function(name, visible) { + var stringify = (UTF8ToString === undefined) ? Pointer_stringify : UTF8ToString; + unityWebView.setVisibility(stringify(name), visible); + }, + + _gree_unity_webview_loadURL: function(name, url) { + var stringify = (UTF8ToString === undefined) ? Pointer_stringify : UTF8ToString; + unityWebView.loadURL(stringify(name), stringify(url)); + }, + + _gree_unity_webview_evaluateJS: function(name, js) { + var stringify = (UTF8ToString === undefined) ? Pointer_stringify : UTF8ToString; + unityWebView.evaluateJS(stringify(name), stringify(js)); + }, + + _gree_unity_webview_destroy: function(name) { + var stringify = (UTF8ToString === undefined) ? Pointer_stringify : UTF8ToString; + unityWebView.destroy(stringify(name)); + }, +}); diff --git a/Packages/com.bywaystudios.webview/Plugins/unity-webview-webgl-plugin.jslib.meta b/Packages/com.bywaystudios.webview/Plugins/unity-webview-webgl-plugin.jslib.meta new file mode 100644 index 0000000..2e24b02 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Plugins/unity-webview-webgl-plugin.jslib.meta @@ -0,0 +1,32 @@ +fileFormatVersion: 2 +guid: 1353be0798ab043d992cd72e4d92970b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Runtime.meta b/Packages/com.bywaystudios.webview/Runtime.meta new file mode 100644 index 0000000..26ad4a9 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 75288488aded1af48851035219fd4e3a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Runtime/BywayWebView.cs b/Packages/com.bywaystudios.webview/Runtime/BywayWebView.cs new file mode 100644 index 0000000..b828f81 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Runtime/BywayWebView.cs @@ -0,0 +1,292 @@ +using System.Collections; +using UnityEngine; +using UnityEngine.Networking; +using UnityEngine.UI; +using Byway.Threading.Tasks; + +namespace Byway.UnityWebView +{ + public class BywayWebView + { + public string Url; + WebViewObject webViewObject; + + private void _CallFromJS(string msg) + { + Debug.Log(string.Format("CallFromJS[{0}]", msg)); + } + + private void _CallOnError(string msg) + { + Debug.Log(string.Format("CallOnError[{0}]", msg)); + } + + private void _CallOnHttpError(string msg) + { + Debug.Log(string.Format("CallOnHttpError[{0}]", msg)); + } + + private void _CallOnStarted(string msg) + { + Debug.Log(string.Format("CallOnStarted[{0}]", msg)); + } + + private void _CallOnHooked(string msg) + { + Debug.Log(string.Format("CallOnHooked[{0}]", msg)); + } + + private void _CallOnCookies(string msg) + { + Debug.Log(string.Format("CallOnCookies[{0}]", msg)); + } + + private void _CallOnLoaded(string msg) + { + Debug.Log(string.Format("CallOnLoaded[{0}]", msg)); + +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS + // NOTE: the following js definition is required only for UIWebView; if + // enabledWKWebView is true and runtime has WKWebView, Unity.call is defined + // directly by the native plugin. +#if true + var js = @" + if (!(window.webkit && window.webkit.messageHandlers)) { + window.Unity = { + call: function(msg) { + window.location = 'unity:' + msg; + } + }; + } + "; +#else + // NOTE: depending on the situation, you might prefer this 'iframe' approach. + // cf. https://github.com/gree/unity-webview/issues/189 + var js = @" + if (!(window.webkit && window.webkit.messageHandlers)) { + window.Unity = { + call: function(msg) { + var iframe = document.createElement('IFRAME'); + iframe.setAttribute('src', 'unity:' + msg); + document.documentElement.appendChild(iframe); + iframe.parentNode.removeChild(iframe); + iframe = null; + } + }; + } + "; +#endif +#elif UNITY_WEBPLAYER || UNITY_WEBGL + var js = @" + window.Unity = { + call:function(msg) { + parent.unityWebView.sendMessage('WebViewObject', msg); + } + }; + "; +#else + var js = ""; +#endif + webViewObject.EvaluateJS(js + @"Unity.call('ua=' + navigator.userAgent)"); + } + + public BywayWebView() + { + var go = new GameObject("[WebViewObject]"); + go.hideFlags = HideFlags.HideAndDontSave; + Object.DontDestroyOnLoad(go); + webViewObject = go.AddComponent(); +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + webViewObject.canvas = GameObject.Find("Canvas"); +#endif + webViewObject.Init( + cb: _CallFromJS, + err: _CallOnError, + httpErr: _CallOnHttpError, + started: _CallOnStarted, + hooked: _CallOnHooked, + cookies: _CallOnCookies, + ld: _CallOnLoaded + //transparent: false, + //zoom: true, + //ua: "custom user agent string", + //radius: 0, // rounded corner radius in pixel + //// android + //androidForceDarkMode: 0, // 0: follow system setting, 1: force dark off, 2: force dark on + //// ios + //enableWKWebView: true, + //wkContentMode: 0, // 0: recommended, 1: mobile, 2: desktop + //wkAllowsLinkPreview: true, + //// editor + //separated: false + ); + // cf. https://github.com/gree/unity-webview/issues/1094#issuecomment-2358718029 + } + + public BywayWebView(string url) : this() + { + Url = url; + } + + public async UniTask Ready() + { + await UniTask.WaitUntil(() => webViewObject.IsInitialized()); + return true; + } + + public int marginLeft = 0; + public int marginTop = 0; + public int marginRight = 0; + public int marginBottom = 0; + + public int textZoom = 100; + + private void _Preprocess() + { +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + webViewObject.bitmapRefreshCycle = 1; + webViewObject.devicePixelRatio = 1; // 1 or 2 +#endif + // cf. https://github.com/gree/unity-webview/pull/512 + // Added alertDialogEnabled flag to enable/disable alert/confirm/prompt dialogs. by KojiNakamaru · Pull Request #512 · gree/unity-webview + //webViewObject.SetAlertDialogEnabled(false); + + // cf. https://github.com/gree/unity-webview/pull/728 + //webViewObject.SetCameraAccess(true); + //webViewObject.SetMicrophoneAccess(true); + + // cf. https://github.com/gree/unity-webview/pull/550 + // introduced SetURLPattern(..., hookPattern). by KojiNakamaru · Pull Request #550 · gree/unity-webview + //webViewObject.SetURLPattern("", "^https://.*youtube.com", "^https://.*google.com"); + + // cf. https://github.com/gree/unity-webview/pull/570 + // Add BASIC authentication feature (Android and iOS with WKWebView only) by takeh1k0 · Pull Request #570 · gree/unity-webview + //webViewObject.SetBasicAuthInfo("id", "password"); + + //webViewObject.SetScrollbarsVisibility(true); + + webViewObject.SetMargins(marginLeft, marginTop, marginRight, marginBottom); + webViewObject.SetTextZoom(textZoom); // android only. cf. https://stackoverflow.com/questions/21647641/android-webview-set-font-size-system-default/47017410#47017410 + //webViewObject.SetMixedContentMode(2); // android only. 0: MIXED_CONTENT_ALWAYS_ALLOW, 1: MIXED_CONTENT_NEVER_ALLOW, 2: MIXED_CONTENT_COMPATIBILITY_MODE + webViewObject.SetVisibility(true); + } + + private async UniTask _Open() + { +#if !UNITY_WEBPLAYER && !UNITY_WEBGL + if (Url.StartsWith("http")) + { + webViewObject.LoadURL(Url.Replace(" ", "%20")); + } + else + { + var exts = new string[]{ + ".jpg", + ".js", + ".html" // should be last + }; + + foreach (var ext in exts) + { + var url = Url.Replace(".html", ext); + var src = System.IO.Path.Combine(Application.streamingAssetsPath, url); + var dst = System.IO.Path.Combine(Application.temporaryCachePath, url); + byte[] result = null; + if (src.Contains("://")) + { // for Android + // NOTE: a more complete code that utilizes UnityWebRequest can be found in https://github.com/gree/unity-webview/commit/2a07e82f760a8495aa3a77a23453f384869caba7#diff-4379160fa4c2a287f414c07eb10ee36d + var unityWebRequest = UnityWebRequest.Get(src); + await unityWebRequest.SendWebRequest(); + result = unityWebRequest.downloadHandler.data; + } + else + { + result = System.IO.File.ReadAllBytes(src); + } + System.IO.File.WriteAllBytes(dst, result); + if (ext == ".html") + { + webViewObject.LoadURL("file://" + dst.Replace(" ", "%20")); + break; + } + } + } +#else + if (Url.StartsWith("http")) + { + webViewObject.LoadURL(Url.Replace(" ", "%20")); + } + else + { + webViewObject.LoadURL("StreamingAssets/" + Url.Replace(" ", "%20")); + } +#endif + } + + public async UniTask Open() + { + await Ready(); + _Preprocess(); + await _Open(); + } + + public async UniTask Open(string url) + { + Url = url; + await Open(); + } + + public bool CanGoBack() + { + return (webViewObject == null) ? false : webViewObject.CanGoBack(); + } + + public bool CanGoForward() + { + return (webViewObject == null) ? false : webViewObject.CanGoForward(); + } + + public void GoBack() + { + webViewObject?.GoBack(); + } + + public void GoForward() + { + webViewObject?.GoForward(); + } + + public void Reload() + { + webViewObject?.Reload(); + } + + public int Progress() + { + return (webViewObject == null) ? 0 : webViewObject.Progress(); + } + + public void ClearCookies() + { + webViewObject?.ClearCookies(); + } + + public void GetCookies(string url) + { + webViewObject?.GetCookies(url); + } + + public void SetInteractionEnabled(bool enabled) + { + webViewObject?.SetInteractionEnabled(enabled); + } + + public void Close() + { + if (webViewObject != null && webViewObject.gameObject != null) + { + GameObject.Destroy(webViewObject.gameObject); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.bywaystudios.webview/Runtime/BywayWebView.cs.meta b/Packages/com.bywaystudios.webview/Runtime/BywayWebView.cs.meta new file mode 100644 index 0000000..45a91cd --- /dev/null +++ b/Packages/com.bywaystudios.webview/Runtime/BywayWebView.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: fb516f7ac0cee4b0f8345a5a60de3f42 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Packages/com.bywaystudios.webview/Runtime/WebView.asmdef b/Packages/com.bywaystudios.webview/Runtime/WebView.asmdef new file mode 100644 index 0000000..187901e --- /dev/null +++ b/Packages/com.bywaystudios.webview/Runtime/WebView.asmdef @@ -0,0 +1,16 @@ +{ + "name": "WebView", + "rootNamespace": "", + "references": [ + "UniTask" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Packages/com.bywaystudios.webview/Runtime/WebView.asmdef.meta b/Packages/com.bywaystudios.webview/Runtime/WebView.asmdef.meta new file mode 100644 index 0000000..f4d9abe --- /dev/null +++ b/Packages/com.bywaystudios.webview/Runtime/WebView.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 15347ff89b28daa438c9826c6312a027 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/Runtime/WebViewObject.cs b/Packages/com.bywaystudios.webview/Runtime/WebViewObject.cs new file mode 100644 index 0000000..8f99412 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Runtime/WebViewObject.cs @@ -0,0 +1,2124 @@ +using UnityEngine; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; +#if UNITY_2018_4_OR_NEWER +using UnityEngine.Networking; +#endif +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX +using System.IO; +using System.Text.RegularExpressions; +using UnityEngine.EventSystems; +using UnityEngine.Rendering; +using UnityEngine.UI; +#endif +#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN +using UnityEngine.Rendering; +using UnityEngine.UI; +#endif +#if UNITY_ANDROID +using UnityEngine.Android; +#endif + +using Callback = System.Action; + +namespace Byway.UnityWebView +{ +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + public class UnitySendMessageDispatcher + { + public static void Dispatch(string name, string method, string message) + { + GameObject obj = GameObject.Find(name); + if (obj != null) + obj.SendMessage(method, message); + } + } +#endif + + public class WebViewObject : MonoBehaviour + { + Callback onJS; + Callback onError; + Callback onHttpError; + Callback onStarted; + Callback onLoaded; + Callback onHooked; + Callback onCookies; + bool paused; + bool visibility; + bool alertDialogEnabled; + bool scrollBounceEnabled; + int mMarginLeft; + int mMarginTop; + int mMarginRight; + int mMarginBottom; + bool mMarginRelative; + float mMarginLeftComputed; + float mMarginTopComputed; + float mMarginRightComputed; + float mMarginBottomComputed; + bool mMarginRelativeComputed; +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + public GameObject canvas; + Image bg; + IntPtr webView; + Rect rect; + Texture2D texture; +#if UNITY_2018_2_OR_NEWER +#else + byte[] textureDataBuffer; +#endif + string inputString = ""; + bool hasFocus; +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + public GameObject canvas; + Image bg; + IntPtr webView; + Rect rect; + Texture2D texture; + byte[] textureDataBuffer; + string inputString = ""; + bool hasFocus; +#elif UNITY_IPHONE + IntPtr webView; +#elif UNITY_ANDROID + AndroidJavaObject webView; + + bool mVisibility; + int mKeyboardVisibleHeight; + float mResumedTimestamp; + int mLastScreenHeight; +#if UNITYWEBVIEW_ANDROID_ENABLE_NAVIGATOR_ONLINE + float androidNetworkReachabilityCheckT0 = -1.0f; + NetworkReachability? androidNetworkReachability0 = null; +#endif + + void OnApplicationPause(bool paused) + { + this.paused = paused; + if (webView == null) + return; + // if (!paused && mKeyboardVisibleHeight > 0) + // { + // webView.Call("SetVisibility", false); + // mResumedTimestamp = Time.realtimeSinceStartup; + // } + webView.Call("OnApplicationPause", paused); + } + + void Update() + { + // NOTE: + // + // When OnApplicationPause(true) is called and the app is in closing, webView.Call(...) + // after that could cause crashes because underlying java instances were closed. + // + // This has not been cleary confirmed yet. However, as Update() is called once after + // OnApplicationPause(true), it is likely correct. + // + // Base on this assumption, we do nothing here if the app is paused. + // + // cf. https://github.com/gree/unity-webview/issues/991#issuecomment-1776628648 + // cf. https://docs.unity3d.com/2020.3/Documentation/Manual/ExecutionOrder.html + // + // In between frames + // + // * OnApplicationPause: This is called at the end of the frame where the pause is detected, + // effectively between the normal frame updates. One extra frame will be issued after + // OnApplicationPause is called to allow the game to show graphics that indicate the + // paused state. + // + if (paused) + return; + if (webView == null) + return; +#if UNITYWEBVIEW_ANDROID_ENABLE_NAVIGATOR_ONLINE + var t = Time.time; + if (t - 1.0f >= androidNetworkReachabilityCheckT0) + { + androidNetworkReachabilityCheckT0 = t; + var androidNetworkReachability = Application.internetReachability; + if (androidNetworkReachability0 != androidNetworkReachability) + { + androidNetworkReachability0 = androidNetworkReachability; + webView.Call("SetNetworkAvailable", androidNetworkReachability != NetworkReachability.NotReachable); + } + } +#endif + if (mResumedTimestamp != 0.0f && Time.realtimeSinceStartup - mResumedTimestamp > 0.5f) + { + mResumedTimestamp = 0.0f; + webView.Call("SetVisibility", mVisibility); + } + if (Screen.height != mLastScreenHeight) + { + mLastScreenHeight = Screen.height; + webView.Call("EvaluateJS", "(function() {var e = document.activeElement; if (e != null && e.tagName.toLowerCase() != 'body') {e.blur(); e.focus();}})()"); + } + for (;;) { + if (webView == null) + break; + var s = webView.Call("GetMessage"); + if (s == null) + break; + var i = s.IndexOf(':', 0); + if (i == -1) + continue; + switch (s.Substring(0, i)) { + case "CallFromJS": + CallFromJS(s.Substring(i + 1)); + break; + case "CallOnError": + CallOnError(s.Substring(i + 1)); + break; + case "CallOnHttpError": + CallOnHttpError(s.Substring(i + 1)); + break; + case "CallOnLoaded": + CallOnLoaded(s.Substring(i + 1)); + break; + case "CallOnStarted": + CallOnStarted(s.Substring(i + 1)); + break; + case "CallOnHooked": + CallOnHooked(s.Substring(i + 1)); + break; + case "CallOnCookies": + CallOnCookies(s.Substring(i + 1)); + break; + case "SetKeyboardVisible": + SetKeyboardVisible(s.Substring(i + 1)); + break; + case "RequestFileChooserPermissions": + RequestFileChooserPermissions(); + break; + } + } + } + + /// Called from Java native plugin to set when the keyboard is opened + public void SetKeyboardVisible(string keyboardVisibleHeight) + { + if (BottomAdjustmentDisabled()) + { + return; + } + var keyboardVisibleHeight0 = mKeyboardVisibleHeight; + var keyboardVisibleHeight1 = Int32.Parse(keyboardVisibleHeight); + if (keyboardVisibleHeight0 != keyboardVisibleHeight1) + { + mKeyboardVisibleHeight = keyboardVisibleHeight1; + SetMargins(mMarginLeft, mMarginTop, mMarginRight, mMarginBottom, mMarginRelative); + EvaluateJS("setTimeout(function(){if(document&&document.activeElement){document.activeElement.scrollIntoView();}}, 200);"); + } + } + + /// Called from Java native plugin to request permissions for the file chooser. + public void RequestFileChooserPermissions() + { + var permissions = new List(); + using (var version = new AndroidJavaClass("android.os.Build$VERSION")) + { + if (version.GetStatic("SDK_INT") >= 33) + { + if (!Permission.HasUserAuthorizedPermission("android.permission.READ_MEDIA_IMAGES")) + { + permissions.Add("android.permission.READ_MEDIA_IMAGES"); + } + if (!Permission.HasUserAuthorizedPermission("android.permission.READ_MEDIA_VIDEO")) + { + permissions.Add("android.permission.READ_MEDIA_VIDEO"); + } + if (!Permission.HasUserAuthorizedPermission("android.permission.READ_MEDIA_AUDIO")) + { + permissions.Add("android.permission.READ_MEDIA_AUDIO"); + } + } + else + { + if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageRead)) + { + permissions.Add(Permission.ExternalStorageRead); + } + if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite)) + { + permissions.Add(Permission.ExternalStorageWrite); + } + } + } + if (!Permission.HasUserAuthorizedPermission(Permission.Camera)) + { + permissions.Add(Permission.Camera); + } + if (permissions.Count > 0) + { +#if UNITY_2020_2_OR_NEWER + var grantedCount = 0; + var deniedCount = 0; + var callbacks = new PermissionCallbacks(); + callbacks.PermissionGranted += (permission) => + { + grantedCount++; + if (grantedCount + deniedCount == permissions.Count) + { + StartCoroutine(CallOnRequestFileChooserPermissionsResult(grantedCount == permissions.Count)); + } + }; + callbacks.PermissionDenied += (permission) => + { + deniedCount++; + if (grantedCount + deniedCount == permissions.Count) + { + StartCoroutine(CallOnRequestFileChooserPermissionsResult(grantedCount == permissions.Count)); + } + }; + callbacks.PermissionDeniedAndDontAskAgain += (permission) => + { + deniedCount++; + if (grantedCount + deniedCount == permissions.Count) + { + StartCoroutine(CallOnRequestFileChooserPermissionsResult(grantedCount == permissions.Count)); + } + }; + Permission.RequestUserPermissions(permissions.ToArray(), callbacks); +#else + StartCoroutine(RequestFileChooserPermissionsCoroutine(permissions.ToArray())); +#endif + } + else + { + StartCoroutine(CallOnRequestFileChooserPermissionsResult(true)); + } + } + +#if UNITY_2020_2_OR_NEWER +#else + int mRequestPermissionPhase; + + IEnumerator RequestFileChooserPermissionsCoroutine(string[] permissions) + { + foreach (var permission in permissions) + { + mRequestPermissionPhase = 0; + Permission.RequestUserPermission(permission); + // waiting permission dialog that may not be opened. + for (var i = 0; i < 8 && mRequestPermissionPhase == 0; i++) + { + yield return new WaitForSeconds(0.25f); + } + if (mRequestPermissionPhase == 0) + { + // permission dialog was not opened. + continue; + } + while (mRequestPermissionPhase == 1) + { + yield return new WaitForSeconds(0.3f); + } + } + yield return new WaitForSeconds(0.3f); + var granted = 0; + foreach (var permission in permissions) + { + if (Permission.HasUserAuthorizedPermission(permission)) + { + granted++; + } + } + StartCoroutine(CallOnRequestFileChooserPermissionsResult(granted == permissions.Length)); + } + + void OnApplicationFocus(bool hasFocus) + { + if (hasFocus) + { + if (mRequestPermissionPhase == 1) + { + mRequestPermissionPhase = 2; + } + } + else + { + if (mRequestPermissionPhase == 0) + { + mRequestPermissionPhase = 1; + } + } + } +#endif + + private IEnumerator CallOnRequestFileChooserPermissionsResult(bool granted) + { + for (var i = 0; i < 3; i++) + { + yield return null; + } + webView.Call("OnRequestFileChooserPermissionsResult", granted); + } + + public int AdjustBottomMargin(int bottom) + { + if (BottomAdjustmentDisabled()) + { + return bottom; + } + else if (mKeyboardVisibleHeight <= 0) + { + return bottom; + } + else + { + int keyboardHeight = 0; + using (var unityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) + using (var activity = unityClass.GetStatic("currentActivity")) + using (var player = activity.Get("mUnityPlayer")) + using (var view = player.Call("getView")) + using (var rect = new AndroidJavaObject("android.graphics.Rect")) + { + if (view.Call("getGlobalVisibleRect", rect)) + { + int h0 = rect.Get("bottom"); + view.Call("getWindowVisibleDisplayFrame", rect); + int h1 = rect.Get("bottom"); + keyboardHeight = h0 - h1; + } + } + return (bottom > keyboardHeight) ? bottom : keyboardHeight; + } + } + + private bool BottomAdjustmentDisabled() + { +#if UNITYWEBVIEW_ANDROID_FORCE_MARGIN_ADJUSTMENT_FOR_KEYBOARD + return false; +#else + return + !Screen.fullScreen + || ((Screen.autorotateToLandscapeLeft || Screen.autorotateToLandscapeRight) + && (Screen.autorotateToPortrait || Screen.autorotateToPortraitUpsideDown)); +#endif + } +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + IntPtr webView; +#else + IntPtr webView; +#endif + + void Awake() + { + alertDialogEnabled = true; + scrollBounceEnabled = true; + mMarginLeftComputed = -9999; + mMarginTopComputed = -9999; + mMarginRightComputed = -9999; + mMarginBottomComputed = -9999; + } + + public bool IsKeyboardVisible + { + get + { +#if !UNITY_EDITOR && UNITY_ANDROID + return mKeyboardVisibleHeight > 0; +#elif !UNITY_EDITOR && UNITY_IPHONE + return TouchScreenKeyboard.visible; +#else + return false; +#endif + } + } + +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + [DllImport("WebView")] + private static extern string _CWebViewPlugin_GetAppPath(); + [DllImport("WebView")] + private static extern IntPtr _CWebViewPlugin_InitStatic( + bool inEditor, bool useMetal); + [DllImport("WebView")] + private static extern bool _CWebViewPlugin_IsInitialized( + IntPtr instance); + [DllImport("WebView")] + private static extern IntPtr _CWebViewPlugin_Init( + string gameObject, bool transparent, bool zoom, int width, int height, string ua, bool separated); + [DllImport("WebView")] + private static extern int _CWebViewPlugin_Destroy(IntPtr instance); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_SetRect( + IntPtr instance, int width, int height); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_SetVisibility( + IntPtr instance, bool visibility); + [DllImport("WebView")] + private static extern bool _CWebViewPlugin_SetURLPattern( + IntPtr instance, string allowPattern, string denyPattern, string hookPattern); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_LoadURL( + IntPtr instance, string url); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_LoadHTML( + IntPtr instance, string html, string baseUrl); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_EvaluateJS( + IntPtr instance, string url); + [DllImport("WebView")] + private static extern int _CWebViewPlugin_Progress( + IntPtr instance); + [DllImport("WebView")] + private static extern bool _CWebViewPlugin_CanGoBack( + IntPtr instance); + [DllImport("WebView")] + private static extern bool _CWebViewPlugin_CanGoForward( + IntPtr instance); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_GoBack( + IntPtr instance); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_GoForward( + IntPtr instance); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_Reload( + IntPtr instance); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_SendMouseEvent(IntPtr instance, int x, int y, float deltaY, int mouseState); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_SendKeyEvent(IntPtr instance, int x, int y, string keyChars, ushort keyCode, int keyState); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_Update(IntPtr instance, bool refreshBitmap, int devicePixelRatio); + [DllImport("WebView")] + private static extern int _CWebViewPlugin_BitmapWidth(IntPtr instance); + [DllImport("WebView")] + private static extern int _CWebViewPlugin_BitmapHeight(IntPtr instance); + [DllImport("WebView")] + private static extern bool _CWebViewPlugin_BitmapARGB(IntPtr instance); + [DllImport("WebView")] + private static extern IntPtr _CWebViewPlugin_Render(IntPtr instance, IntPtr textureBuffer); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_AddCustomHeader(IntPtr instance, string headerKey, string headerValue); + [DllImport("WebView")] + private static extern string _CWebViewPlugin_GetCustomHeaderValue(IntPtr instance, string headerKey); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_RemoveCustomHeader(IntPtr instance, string headerKey); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_ClearCustomHeader(IntPtr instance); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_ClearCookie(string url, string name); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_ClearCookies(); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_SaveCookies(); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_GetCookies(IntPtr instance, string url); + [DllImport("WebView")] + private static extern string _CWebViewPlugin_GetMessage(IntPtr instance); +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + [DllImport("WebView")] + private static extern string _CWebViewPlugin_GetAppPath(); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_InitStatic(bool inEditor, bool useMetal); + [DllImport("WebView")] + private static extern bool _CWebViewPlugin_IsInitialized(IntPtr instance); + [DllImport("WebView")] + private static extern IntPtr _CWebViewPlugin_Init(string gameObject, bool transparent, bool zoom, int width, int height, string ua, bool separated); + [DllImport("WebView")] + private static extern int _CWebViewPlugin_Destroy(IntPtr instance); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_SetRect(IntPtr instance, int width, int height); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_SetVisibility(IntPtr instance, bool visibility); + [DllImport("WebView")] + private static extern bool _CWebViewPlugin_SetURLPattern(IntPtr instance, string allowPattern, string denyPattern, string hookPattern); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_LoadURL(IntPtr instance, string url); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_LoadHTML(IntPtr instance, string html, string baseUrl); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_EvaluateJS(IntPtr instance, string url); + [DllImport("WebView")] + private static extern int _CWebViewPlugin_Progress(IntPtr instance); + [DllImport("WebView")] + private static extern bool _CWebViewPlugin_CanGoBack(IntPtr instance); + [DllImport("WebView")] + private static extern bool _CWebViewPlugin_CanGoForward(IntPtr instance); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_GoBack(IntPtr instance); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_GoForward(IntPtr instance); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_Reload(IntPtr instance); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_SendMouseEvent(IntPtr instance, int x, int y, float deltaY, int mouseState); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_SendKeyEvent(IntPtr instance, int x, int y, string keyChars, ushort keyCode, int keyState); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_Update(IntPtr instance, bool refreshBitmap, int devicePixelRatio); + [DllImport("WebView")] + private static extern int _CWebViewPlugin_BitmapWidth(IntPtr instance); + [DllImport("WebView")] + private static extern int _CWebViewPlugin_BitmapHeight(IntPtr instance); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_Render(IntPtr instance, IntPtr textureBuffer); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_AddCustomHeader(IntPtr instance, string headerKey, string headerValue); + [DllImport("WebView")] + private static extern string _CWebViewPlugin_GetCustomHeaderValue(IntPtr instance, string headerKey); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_RemoveCustomHeader(IntPtr instance, string headerKey); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_ClearCustomHeader(IntPtr instance); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_ClearCookie(string url, string name); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_ClearCookies(); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_SaveCookies(); + [DllImport("WebView")] + private static extern void _CWebViewPlugin_GetCookies(IntPtr instance, string url); + [DllImport("WebView")] + private static extern string _CWebViewPlugin_GetMessage(IntPtr instance); +#elif UNITY_IPHONE + [DllImport("__Internal")] + private static extern bool _CWebViewPlugin_IsInitialized( + IntPtr instance); + [DllImport("__Internal")] + private static extern IntPtr _CWebViewPlugin_Init(string gameObject, bool transparent, bool zoom, string ua, bool enableWKWebView, int wkContentMode, bool wkAllowsLinkPreview, bool wkAllowsBackForwardNavigationGestures, int radius); + [DllImport("__Internal")] + private static extern int _CWebViewPlugin_Destroy(IntPtr instance); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_SetMargins( + IntPtr instance, float left, float top, float right, float bottom, bool relative); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_SetVisibility( + IntPtr instance, bool visibility); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_SetScrollbarsVisibility( + IntPtr instance, bool visibility); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_SetAlertDialogEnabled( + IntPtr instance, bool enabled); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_SetScrollBounceEnabled( + IntPtr instance, bool enabled); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_SetInteractionEnabled( + IntPtr instance, bool enabled); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_SetGoogleAppRedirectionEnabled( + IntPtr instance, bool enabled); + [DllImport("__Internal")] + private static extern bool _CWebViewPlugin_SetURLPattern( + IntPtr instance, string allowPattern, string denyPattern, string hookPattern); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_LoadURL( + IntPtr instance, string url); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_LoadHTML( + IntPtr instance, string html, string baseUrl); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_EvaluateJS( + IntPtr instance, string url); + [DllImport("__Internal")] + private static extern int _CWebViewPlugin_Progress( + IntPtr instance); + [DllImport("__Internal")] + private static extern bool _CWebViewPlugin_CanGoBack( + IntPtr instance); + [DllImport("__Internal")] + private static extern bool _CWebViewPlugin_CanGoForward( + IntPtr instance); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_GoBack( + IntPtr instance); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_GoForward( + IntPtr instance); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_Reload( + IntPtr instance); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_AddCustomHeader(IntPtr instance, string headerKey, string headerValue); + [DllImport("__Internal")] + private static extern string _CWebViewPlugin_GetCustomHeaderValue(IntPtr instance, string headerKey); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_RemoveCustomHeader(IntPtr instance, string headerKey); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_ClearCustomHeader(IntPtr instance); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_ClearCookie(string url, string name); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_ClearCookies(); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_SaveCookies(); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_GetCookies(IntPtr instance, string url); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_SetBasicAuthInfo(IntPtr instance, string userName, string password); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_ClearCache(IntPtr instance, bool includeDiskFiles); + [DllImport("__Internal")] + private static extern void _CWebViewPlugin_SetSuspended(IntPtr instance, bool suspended); +#elif UNITY_WEBGL + [DllImport("__Internal")] + private static extern void _gree_unity_webview_init(string name); + [DllImport("__Internal")] + private static extern void _gree_unity_webview_setMargins(string name, int left, int top, int right, int bottom); + [DllImport("__Internal")] + private static extern void _gree_unity_webview_setVisibility(string name, bool visible); + [DllImport("__Internal")] + private static extern void _gree_unity_webview_loadURL(string name, string url); + [DllImport("__Internal")] + private static extern void _gree_unity_webview_evaluateJS(string name, string js); + [DllImport("__Internal")] + private static extern void _gree_unity_webview_destroy(string name); +#endif + + public static bool IsWebViewAvailable() + { +#if !UNITY_EDITOR && UNITY_ANDROID + using (var plugin = new AndroidJavaObject("net.gree.unitywebview.CWebViewPlugin")) + { + return plugin.CallStatic("IsWebViewAvailable"); + } +#else + return true; +#endif + } + + public bool IsInitialized() + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED + return true; +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED + return true; +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return false; + return _CWebViewPlugin_IsInitialized(webView); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return false; + return _CWebViewPlugin_IsInitialized(webView); +#elif UNITY_ANDROID + if (webView == null) + return false; + return webView.Call("IsInitialized"); +#endif + } + + public void Init( + Callback cb = null, + Callback err = null, + Callback httpErr = null, + Callback ld = null, + Callback started = null, + Callback hooked = null, + Callback cookies = null, + bool transparent = false, + bool zoom = true, + string ua = "", + int radius = 0, + // android + int androidForceDarkMode = 0, // 0: follow system setting, 1: force dark off, 2: force dark on + // ios + bool enableWKWebView = true, + int wkContentMode = 0, // 0: recommended, 1: mobile, 2: desktop + bool wkAllowsLinkPreview = true, + bool wkAllowsBackForwardNavigationGestures = true, + // editor + bool separated = false) + { +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + _CWebViewPlugin_InitStatic( + Application.platform == RuntimePlatform.OSXEditor, + SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal); +#endif + onJS = cb; + onError = err; + onHttpError = httpErr; + onStarted = started; + onLoaded = ld; + onHooked = hooked; + onCookies = cookies; +#if UNITY_WEBGL +#if !UNITY_EDITOR + _gree_unity_webview_init(name); +#endif +#elif UNITY_WEBPLAYER + Application.ExternalCall("unityWebView.init", name); +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED + Debug.LogError("Webview is not supported on this platform."); +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + _CWebViewPlugin_InitStatic( + Application.platform == RuntimePlatform.WindowsEditor, + SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11); + webView = _CWebViewPlugin_Init( + name, + transparent, + zoom, + Screen.width, + Screen.height, + ua +#if UNITY_EDITOR + , separated +#else + , false +#endif + ); + rect = new Rect(0, 0, Screen.width, Screen.height); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + { + var uri = new Uri(_CWebViewPlugin_GetAppPath()); + var info = File.ReadAllText(uri.LocalPath + "Contents/Info.plist"); + if (Regex.IsMatch(info, @"CFBundleGetInfoString\s*Unity version [5-9]\.[3-9]") + && !Regex.IsMatch(info, @"NSAppTransportSecurity\s*\s*NSAllowsArbitraryLoads\s*\s*")) { + Debug.LogWarning("WebViewObject: NSAppTransportSecurity isn't configured to allow HTTP. If you need to allow any HTTP access, please shutdown Unity and invoke:\n/usr/libexec/PlistBuddy -c \"Add NSAppTransportSecurity:NSAllowsArbitraryLoads bool true\" /Applications/Unity/Unity.app/Contents/Info.plist"); + } + } +#if UNITY_EDITOR_OSX + // if (string.IsNullOrEmpty(ua)) { + // ua = @"Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53"; + // } +#endif + webView = _CWebViewPlugin_Init( + name, + transparent, + zoom, + Screen.width, + Screen.height, + ua +#if UNITY_EDITOR + , separated +#else + , false +#endif + ); + rect = new Rect(0, 0, Screen.width, Screen.height); +#elif UNITY_IPHONE + webView = _CWebViewPlugin_Init(name, transparent, zoom, ua, enableWKWebView, wkContentMode, wkAllowsLinkPreview, wkAllowsBackForwardNavigationGestures, radius); +#elif UNITY_ANDROID + webView = new AndroidJavaObject("net.gree.unitywebview.CWebViewPlugin"); +#if UNITY_2021_1_OR_NEWER + webView.SetStatic("forceBringToFront", true); +#endif + webView.Call("Init", name, transparent, zoom, androidForceDarkMode, ua, radius); +#else + Debug.LogError("Webview is not supported on this platform."); +#endif + } + + protected virtual void OnDestroy() + { +#if UNITY_WEBGL +#if !UNITY_EDITOR + _gree_unity_webview_destroy(name); +#endif +#elif UNITY_WEBPLAYER + Application.ExternalCall("unityWebView.destroy", name); +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (bg != null) + { + Destroy(bg.gameObject); + } + if (webView == IntPtr.Zero) + return; + var ptr = webView; + webView = IntPtr.Zero; + _CWebViewPlugin_Destroy(ptr); + Destroy(texture); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + if (bg != null) { + Destroy(bg.gameObject); + } + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_Destroy(webView); + webView = IntPtr.Zero; + Destroy(texture); +#elif UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_Destroy(webView); + webView = IntPtr.Zero; +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("Destroy"); + webView.Dispose(); + webView = null; +#endif + } + + public void Pause() + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + //TODO: UNSUPPORTED +#elif UNITY_IPHONE + // NOTE: this suspends media playback only. + if (webView == null) + return; + _CWebViewPlugin_SetSuspended(webView, true); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("Pause"); +#endif + } + + public void Resume() + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + //TODO: UNSUPPORTED +#elif UNITY_IPHONE + // NOTE: this resumes media playback only. + _CWebViewPlugin_SetSuspended(webView, false); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("Resume"); +#endif + } + + // Use this function instead of SetMargins to easily set up a centered window + // NOTE: for historical reasons, `center` means the lower left corner and positive y values extend up. + public void SetCenterPositionWithScale(Vector2 center, Vector2 scale) + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + float left = (Screen.width - scale.x) / 2.0f + center.x; + float right = Screen.width - (left + scale.x); + float bottom = (Screen.height - scale.y) / 2.0f + center.y; + float top = Screen.height - (bottom + scale.y); + SetMargins((int)left, (int)top, (int)right, (int)bottom); +#else + float left = (Screen.width - scale.x) / 2.0f + center.x; + float right = Screen.width - (left + scale.x); + float bottom = (Screen.height - scale.y) / 2.0f + center.y; + float top = Screen.height - (bottom + scale.y); + SetMargins((int)left, (int)top, (int)right, (int)bottom); +#endif + } + + public void SetMargins(int left, int top, int right, int bottom, bool relative = false) + { +#if UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED + return; +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return; +#elif UNITY_WEBPLAYER || UNITY_WEBGL +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + if (webView == IntPtr.Zero) + return; +#elif UNITY_IPHONE + if (webView == IntPtr.Zero) + return; +#elif UNITY_ANDROID + if (webView == null) + return; +#endif + + mMarginLeft = left; + mMarginTop = top; + mMarginRight = right; + mMarginBottom = bottom; + mMarginRelative = relative; + float ml, mt, mr, mb; +#if UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + ml = left; + mt = top; + mr = right; + mb = bottom; +#elif UNITY_WEBPLAYER || UNITY_WEBGL + ml = left; + mt = top; + mr = right; + mb = bottom; +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + ml = left; + mt = top; + mr = right; + mb = bottom; +#elif UNITY_IPHONE + if (relative) + { + float w = (float)Screen.width; + float h = (float)Screen.height; + ml = left / w; + mt = top / h; + mr = right / w; + mb = bottom / h; + } + else + { + ml = left; + mt = top; + mr = right; + mb = bottom; + } +#elif UNITY_ANDROID + if (relative) + { + float w = (float)Screen.width; + float h = (float)Screen.height; + int iw = Display.main.systemWidth; + int ih = Display.main.systemHeight; + if (!Screen.fullScreen) + { + using (var unityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) + using (var activity = unityClass.GetStatic("currentActivity")) + using (var player = activity.Get("mUnityPlayer")) + using (var view = player.Call("getView")) + using (var rect = new AndroidJavaObject("android.graphics.Rect")) + { + view.Call("getDrawingRect", rect); + iw = rect.Call("width"); + ih = rect.Call("height"); + } + } + ml = left / w * iw; + mt = top / h * ih; + mr = right / w * iw; + mb = AdjustBottomMargin((int)(bottom / h * ih)); + } + else + { + ml = left; + mt = top; + mr = right; + mb = AdjustBottomMargin(bottom); + } +#endif + bool r = relative; + + if (ml == mMarginLeftComputed + && mt == mMarginTopComputed + && mr == mMarginRightComputed + && mb == mMarginBottomComputed + && r == mMarginRelativeComputed) + { + return; + } + mMarginLeftComputed = ml; + mMarginTopComputed = mt; + mMarginRightComputed = mr; + mMarginBottomComputed = mb; + mMarginRelativeComputed = r; + +#if UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + int width = (int)(Screen.width - (ml + mr)); + int height = (int)(Screen.height - (mb + mt)); + _CWebViewPlugin_SetRect(webView, width, height); + rect = new Rect(left, bottom, width, height); + UpdateBGTransform(); +#elif UNITY_WEBPLAYER + Application.ExternalCall("unityWebView.setMargins", name, (int)ml, (int)mt, (int)mr, (int)mb); +#elif UNITY_WEBGL && !UNITY_EDITOR + _gree_unity_webview_setMargins(name, (int)ml, (int)mt, (int)mr, (int)mb); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + int width = (int)(Screen.width - (ml + mr)); + int height = (int)(Screen.height - (mb + mt)); + _CWebViewPlugin_SetRect(webView, width, height); + rect = new Rect(left, bottom, width, height); + UpdateBGTransform(); +#elif UNITY_IPHONE + _CWebViewPlugin_SetMargins(webView, ml, mt, mr, mb, r); +#elif UNITY_ANDROID + webView.Call("SetMargins", (int)ml, (int)mt, (int)mr, (int)mb); +#endif + } + + public void SetVisibility(bool v) + { +#if UNITY_EDITOR || UNITY_STANDALONE + if (bg != null) + { + bg.gameObject.SetActive(v); + } +#endif + if (GetVisibility() && !v) + { + EvaluateJS("if (document && document.activeElement) document.activeElement.blur();"); + } +#if UNITY_WEBGL +#if !UNITY_EDITOR + _gree_unity_webview_setVisibility(name, v); +#endif +#elif UNITY_WEBPLAYER + Application.ExternalCall("unityWebView.setVisibility", name, v); +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_SetVisibility(webView, v); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_SetVisibility(webView, v); +#elif UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_SetVisibility(webView, v); +#elif UNITY_ANDROID + if (webView == null) + return; + mVisibility = v; + webView.Call("SetVisibility", v); +#endif + visibility = v; + } + + public bool GetVisibility() + { + return visibility; + } + + public void SetScrollbarsVisibility(bool v) + { +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + // TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + // TODO: WebView2 not implemented in native plugin +#elif UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_SetScrollbarsVisibility(webView, v); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("SetScrollbarsVisibility", v); +#else + // TODO: UNSUPPORTED +#endif + } + + public void SetInteractionEnabled(bool enabled) + { +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + // TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + // TODO: WebView2 not implemented in native plugin +#elif UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_SetInteractionEnabled(webView, enabled); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("SetInteractionEnabled", enabled); +#else + // TODO: UNSUPPORTED +#endif + } + + public void SetGoogleAppRedirectionEnabled(bool enabled) + { +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + // TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + // TODO: WebView2 not implemented in native plugin +#elif UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_SetGoogleAppRedirectionEnabled(webView, enabled); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("SetGoogleAppRedirectionEnabled", enabled); +#else + // TODO: UNSUPPORTED +#endif + } + + public void SetAlertDialogEnabled(bool e) + { +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + // TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + // TODO: WebView2 not implemented in native plugin +#elif UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_SetAlertDialogEnabled(webView, e); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("SetAlertDialogEnabled", e); +#else + // TODO: UNSUPPORTED +#endif + alertDialogEnabled = e; + } + + public bool GetAlertDialogEnabled() + { + return alertDialogEnabled; + } + + public void SetScrollBounceEnabled(bool e) + { +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + // TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + // TODO: WebView2 not implemented in native plugin +#elif UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_SetScrollBounceEnabled(webView, e); +#elif UNITY_ANDROID + // TODO: UNSUPPORTED +#else + // TODO: UNSUPPORTED +#endif + scrollBounceEnabled = e; + } + + public bool GetScrollBounceEnabled() + { + return scrollBounceEnabled; + } + + public void SetCameraAccess(bool allowed) + { +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + // TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + // TODO: WebView2 not implemented in native plugin +#elif UNITY_IPHONE + // TODO: UNSUPPORTED +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("SetCameraAccess", allowed); +#else + // TODO: UNSUPPORTED +#endif + } + + public void SetMicrophoneAccess(bool allowed) + { +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + // TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + // TODO: WebView2 not implemented in native plugin +#elif UNITY_IPHONE + // TODO: UNSUPPORTED +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("SetMicrophoneAccess", allowed); +#else + // TODO: UNSUPPORTED +#endif + } + + public bool SetURLPattern(string allowPattern, string denyPattern, string hookPattern) + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED + return false; +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED + return false; +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return false; + return _CWebViewPlugin_SetURLPattern(webView, allowPattern, denyPattern, hookPattern); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return false; + return _CWebViewPlugin_SetURLPattern(webView, allowPattern, denyPattern, hookPattern); +#elif UNITY_ANDROID + if (webView == null) + return false; + return webView.Call("SetURLPattern", allowPattern, denyPattern, hookPattern); +#endif + } + + public void LoadURL(string url) + { + if (string.IsNullOrEmpty(url)) + return; +#if UNITY_WEBGL +#if !UNITY_EDITOR + _gree_unity_webview_loadURL(name, url); +#endif +#elif UNITY_WEBPLAYER + Application.ExternalCall("unityWebView.loadURL", name, url); +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_LoadURL(webView, url); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_LoadURL(webView, url); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("LoadURL", url); +#endif + } + + public void LoadHTML(string html, string baseUrl) + { + if (string.IsNullOrEmpty(html)) + return; + if (string.IsNullOrEmpty(baseUrl)) + baseUrl = ""; +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_LoadHTML(webView, html, baseUrl); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_LoadHTML(webView, html, baseUrl); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("LoadHTML", html, baseUrl); +#endif + } + + public void EvaluateJS(string js) + { +#if UNITY_WEBGL +#if !UNITY_EDITOR + _gree_unity_webview_evaluateJS(name, js); +#endif +#elif UNITY_WEBPLAYER + Application.ExternalCall("unityWebView.evaluateJS", name, js); +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_EvaluateJS(webView, js); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_EvaluateJS(webView, js); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("EvaluateJS", js); +#endif + } + + public int Progress() + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED + return 0; +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED + return 0; +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return 0; + return _CWebViewPlugin_Progress(webView); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return 0; + return _CWebViewPlugin_Progress(webView); +#elif UNITY_ANDROID + if (webView == null) + return 0; + return webView.Get("progress"); +#endif + } + + public bool CanGoBack() + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED + return false; +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED + return false; +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return false; + return _CWebViewPlugin_CanGoBack(webView); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return false; + return _CWebViewPlugin_CanGoBack(webView); +#elif UNITY_ANDROID + if (webView == null) + return false; + return webView.Get("canGoBack"); +#endif + } + + public bool CanGoForward() + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED + return false; +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED + return false; +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return false; + return _CWebViewPlugin_CanGoForward(webView); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return false; + return _CWebViewPlugin_CanGoForward(webView); +#elif UNITY_ANDROID + if (webView == null) + return false; + return webView.Get("canGoForward"); +#endif + } + + public void GoBack() + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_GoBack(webView); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_GoBack(webView); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("GoBack"); +#endif + } + + public void GoForward() + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_GoForward(webView); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_GoForward(webView); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("GoForward"); +#endif + } + + public void Reload() + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_Reload(webView); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_Reload(webView); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("Reload"); +#endif + } + + public void CallOnError(string error) + { + if (onError != null) + { + onError(error); + } + } + + public void CallOnHttpError(string error) + { + if (onHttpError != null) + { + onHttpError(error); + } + } + + public void CallOnStarted(string url) + { + if (onStarted != null) + { + onStarted(url); + } + } + + public void CallOnLoaded(string url) + { + if (onLoaded != null) + { + onLoaded(url); + } + } + + public void CallFromJS(string message) + { + if (onJS != null) + { +#if !UNITY_ANDROID +#if UNITY_2018_4_OR_NEWER + message = UnityWebRequest.UnEscapeURL(message); +#else // UNITY_2018_4_OR_NEWER + message = WWW.UnEscapeURL(message); +#endif // UNITY_2018_4_OR_NEWER +#endif // !UNITY_ANDROID + onJS(message); + } + } + + public void CallOnHooked(string message) + { + if (onHooked != null) + { +#if !UNITY_ANDROID +#if UNITY_2018_4_OR_NEWER + message = UnityWebRequest.UnEscapeURL(message); +#else // UNITY_2018_4_OR_NEWER + message = WWW.UnEscapeURL(message); +#endif // UNITY_2018_4_OR_NEWER +#endif // !UNITY_ANDROID + onHooked(message); + } + } + + public void CallOnCookies(string cookies) + { + if (onCookies != null) + { + onCookies(cookies); + } + } + + public void AddCustomHeader(string headerKey, string headerValue) + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_AddCustomHeader(webView, headerKey, headerValue); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_AddCustomHeader(webView, headerKey, headerValue); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("AddCustomHeader", headerKey, headerValue); +#endif + } + + public string GetCustomHeaderValue(string headerKey) + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED + return null; +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED + return null; +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return null; + return _CWebViewPlugin_GetCustomHeaderValue(webView, headerKey); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return null; + return _CWebViewPlugin_GetCustomHeaderValue(webView, headerKey); +#elif UNITY_ANDROID + if (webView == null) + return null; + return webView.Call("GetCustomHeaderValue", headerKey); +#endif + } + + public void RemoveCustomHeader(string headerKey) + { +#if UNITY_WEBPLAYER || UNITY_WEBGL +#elif UNITY_EDITOR_LINUX || UNITY_SERVER +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_RemoveCustomHeader(webView, headerKey); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_RemoveCustomHeader(webView, headerKey); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("RemoveCustomHeader", headerKey); +#endif + } + + public void ClearCustomHeader() + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_ClearCustomHeader(webView); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_ClearCustomHeader(webView); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("ClearCustomHeader"); +#endif + } + + public void ClearCookie(string url, string name) + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + _CWebViewPlugin_ClearCookie(url, name); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_ClearCookie(url, name); +#elif UNITY_ANDROID && !UNITY_EDITOR + if (webView == null) + return; + webView.Call("ClearCookie", url, name); +#endif + } + + public void ClearCookies() + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + _CWebViewPlugin_ClearCookies(); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_ClearCookies(); +#elif UNITY_ANDROID && !UNITY_EDITOR + if (webView == null) + return; + webView.Call("ClearCookies"); +#endif + } + + + public void SaveCookies() + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + _CWebViewPlugin_SaveCookies(); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_SaveCookies(); +#elif UNITY_ANDROID && !UNITY_EDITOR + if (webView == null) + return; + webView.Call("SaveCookies"); +#endif + } + + + public void GetCookies(string url) + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_GetCookies(webView, url); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_GetCookies(webView, url); +#elif UNITY_ANDROID && !UNITY_EDITOR + if (webView == null) + return; + webView.Call("GetCookies", url); +#else + //TODO: UNSUPPORTED +#endif + } + + public void SetBasicAuthInfo(string userName, string password) + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + //TODO: WebView2 basic auth not implemented in native plugin +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + //TODO: UNSUPPORTED +#elif UNITY_IPHONE + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_SetBasicAuthInfo(webView, userName, password); +#elif UNITY_ANDROID + if (webView == null) + return; + webView.Call("SetBasicAuthInfo", userName, password); +#endif + } + + public void ClearCache(bool includeDiskFiles) + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + //TODO: WebView2 cache clear not implemented in native plugin +#elif UNITY_IPHONE && !UNITY_EDITOR + if (webView == IntPtr.Zero) + return; + _CWebViewPlugin_ClearCache(webView, includeDiskFiles); +#elif UNITY_ANDROID && !UNITY_EDITOR + if (webView == null) + return; + webView.Call("ClearCache", includeDiskFiles); +#endif + } + + + public void SetTextZoom(int textZoom) + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + //TODO: WebView2 text zoom not implemented in native plugin +#elif UNITY_IPHONE && !UNITY_EDITOR + //TODO: UNSUPPORTED +#elif UNITY_ANDROID && !UNITY_EDITOR + if (webView == null) + return; + webView.Call("SetTextZoom", textZoom); +#endif + } + + public void SetMixedContentMode(int mode) // 0: MIXED_CONTENT_ALWAYS_ALLOW, 1: MIXED_CONTENT_NEVER_ALLOW, 2: MIXED_CONTENT_COMPATIBILITY_MODE + { +#if UNITY_WEBPLAYER || UNITY_WEBGL + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_LINUX || UNITY_SERVER + //TODO: UNSUPPORTED +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + //TODO: WebView2 mixed content mode not implemented in native plugin +#elif UNITY_IPHONE && !UNITY_EDITOR + //TODO: UNSUPPORTED +#elif UNITY_ANDROID && !UNITY_EDITOR + if (webView == null) + return; + webView.Call("SetMixedContentMode", mode); +#endif + } + +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + void OnApplicationFocus(bool focus) + { + if (!focus) + { + hasFocus = false; + } + } + + void Start() + { + if (canvas != null) + { + var g = new GameObject(gameObject.name + "BG"); + g.transform.parent = canvas.transform; + bg = g.AddComponent(); + UpdateBGTransform(); + } + } + + void Update() + { + if (bg != null) { + bg.transform.SetAsLastSibling(); + } + if (hasFocus) { + inputString += Input.inputString; + } + for (;;) { + if (webView == IntPtr.Zero) + break; + string s = _CWebViewPlugin_GetMessage(webView); + if (s == null) + break; + var i = s.IndexOf(':', 0); + if (i == -1) + continue; + switch (s.Substring(0, i)) { + case "CallFromJS": + CallFromJS(s.Substring(i + 1)); + break; + case "CallOnError": + CallOnError(s.Substring(i + 1)); + break; + case "CallOnHttpError": + CallOnHttpError(s.Substring(i + 1)); + break; + case "CallOnLoaded": + CallOnLoaded(s.Substring(i + 1)); + break; + case "CallOnStarted": + CallOnStarted(s.Substring(i + 1)); + break; + case "CallOnHooked": + CallOnHooked(s.Substring(i + 1)); + break; + case "CallOnCookies": + CallOnCookies(s.Substring(i + 1)); + break; + } + } + if (webView == IntPtr.Zero || !visibility) + return; + bool refreshBitmap = (Time.frameCount % bitmapRefreshCycle == 0); + _CWebViewPlugin_Update(webView, refreshBitmap, devicePixelRatio); + if (refreshBitmap) { + var w = _CWebViewPlugin_BitmapWidth(webView); + var h = _CWebViewPlugin_BitmapHeight(webView); + var f = (_CWebViewPlugin_BitmapARGB(webView)) ? TextureFormat.ARGB32 : TextureFormat.RGBA32; + if (w > 0 && h > 0) { + if (texture == null || texture.width != w || texture.height != h) { + bool isLinearSpace = QualitySettings.activeColorSpace == ColorSpace.Linear; + texture = new Texture2D(w, h, f, false, !isLinearSpace); + texture.filterMode = FilterMode.Bilinear; + texture.wrapMode = TextureWrapMode.Clamp; +#if UNITY_2018_2_OR_NEWER +#else + textureDataBuffer = new byte[w * h * 4]; +#endif + } + if (texture != null) { +#if UNITY_2018_2_OR_NEWER + var ptr = _CWebViewPlugin_Render(webView, IntPtr.Zero); + if (ptr != IntPtr.Zero) { + texture.LoadRawTextureData(ptr, w * h * 4); + texture.Apply(); + } +#else + var gch = GCHandle.Alloc(textureDataBuffer, GCHandleType.Pinned); + _CWebViewPlugin_Render(webView, gch.AddrOfPinnedObject()); + gch.Free(); + texture.LoadRawTextureData(textureDataBuffer); + texture.Apply(); +#endif + } + } + } + } + + void UpdateBGTransform() + { + if (bg != null) { + bg.rectTransform.anchorMin = Vector2.zero; + bg.rectTransform.anchorMax = Vector2.zero; + bg.rectTransform.pivot = Vector2.zero; + bg.rectTransform.position = rect.min; + bg.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, rect.size.x); + bg.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, rect.size.y); + } + } + + public int bitmapRefreshCycle = 1; + public int devicePixelRatio = 1; + + void OnGUI() + { + if (webView == IntPtr.Zero || !visibility) + return; + switch (Event.current.type) { + case EventType.MouseDown: + case EventType.MouseUp: + hasFocus = rect.Contains(Input.mousePosition); + break; + } + switch (Event.current.type) { + case EventType.MouseMove: + case EventType.MouseDown: + case EventType.MouseDrag: + case EventType.MouseUp: + case EventType.ScrollWheel: + if (hasFocus) { + Vector3 p; + p.x = Input.mousePosition.x - rect.x; + p.y = Input.mousePosition.y - rect.y; + { + int mouseState = 0; + if (Input.GetButtonDown("Fire1")) { + mouseState = 1; + } else if (Input.GetButton("Fire1")) { + mouseState = 2; + } else if (Input.GetButtonUp("Fire1")) { + mouseState = 3; + } + //_CWebViewPlugin_SendMouseEvent(webView, (int)p.x, (int)p.y, Input.GetAxis("Mouse ScrollWheel"), mouseState); + _CWebViewPlugin_SendMouseEvent(webView, (int)p.x, (int)p.y, Input.mouseScrollDelta.y, mouseState); + } + } + break; + case EventType.Repaint: + while (!string.IsNullOrEmpty(inputString)) { + var keyChars = inputString.Substring(0, 1); + var keyCode = (ushort)inputString[0]; + inputString = inputString.Substring(1); + if (!string.IsNullOrEmpty(keyChars) || keyCode != 0) { + Vector3 p; + p.x = Input.mousePosition.x - rect.x; + p.y = Input.mousePosition.y - rect.y; + _CWebViewPlugin_SendKeyEvent(webView, (int)p.x, (int)p.y, keyChars, keyCode, 1); + } + } + if (texture != null) { + Matrix4x4 m = GUI.matrix; + GUI.matrix + = Matrix4x4.TRS( + new Vector3(0, Screen.height, 0), + Quaternion.identity, + new Vector3(1, -1, 1)); + Graphics.DrawTexture(rect, texture); + GUI.matrix = m; + } + break; + } + } +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + void OnApplicationFocus(bool focus) + { + if (!focus) + { + hasFocus = false; + } + } + + void Start() + { + if (canvas != null) + { + var g = new GameObject(gameObject.name + "BG"); + g.transform.parent = canvas.transform; + bg = g.AddComponent(); + UpdateBGTransform(); + } + } + + void Update() + { + if (bg != null) + { + bg.transform.SetAsLastSibling(); + } + if (hasFocus) + { + inputString += Input.inputString; + } + for (;;) + { + if (webView == IntPtr.Zero) + break; + string s = _CWebViewPlugin_GetMessage(webView); + if (s == null) + break; + var i = s.IndexOf(':', 0); + if (i == -1) + continue; + switch (s.Substring(0, i)) + { + case "CallFromJS": + CallFromJS(s.Substring(i + 1)); + break; + case "CallOnError": + CallOnError(s.Substring(i + 1)); + break; + case "CallOnHttpError": + CallOnHttpError(s.Substring(i + 1)); + break; + case "CallOnLoaded": + CallOnLoaded(s.Substring(i + 1)); + break; + case "CallOnStarted": + CallOnStarted(s.Substring(i + 1)); + break; + case "CallOnHooked": + CallOnHooked(s.Substring(i + 1)); + break; + case "CallOnCookies": + CallOnCookies(s.Substring(i + 1)); + break; + } + } + if (webView == IntPtr.Zero || !visibility) + return; + bool refreshBitmap = (Time.frameCount % bitmapRefreshCycle == 0); + _CWebViewPlugin_Update(webView, refreshBitmap, devicePixelRatio); + if (refreshBitmap) + { + var w = _CWebViewPlugin_BitmapWidth(webView); + var h = _CWebViewPlugin_BitmapHeight(webView); + if (w > 0 && h > 0) + { + if (texture == null || texture.width != w || texture.height != h) + { + bool isLinearSpace = QualitySettings.activeColorSpace == ColorSpace.Linear; + texture = new Texture2D(w, h, TextureFormat.RGBA32, false, !isLinearSpace); + texture.filterMode = FilterMode.Bilinear; + texture.wrapMode = TextureWrapMode.Clamp; + textureDataBuffer = new byte[w * h * 4]; + } + if (textureDataBuffer != null && textureDataBuffer.Length > 0) + { + var gch = GCHandle.Alloc(textureDataBuffer, GCHandleType.Pinned); + _CWebViewPlugin_Render(webView, gch.AddrOfPinnedObject()); + gch.Free(); + texture.LoadRawTextureData(textureDataBuffer); + texture.Apply(); + } + } + } + } + + void UpdateBGTransform() + { + if (bg != null) + { + bg.rectTransform.anchorMin = Vector2.zero; + bg.rectTransform.anchorMax = Vector2.zero; + bg.rectTransform.pivot = Vector2.zero; + bg.rectTransform.position = rect.min; + bg.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, rect.size.x); + bg.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, rect.size.y); + } + } + + // On Windows, CapturePreview is heavy; default 10 = refresh every 10th frame for better FPS. + public int bitmapRefreshCycle = 10; + public int devicePixelRatio = 1; + + void OnGUI() + { + if (webView == IntPtr.Zero || !visibility) + return; + switch (Event.current.type) + { + case EventType.MouseDown: + case EventType.MouseUp: + hasFocus = rect.Contains(Input.mousePosition); + break; + } + switch (Event.current.type) + { + case EventType.MouseMove: + case EventType.MouseDown: + case EventType.MouseDrag: + case EventType.MouseUp: + case EventType.ScrollWheel: + if (hasFocus) + { + Vector3 p; + p.x = Input.mousePosition.x - rect.x; + p.y = Input.mousePosition.y - rect.y; + int mouseState = 0; + if (Input.GetButtonDown("Fire1")) + mouseState = 1; + else if (Input.GetButton("Fire1")) + mouseState = 2; + else if (Input.GetButtonUp("Fire1")) + mouseState = 3; + _CWebViewPlugin_SendMouseEvent(webView, (int)p.x, (int)p.y, Input.mouseScrollDelta.y, mouseState); + } + break; + case EventType.Repaint: + while (!string.IsNullOrEmpty(inputString)) + { + var keyChars = inputString.Substring(0, 1); + var keyCode = (ushort)inputString[0]; + inputString = inputString.Substring(1); + if (!string.IsNullOrEmpty(keyChars) || keyCode != 0) + { + Vector3 p; + p.x = Input.mousePosition.x - rect.x; + p.y = Input.mousePosition.y - rect.y; + _CWebViewPlugin_SendKeyEvent(webView, (int)p.x, (int)p.y, keyChars, keyCode, 1); + } + } + if (texture != null) + { + Matrix4x4 m = GUI.matrix; + GUI.matrix = Matrix4x4.TRS(new Vector3(0, Screen.height, 0), Quaternion.identity, new Vector3(1, -1, 1)); + Graphics.DrawTexture(rect, texture); + GUI.matrix = m; + } + break; + } + } +#endif + } +} diff --git a/Packages/com.bywaystudios.webview/Runtime/WebViewObject.cs.meta b/Packages/com.bywaystudios.webview/Runtime/WebViewObject.cs.meta new file mode 100644 index 0000000..3294863 --- /dev/null +++ b/Packages/com.bywaystudios.webview/Runtime/WebViewObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d4d2b188f50df4b299eb714ef4360ee9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.webview/package.json b/Packages/com.bywaystudios.webview/package.json new file mode 100644 index 0000000..12fd217 --- /dev/null +++ b/Packages/com.bywaystudios.webview/package.json @@ -0,0 +1,10 @@ +{ + "name": "com.bywaystudios.webview", + "displayName": "Webview", + "version": "0.1.0", + "dependencies": { + "com.unity.ugui": "1.0.0", + "com.bywaystudios.unitask": "2.5.10-exp.2" + }, + "description": "Package for Displaying WebView in Unity" +} \ No newline at end of file diff --git a/Packages/com.bywaystudios.webview/package.json.meta b/Packages/com.bywaystudios.webview/package.json.meta new file mode 100644 index 0000000..64877ef --- /dev/null +++ b/Packages/com.bywaystudios.webview/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b9d2b33afe0c2a74f9f755375e3441a2 +PackageManifestImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/manifest.json b/Packages/manifest.json new file mode 100644 index 0000000..1af8560 --- /dev/null +++ b/Packages/manifest.json @@ -0,0 +1,57 @@ +{ + "dependencies": { + "com.unity.asset-store-validation": "0.6.0", + "com.unity.collab-proxy": "2.11.3", + "com.unity.editorcoroutines": "1.0.1", + "com.unity.feature.development": "1.0.1", + "com.unity.ide.rider": "3.0.38", + "com.unity.ide.visualstudio": "2.0.25", + "com.unity.ide.vscode": "1.2.5", + "com.unity.nuget.newtonsoft-json": "3.2.2", + "com.unity.performance.profile-analyzer": "1.2.4", + "com.unity.settings-manager": "2.1.1", + "com.unity.test-framework": "1.1.33", + "com.unity.testtools.codecoverage": "1.2.7", + "com.unity.upm.develop": "0.5.3-exp.1", + "com.unity.modules.ai": "1.0.0", + "com.unity.modules.androidjni": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.cloth": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.screencapture": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.umbra": "1.0.0", + "com.unity.modules.unityanalytics": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vehicles": "1.0.0", + "com.unity.modules.video": "1.0.0", + "com.unity.modules.vr": "1.0.0", + "com.unity.modules.wind": "1.0.0", + "com.unity.modules.xr": "1.0.0" + }, + "scopedRegistries": [ + { + "name": "BywayStudios", + "url": "https://npm.bywaystudios.com", + "scopes": [ + "com.bywaystudios" + ] + } + ] +} diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json new file mode 100644 index 0000000..636aec5 --- /dev/null +++ b/Packages/packages-lock.json @@ -0,0 +1,394 @@ +{ + "dependencies": { + "com.bywaystudios.unitask": { + "version": "2.5.10-exp.2", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://npm.bywaystudios.com" + }, + "com.bywaystudios.webview": { + "version": "file:com.bywaystudios.webview", + "depth": 0, + "source": "embedded", + "dependencies": { + "com.unity.ugui": "1.0.0", + "com.bywaystudios.unitask": "2.5.10-exp.2" + } + }, + "com.unity.asset-store-validation": { + "version": "0.6.0", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.nuget.newtonsoft-json": "2.0.2" + }, + "url": "https://packages.unity.com" + }, + "com.unity.collab-proxy": { + "version": "2.11.3", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.editorcoroutines": { + "version": "1.0.1", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.ext.nunit": { + "version": "1.0.6", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.feature.development": { + "version": "1.0.1", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.ide.visualstudio": "2.0.22", + "com.unity.ide.rider": "3.0.36", + "com.unity.ide.vscode": "1.2.5", + "com.unity.editorcoroutines": "1.0.0", + "com.unity.performance.profile-analyzer": "1.2.3", + "com.unity.test-framework": "1.1.33", + "com.unity.testtools.codecoverage": "1.2.6" + } + }, + "com.unity.ide.rider": { + "version": "3.0.38", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ide.visualstudio": { + "version": "2.0.25", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.31" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ide.vscode": { + "version": "1.2.5", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.nuget.newtonsoft-json": { + "version": "3.2.2", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.performance.profile-analyzer": { + "version": "1.2.4", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.settings-manager": { + "version": "2.1.1", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.test-framework": { + "version": "1.1.33", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.testtools.codecoverage": { + "version": "1.2.7", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.0.16", + "com.unity.settings-manager": "1.0.1" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ugui": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0" + } + }, + "com.unity.upm.develop": { + "version": "0.5.3-exp.1", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.20", + "com.unity.nuget.newtonsoft-json": "2.0.2", + "com.unity.asset-store-validation": "0.1.3" + }, + "url": "https://packages.unity.com" + }, + "com.unity.modules.ai": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.androidjni": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.animation": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.assetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.audio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.cloth": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.director": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.animation": "1.0.0" + } + }, + "com.unity.modules.imageconversion": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.imgui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.jsonserialize": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.particlesystem": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics2d": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.screencapture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.subsystems": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.terrain": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.terrainphysics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.terrain": "1.0.0" + } + }, + "com.unity.modules.tilemap": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics2d": "1.0.0" + } + }, + "com.unity.modules.ui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.uielements": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.umbra": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unityanalytics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.unitywebrequest": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unitywebrequestassetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestaudio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.audio": "1.0.0" + } + }, + "com.unity.modules.unitywebrequesttexture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestwww": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.vehicles": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.video": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.vr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } + }, + "com.unity.modules.wind": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.xr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.subsystems": "1.0.0" + } + } + } +} diff --git a/ProjectSettings/AudioManager.asset b/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..07ebfb0 --- /dev/null +++ b/ProjectSettings/AudioManager.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 1024 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 + m_RequestedDSPBufferSize: 1024 diff --git a/ProjectSettings/ClusterInputManager.asset b/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000..e7886b2 --- /dev/null +++ b/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/ProjectSettings/DynamicsManager.asset b/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..cdc1f3e --- /dev/null +++ b/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,34 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0 + m_ClothInterCollisionStiffness: 0 + m_ContactsGeneration: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 + m_AutoSyncTransforms: 0 + m_ReuseCollisionCallbacks: 1 + m_ClothInterCollisionSettingsToggle: 0 + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_WorldBounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 250, y: 250, z: 250} + m_WorldSubdivisions: 8 + m_FrictionType: 0 + m_EnableEnhancedDeterminism: 0 + m_EnableUnifiedHeightmaps: 1 + m_DefaultMaxAngluarSpeed: 7 diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..0147887 --- /dev/null +++ b/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: [] + m_configObjects: {} diff --git a/ProjectSettings/EditorSettings.asset b/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..1e44a0a --- /dev/null +++ b/ProjectSettings/EditorSettings.asset @@ -0,0 +1,30 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_ExternalVersionControlSupport: Visible Meta Files + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 0 + m_DefaultBehaviorMode: 0 + m_PrefabRegularEnvironment: {fileID: 0} + m_PrefabUIEnvironment: {fileID: 0} + m_SpritePackerMode: 0 + m_SpritePackerPaddingPower: 1 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp;asmref + m_ProjectGenerationRootNamespace: + m_CollabEditorSettings: + inProgressEnabled: 1 + m_EnableTextureStreamingInEditMode: 1 + m_EnableTextureStreamingInPlayMode: 1 + m_AsyncShaderCompilation: 1 + m_EnterPlayModeOptionsEnabled: 0 + m_EnterPlayModeOptions: 3 + m_ShowLightmapResolutionOverlay: 1 + m_UseLegacyProbeSampleCount: 0 + m_SerializeInlineMappingsOnOneLine: 1 diff --git a/ProjectSettings/GraphicsSettings.asset b/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000..43369e3 --- /dev/null +++ b/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,63 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_AlwaysIncludedShaders: + - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, + type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 + m_LogWhenShaderIsCompiled: 0 + m_AllowEnlightenSupportForUpgradedProject: 0 diff --git a/ProjectSettings/InputManager.asset b/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..17c8f53 --- /dev/null +++ b/ProjectSettings/InputManager.asset @@ -0,0 +1,295 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 diff --git a/ProjectSettings/MemorySettings.asset b/ProjectSettings/MemorySettings.asset new file mode 100644 index 0000000..5b5face --- /dev/null +++ b/ProjectSettings/MemorySettings.asset @@ -0,0 +1,35 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!387306366 &1 +MemorySettings: + m_ObjectHideFlags: 0 + m_EditorMemorySettings: + m_MainAllocatorBlockSize: -1 + m_ThreadAllocatorBlockSize: -1 + m_MainGfxBlockSize: -1 + m_ThreadGfxBlockSize: -1 + m_CacheBlockSize: -1 + m_TypetreeBlockSize: -1 + m_ProfilerBlockSize: -1 + m_ProfilerEditorBlockSize: -1 + m_BucketAllocatorGranularity: -1 + m_BucketAllocatorBucketsCount: -1 + m_BucketAllocatorBlockSize: -1 + m_BucketAllocatorBlockCount: -1 + m_ProfilerBucketAllocatorGranularity: -1 + m_ProfilerBucketAllocatorBucketsCount: -1 + m_ProfilerBucketAllocatorBlockSize: -1 + m_ProfilerBucketAllocatorBlockCount: -1 + m_TempAllocatorSizeMain: -1 + m_JobTempAllocatorBlockSize: -1 + m_BackgroundJobTempAllocatorBlockSize: -1 + m_JobTempAllocatorReducedBlockSize: -1 + m_TempAllocatorSizeGIBakingWorker: -1 + m_TempAllocatorSizeNavMeshWorker: -1 + m_TempAllocatorSizeAudioWorker: -1 + m_TempAllocatorSizeCloudWorker: -1 + m_TempAllocatorSizeGfx: -1 + m_TempAllocatorSizeJobWorker: -1 + m_TempAllocatorSizeBackgroundWorker: -1 + m_TempAllocatorSizePreloadManager: -1 + m_PlatformMemorySettings: {} diff --git a/ProjectSettings/NavMeshAreas.asset b/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000..3b0b7c3 --- /dev/null +++ b/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,91 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_SettingNames: + - Humanoid diff --git a/ProjectSettings/PackageManagerSettings.asset b/ProjectSettings/PackageManagerSettings.asset new file mode 100644 index 0000000..4503659 --- /dev/null +++ b/ProjectSettings/PackageManagerSettings.asset @@ -0,0 +1,44 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_EnablePreReleasePackages: 0 + m_AdvancedSettingsExpanded: 1 + m_ScopedRegistriesSettingsExpanded: 1 + m_SeeAllPackageVersions: 0 + m_DismissPreviewPackagesInUse: 0 + oneTimeWarningShown: 0 + m_Registries: + - m_Id: main + m_Name: + m_Url: https://packages.unity.com + m_Scopes: [] + m_IsDefault: 1 + m_Capabilities: 7 + m_ConfigSource: 0 + - m_Id: scoped:project:BywayStudios + m_Name: BywayStudios + m_Url: https://npm.bywaystudios.com + m_Scopes: + - com.bywaystudios + m_IsDefault: 0 + m_Capabilities: 0 + m_ConfigSource: 4 + m_UserSelectedRegistryName: BywayStudios + m_UserAddingNewScopedRegistry: 0 + m_RegistryInfoDraft: + m_Modified: 0 + m_ErrorMessage: + m_UserModificationsInstanceId: -834 + m_OriginalInstanceId: -836 + m_LoadAssets: 0 diff --git a/ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json b/ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json new file mode 100644 index 0000000..3c7b4c1 --- /dev/null +++ b/ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json @@ -0,0 +1,5 @@ +{ + "m_Dictionary": { + "m_DictionaryValues": [] + } +} \ No newline at end of file diff --git a/ProjectSettings/Physics2DSettings.asset b/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000..47880b1 --- /dev/null +++ b/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,56 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 4 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_JobOptions: + serializedVersion: 2 + useMultithreading: 0 + useConsistencySorting: 0 + m_InterpolationPosesPerJob: 100 + m_NewContactsPerJob: 30 + m_CollideContactsPerJob: 100 + m_ClearFlagsPerJob: 200 + m_ClearBodyForcesPerJob: 200 + m_SyncDiscreteFixturesPerJob: 50 + m_SyncContinuousFixturesPerJob: 50 + m_FindNearestContactsPerJob: 100 + m_UpdateTriggerContactsPerJob: 100 + m_IslandSolverCostThreshold: 100 + m_IslandSolverBodyCostScale: 1 + m_IslandSolverContactCostScale: 10 + m_IslandSolverJointCostScale: 10 + m_IslandSolverBodiesPerJob: 50 + m_IslandSolverContactsPerJob: 50 + m_AutoSimulation: 1 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_CallbacksOnDisable: 1 + m_ReuseCollisionCallbacks: 1 + m_AutoSyncTransforms: 0 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/ProjectSettings/PresetManager.asset b/ProjectSettings/PresetManager.asset new file mode 100644 index 0000000..67a94da --- /dev/null +++ b/ProjectSettings/PresetManager.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_DefaultPresets: {} diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..f61a26c --- /dev/null +++ b/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,961 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 26 + productGUID: 8036187eb61cd4c48bf408720f976698 + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: WebviewPackage + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1920 + defaultScreenHeight: 1080 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 1 + unsupportedMSAAFallback: 0 + m_SpriteBatchVertexThreshold: 300 + m_MTRendering: 1 + mipStripping: 0 + numberOfMipsStripped: 0 + numberOfMipsStrippedPerMipmapLimitGroup: {} + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + iosUseCustomAppBackgroundBehavior: 0 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 1 + androidUseSwappy: 1 + androidBlitType: 0 + androidResizableWindow: 0 + androidDefaultWindowWidth: 1920 + androidDefaultWindowHeight: 1080 + androidMinimumWindowWidth: 400 + androidMinimumWindowHeight: 300 + androidFullscreenMode: 1 + androidAutoRotationBehavior: 1 + androidPredictiveBackSupport: 1 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 1 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + audioSpatialExperience: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + dedicatedServerOptimizations: 0 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + useFlipModelSwapchain: 1 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 1 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + fullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 0 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + switchGpuScratchPoolGranularity: 2097152 + switchAllowGpuScratchShrinking: 0 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + switchNVNGraphicsFirmwareMemory: 32 + switchMaxWorkerMultiple: 8 + stadiaPresentMode: 0 + stadiaTargetFramerate: 0 + vulkanNumSwapchainBuffers: 3 + vulkanEnableSetSRGBWrite: 0 + vulkanEnablePreTransform: 1 + vulkanEnableLateAcquireNextImage: 0 + vulkanEnableCommandBufferRecycling: 1 + loadStoreDebugModeEnabled: 0 + visionOSBundleVersion: 1.0 + tvOSBundleVersion: 1.0 + bundleVersion: 0.1 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 1 + xboxOneEnable7thCore: 1 + vrSettings: + enable360StereoCapture: 0 + isWsaHolographicRemotingEnabled: 0 + enableFrameTimingStats: 0 + enableOpenGLProfilerGPURecorders: 1 + allowHDRDisplaySupport: 0 + useHDRDisplay: 0 + hdrBitDepth: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + resetResolutionOnWindowResize: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + applicationIdentifier: {} + buildNumber: + Standalone: 0 + VisionOS: 0 + iPhone: 0 + tvOS: 0 + overrideDefaultApplicationIdentifier: 0 + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 22 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 1 + strictShaderVariantMatching: 0 + VertexChannelCompressionMask: 4054 + iPhoneSdkVersion: 988 + iOSSimulatorArchitecture: 0 + iOSTargetOSVersionString: 12.0 + tvOSSdkVersion: 0 + tvOSSimulatorArchitecture: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 12.0 + VisionOSSdkVersion: 0 + VisionOSTargetOSVersionString: 1.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSLaunchScreenCustomStoryboardPath: + iOSLaunchScreeniPadCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + macOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + metalCompileShaderBinary: 0 + iOSRenderExtraFrameOnPause: 0 + iosCopyPluginsCodeInsteadOfSymlink: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + VisionOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + VisionOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + shaderPrecisionModel: 0 + clonedFromGUID: c0afd0d1d80e3634a9dac47e8a0426ea + templatePackageId: com.unity.template.packagetemplate@1.0.0 + templateDefaultScene: Assets/Scenes/SampleScene.unity + useCustomMainManifest: 0 + useCustomLauncherManifest: 0 + useCustomMainGradleTemplate: 0 + useCustomLauncherGradleManifest: 0 + useCustomBaseGradleTemplate: 0 + useCustomGradlePropertiesTemplate: 0 + useCustomGradleSettingsTemplate: 0 + useCustomProguardFile: 0 + AndroidTargetArchitectures: 1 + AndroidTargetDevices: 0 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidEnableArmv9SecurityFeatures: 0 + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 0 + AndroidIsGame: 1 + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + androidUseCustomKeystore: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + chromeosInputEmulation: 1 + AndroidMinifyRelease: 0 + AndroidMinifyDebug: 0 + AndroidValidateAppBundleSize: 1 + AndroidAppBundleSizeToValidate: 150 + m_BuildTargetIcons: [] + m_BuildTargetPlatformIcons: + - m_BuildTarget: iPhone + m_Icons: + - m_Textures: [] + m_Width: 180 + m_Height: 180 + m_Kind: 0 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 120 + m_Height: 120 + m_Kind: 0 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 167 + m_Height: 167 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 152 + m_Height: 152 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 76 + m_Height: 76 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 120 + m_Height: 120 + m_Kind: 3 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 80 + m_Height: 80 + m_Kind: 3 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 80 + m_Height: 80 + m_Kind: 3 + m_SubKind: iPad + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 3 + m_SubKind: iPad + - m_Textures: [] + m_Width: 87 + m_Height: 87 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 58 + m_Height: 58 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 29 + m_Height: 29 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 58 + m_Height: 58 + m_Kind: 1 + m_SubKind: iPad + - m_Textures: [] + m_Width: 29 + m_Height: 29 + m_Kind: 1 + m_SubKind: iPad + - m_Textures: [] + m_Width: 60 + m_Height: 60 + m_Kind: 2 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 2 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 2 + m_SubKind: iPad + - m_Textures: [] + m_Width: 20 + m_Height: 20 + m_Kind: 2 + m_SubKind: iPad + - m_Textures: [] + m_Width: 1024 + m_Height: 1024 + m_Kind: 4 + m_SubKind: App Store + - m_BuildTarget: Android + m_Icons: + - m_Textures: [] + m_Width: 432 + m_Height: 432 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 324 + m_Height: 324 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 216 + m_Height: 216 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 162 + m_Height: 162 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 108 + m_Height: 108 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 81 + m_Height: 81 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 192 + m_Height: 192 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 144 + m_Height: 144 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 96 + m_Height: 96 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 72 + m_Height: 72 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 48 + m_Height: 48 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 36 + m_Height: 36 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 192 + m_Height: 192 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 144 + m_Height: 144 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 96 + m_Height: 96 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 72 + m_Height: 72 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 48 + m_Height: 48 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 36 + m_Height: 36 + m_Kind: 0 + m_SubKind: + m_BuildTargetBatching: + - m_BuildTarget: Standalone + m_StaticBatching: 1 + m_DynamicBatching: 0 + - m_BuildTarget: tvOS + m_StaticBatching: 1 + m_DynamicBatching: 0 + - m_BuildTarget: Android + m_StaticBatching: 1 + m_DynamicBatching: 0 + - m_BuildTarget: iPhone + m_StaticBatching: 1 + m_DynamicBatching: 0 + - m_BuildTarget: WebGL + m_StaticBatching: 0 + m_DynamicBatching: 0 + m_BuildTargetShaderSettings: [] + m_BuildTargetGraphicsJobs: + - m_BuildTarget: MacStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: Switch + m_GraphicsJobs: 1 + - m_BuildTarget: MetroSupport + m_GraphicsJobs: 1 + - m_BuildTarget: AppleTVSupport + m_GraphicsJobs: 0 + - m_BuildTarget: BJMSupport + m_GraphicsJobs: 1 + - m_BuildTarget: LinuxStandaloneSupport + m_GraphicsJobs: 1 + - m_BuildTarget: PS4Player + m_GraphicsJobs: 1 + - m_BuildTarget: iOSSupport + m_GraphicsJobs: 0 + - m_BuildTarget: WindowsStandaloneSupport + m_GraphicsJobs: 1 + - m_BuildTarget: XboxOnePlayer + m_GraphicsJobs: 1 + - m_BuildTarget: LuminSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AndroidPlayer + m_GraphicsJobs: 0 + - m_BuildTarget: WebGLSupport + m_GraphicsJobs: 0 + m_BuildTargetGraphicsJobMode: + - m_BuildTarget: PS4Player + m_GraphicsJobMode: 0 + - m_BuildTarget: XboxOnePlayer + m_GraphicsJobMode: 0 + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: AndroidPlayer + m_APIs: 150000000b000000 + m_Automatic: 1 + - m_BuildTarget: iOSSupport + m_APIs: 10000000 + m_Automatic: 1 + - m_BuildTarget: AppleTVSupport + m_APIs: 10000000 + m_Automatic: 1 + - m_BuildTarget: WebGLSupport + m_APIs: 0b000000 + m_Automatic: 1 + m_BuildTargetVRSettings: + - m_BuildTarget: Standalone + m_Enabled: 0 + m_Devices: + - Oculus + - OpenVR + m_DefaultShaderChunkSizeInMB: 16 + m_DefaultShaderChunkCount: 0 + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + openGLRequireES32: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: + - m_BuildTarget: Android + m_EncodingQuality: 1 + - m_BuildTarget: iPhone + m_EncodingQuality: 1 + - m_BuildTarget: tvOS + m_EncodingQuality: 1 + m_BuildTargetGroupHDRCubemapEncodingQuality: + - m_BuildTarget: Android + m_EncodingQuality: 1 + - m_BuildTarget: iPhone + m_EncodingQuality: 1 + - m_BuildTarget: tvOS + m_EncodingQuality: 1 + m_BuildTargetGroupLightmapSettings: [] + m_BuildTargetGroupLoadStoreDebugModeSettings: [] + m_BuildTargetNormalMapEncoding: + - m_BuildTarget: Android + m_Encoding: 1 + - m_BuildTarget: iPhone + m_Encoding: 1 + - m_BuildTarget: tvOS + m_Encoding: 1 + m_BuildTargetDefaultTextureCompressionFormat: + - m_BuildTarget: Android + m_Format: 3 + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + bluetoothUsageDescription: + macOSTargetOSVersion: 10.13.0 + switchNMETAOverride: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchEnableFileSystemTrace: 0 + switchLTOSetting: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchCompilerFlags: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchTitleNames_15: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchPublisherNames_15: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchIcons_15: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchSmallIcons_15: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchRatingsInt_12: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 22 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchEnableTouchScreen: 1 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchDisableHTCSPlayerConnection: 0 + switchUseNewStyleFilepaths: 1 + switchUseLegacyFmodPriorities: 0 + switchUseMicroSleepForYield: 1 + switchEnableRamDiskSupport: 0 + switchMicroSleepForYieldTime: 25 + switchRamDiskSpaceSize: 12 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 1 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + playerPrefsMaxSize: 32768 + ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4UseLowGarlicFragmentationMode: 1 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 0 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4CompatibilityPS5: 0 + ps4AllowPS5Detection: 0 + ps4GPU800MHz: 1 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + ps4attribVROutputEnabled: 0 + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + blurSplashScreenBackground: 1 + spritePackerPolicy: + webGLMemorySize: 16 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLShowDiagnostics: 0 + webGLDataCaching: 1 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 1 + webGLWasmArithmeticExceptions: 0 + webGLLinkerTarget: 1 + webGLThreadsSupport: 0 + webGLDecompressionFallback: 0 + webGLInitialMemorySize: 32 + webGLMaximumMemorySize: 2048 + webGLMemoryGrowthMode: 2 + webGLMemoryLinearGrowthStep: 16 + webGLMemoryGeometricGrowthStep: 0.2 + webGLMemoryGeometricGrowthCap: 96 + webGLPowerPreference: 2 + scriptingDefineSymbols: {} + additionalCompilerArguments: {} + platformArchitecture: {} + scriptingBackend: {} + il2cppCompilerConfiguration: {} + il2cppCodeGeneration: {} + managedStrippingLevel: + EmbeddedLinux: 1 + GameCoreScarlett: 1 + GameCoreXboxOne: 1 + Nintendo Switch: 1 + PS4: 1 + PS5: 1 + QNX: 1 + Stadia: 1 + VisionOS: 1 + WebGL: 1 + Windows Store Apps: 1 + XboxOne: 1 + iPhone: 1 + tvOS: 1 + incrementalIl2cppBuild: {} + suppressCommonWarnings: 1 + allowUnsafeCode: 0 + useDeterministicCompilation: 1 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + gcIncremental: 1 + gcWBarrierValidation: 0 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: WebviewPackage + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: WebviewPackage + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} + metroSplashScreenUseBackgroundColor: 0 + syncCapabilities: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + vcxProjDefaultLanguage: + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnhancedXboxCompatibilityMode: 0 + XboxOneEnableGPUVariability: 1 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + XboxOneOverrideIdentityName: + XboxOneOverrideIdentityPublisher: + vrEditorSettings: {} + cloudServicesEnabled: + UNet: 1 + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_SignPackage: 1 + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + hmiPlayerDataPath: + hmiForceSRGBBlit: 1 + embeddedLinuxEnableGamepadInput: 1 + hmiLogStartupTiming: 0 + hmiCpuConfiguration: + apiCompatibilityLevel: 6 + activeInputHandler: 0 + windowsGamepadBackendHint: 0 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + qualitySettingsNames: [] + projectName: + organizationId: + cloudEnabled: 0 + legacyClampBlendShapeWeights: 0 + hmiLoadingImage: {fileID: 0} + platformRequiresReadableAssets: 0 + virtualTexturingSupportEnabled: 0 + insecureHttpOption: 0 diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..587f809 --- /dev/null +++ b/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 2022.3.62f3 +m_EditorVersionWithRevision: 2022.3.62f3 (96770f904ca7) diff --git a/ProjectSettings/QualitySettings.asset b/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..35c3439 --- /dev/null +++ b/ProjectSettings/QualitySettings.asset @@ -0,0 +1,320 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 5 + m_QualitySettings: + - serializedVersion: 3 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 1 + globalTextureMipmapLimit: 1 + textureMipmapLimitSettings: [] + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + useLegacyDetailDistribution: 1 + vSyncCount: 0 + realtimeGICPUUsage: 25 + lodBias: 0.3 + maximumLODLevel: 0 + enableLODCrossFade: 1 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + terrainQualityOverrides: 0 + terrainPixelError: 1 + terrainDetailDensityScale: 1 + terrainBasemapDistance: 1000 + terrainDetailDistance: 80 + terrainTreeDistance: 5000 + terrainBillboardStart: 50 + terrainFadeLength: 5 + terrainMaxTrees: 50 + excludedTargetPlatforms: [] + - serializedVersion: 3 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + globalTextureMipmapLimit: 0 + textureMipmapLimitSettings: [] + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + useLegacyDetailDistribution: 1 + vSyncCount: 0 + realtimeGICPUUsage: 25 + lodBias: 0.4 + maximumLODLevel: 0 + enableLODCrossFade: 1 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + terrainQualityOverrides: 0 + terrainPixelError: 1 + terrainDetailDensityScale: 1 + terrainBasemapDistance: 1000 + terrainDetailDistance: 80 + terrainTreeDistance: 5000 + terrainBillboardStart: 50 + terrainFadeLength: 5 + terrainMaxTrees: 50 + excludedTargetPlatforms: [] + - serializedVersion: 3 + name: Medium + pixelLightCount: 1 + shadows: 1 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + globalTextureMipmapLimit: 0 + textureMipmapLimitSettings: [] + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + useLegacyDetailDistribution: 1 + vSyncCount: 1 + realtimeGICPUUsage: 25 + lodBias: 0.7 + maximumLODLevel: 0 + enableLODCrossFade: 1 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + terrainQualityOverrides: 0 + terrainPixelError: 1 + terrainDetailDensityScale: 1 + terrainBasemapDistance: 1000 + terrainDetailDistance: 80 + terrainTreeDistance: 5000 + terrainBillboardStart: 50 + terrainFadeLength: 5 + terrainMaxTrees: 50 + excludedTargetPlatforms: [] + - serializedVersion: 3 + name: High + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 2 + globalTextureMipmapLimit: 0 + textureMipmapLimitSettings: [] + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + useLegacyDetailDistribution: 1 + vSyncCount: 1 + realtimeGICPUUsage: 50 + lodBias: 1 + maximumLODLevel: 0 + enableLODCrossFade: 1 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + terrainQualityOverrides: 0 + terrainPixelError: 1 + terrainDetailDensityScale: 1 + terrainBasemapDistance: 1000 + terrainDetailDistance: 80 + terrainTreeDistance: 5000 + terrainBillboardStart: 50 + terrainFadeLength: 5 + terrainMaxTrees: 50 + excludedTargetPlatforms: [] + - serializedVersion: 3 + name: Very High + pixelLightCount: 3 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 4 + globalTextureMipmapLimit: 0 + textureMipmapLimitSettings: [] + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + useLegacyDetailDistribution: 1 + vSyncCount: 1 + realtimeGICPUUsage: 50 + lodBias: 1.5 + maximumLODLevel: 0 + enableLODCrossFade: 1 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + terrainQualityOverrides: 0 + terrainPixelError: 1 + terrainDetailDensityScale: 1 + terrainBasemapDistance: 1000 + terrainDetailDistance: 80 + terrainTreeDistance: 5000 + terrainBillboardStart: 50 + terrainFadeLength: 5 + terrainMaxTrees: 50 + excludedTargetPlatforms: [] + - serializedVersion: 3 + name: Ultra + pixelLightCount: 4 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 4 + globalTextureMipmapLimit: 0 + textureMipmapLimitSettings: [] + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + useLegacyDetailDistribution: 1 + vSyncCount: 1 + realtimeGICPUUsage: 100 + lodBias: 2 + maximumLODLevel: 0 + enableLODCrossFade: 1 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + terrainQualityOverrides: 0 + terrainPixelError: 1 + terrainDetailDensityScale: 1 + terrainBasemapDistance: 1000 + terrainDetailDistance: 80 + terrainTreeDistance: 5000 + terrainBillboardStart: 50 + terrainFadeLength: 5 + terrainMaxTrees: 50 + excludedTargetPlatforms: [] + m_TextureMipmapLimitGroupNames: [] + m_PerPlatformDefaultQuality: + Android: 2 + GameCoreScarlett: 5 + GameCoreXboxOne: 5 + Lumin: 5 + Nintendo 3DS: 5 + Nintendo Switch: 5 + PS4: 5 + PS5: 5 + Server: 0 + Stadia: 5 + Standalone: 5 + WebGL: 3 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/ProjectSettings/SceneTemplateSettings.json b/ProjectSettings/SceneTemplateSettings.json new file mode 100644 index 0000000..5e97f83 --- /dev/null +++ b/ProjectSettings/SceneTemplateSettings.json @@ -0,0 +1,121 @@ +{ + "templatePinStates": [], + "dependencyTypeInfos": [ + { + "userAdded": false, + "type": "UnityEngine.AnimationClip", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.Animations.AnimatorController", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.AnimatorOverrideController", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.Audio.AudioMixerController", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.ComputeShader", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.Cubemap", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.GameObject", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.LightingDataAsset", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.LightingSettings", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Material", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.MonoScript", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.PhysicMaterial", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.PhysicsMaterial2D", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.PostProcessing.PostProcessResources", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.VolumeProfile", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.SceneAsset", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.Shader", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.ShaderVariantCollection", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.Texture", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Texture2D", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Timeline.TimelineAsset", + "defaultInstantiationMode": 0 + } + ], + "defaultDependencyTypeInfo": { + "userAdded": false, + "type": "", + "defaultInstantiationMode": 1 + }, + "newSceneOverride": 0 +} \ No newline at end of file diff --git a/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..1c92a78 --- /dev/null +++ b/ProjectSettings/TagManager.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/ProjectSettings/TimeManager.asset b/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..558a017 --- /dev/null +++ b/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.33333334 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/ProjectSettings/UnityConnectSettings.asset b/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 0000000..a88bee0 --- /dev/null +++ b/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 1 + m_Enabled: 0 + m_TestMode: 0 + m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events + m_EventUrl: https://cdp.cloud.unity3d.com/v1/events + m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_DashboardUrl: https://dashboard.unity3d.com + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity3d.com + m_Enabled: 0 + m_LogBufferSize: 10 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_TestMode: 0 + m_InitializeOnStartup: 1 + m_PackageRequiringCoreStatsPresent: 0 + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/ProjectSettings/VFXManager.asset b/ProjectSettings/VFXManager.asset new file mode 100644 index 0000000..3a95c98 --- /dev/null +++ b/ProjectSettings/VFXManager.asset @@ -0,0 +1,12 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!937362698 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_StripUpdateShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05 diff --git a/ProjectSettings/VersionControlSettings.asset b/ProjectSettings/VersionControlSettings.asset new file mode 100644 index 0000000..dca2881 --- /dev/null +++ b/ProjectSettings/VersionControlSettings.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!890905787 &1 +VersionControlSettings: + m_ObjectHideFlags: 0 + m_Mode: Visible Meta Files + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/ProjectSettings/XRSettings.asset b/ProjectSettings/XRSettings.asset new file mode 100644 index 0000000..482590c --- /dev/null +++ b/ProjectSettings/XRSettings.asset @@ -0,0 +1,10 @@ +{ + "m_SettingKeys": [ + "VR Device Disabled", + "VR Device User Alert" + ], + "m_SettingValues": [ + "False", + "False" + ] +} \ No newline at end of file