update: 升级 UniWebview 至 5.11.1
Signed-off-by: huyufei <yufei.hu@castbox.fm>
							parent
							
								
									0139e6e9f3
								
							
						
					
					
						commit
						957ac1a7d7
					
				|  | @ -1,8 +1,8 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 4920deff2cffa43c98b6c11f1d4c90fd | ||||
| guid: d3dad2b92748d439e851e10d4f4ecaf0 | ||||
| folderAsset: yes | ||||
| timeCreated: 1489325633 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| DefaultImporter: | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|  |  | |||
|  | @ -1,9 +1,8 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 5f6c0bbe8d0744418b9d67f7b9fd7fb4 | ||||
| guid: 3916a9bfa4fd449e2827f2f20897a7d7 | ||||
| folderAsset: yes | ||||
| timeCreated: 1489325654 | ||||
| licenseType: Free | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							|  | @ -1,5 +1,5 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 70a24c5a1222c4ae4b48a1d4738ae6de | ||||
| guid: a70633e155d144f5da10d40c35d9c832 | ||||
| PluginImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							|  | @ -1,32 +0,0 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 421fd959e80dc41d1b4d5f714a7f0752 | ||||
| PluginImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   iconMap: {} | ||||
|   executionOrder: {} | ||||
|   defineConstraints: [] | ||||
|   isPreloaded: 0 | ||||
|   isOverridable: 0 | ||||
|   isExplicitlyReferenced: 0 | ||||
|   validateReferences: 1 | ||||
|   platformData: | ||||
|   - first: | ||||
|       Android: Android | ||||
|     second: | ||||
|       enabled: 1 | ||||
|       settings: {} | ||||
|   - first: | ||||
|       Any:  | ||||
|     second: | ||||
|       enabled: 0 | ||||
|       settings: {} | ||||
|   - first: | ||||
|       Editor: Editor | ||||
|     second: | ||||
|       enabled: 0 | ||||
|       settings: | ||||
|         DefaultValueInitialized: true | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -1,5 +1,5 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 12a93e06f58f74620aa3663f7e55ecb3 | ||||
| guid: db3f23043d1754d4b9c96ddb5ab457c9 | ||||
| folderAsset: yes | ||||
| PluginImporter: | ||||
|   externalObjects: {} | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 4fd2db5d630fd43298e453e00f5bf4b6 | ||||
| guid: 19150ffd969374afd9b181cd8c71f662 | ||||
| folderAsset: yes | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>BuildMachineOSBuild</key> | ||||
| 	<string>22D68</string> | ||||
| 	<string>23G93</string> | ||||
| 	<key>CFBundleDevelopmentRegion</key> | ||||
| 	<string>en</string> | ||||
| 	<key>CFBundleExecutable</key> | ||||
|  | @ -27,21 +27,21 @@ | |||
| 	<key>DTCompiler</key> | ||||
| 	<string>com.apple.compilers.llvm.clang.1_0</string> | ||||
| 	<key>DTPlatformBuild</key> | ||||
| 	<string>14C18</string> | ||||
| 	<string></string> | ||||
| 	<key>DTPlatformName</key> | ||||
| 	<string>macosx</string> | ||||
| 	<key>DTPlatformVersion</key> | ||||
| 	<string>13.1</string> | ||||
| 	<string>14.5</string> | ||||
| 	<key>DTSDKBuild</key> | ||||
| 	<string>22C55</string> | ||||
| 	<string>23F73</string> | ||||
| 	<key>DTSDKName</key> | ||||
| 	<string>macosx13.1</string> | ||||
| 	<string>macosx14.5</string> | ||||
| 	<key>DTXcode</key> | ||||
| 	<string>1420</string> | ||||
| 	<string>1540</string> | ||||
| 	<key>DTXcodeBuild</key> | ||||
| 	<string>14C18</string> | ||||
| 	<string>15F31d</string> | ||||
| 	<key>LSMinimumSystemVersion</key> | ||||
| 	<string>10.10</string> | ||||
| 	<string>10.11</string> | ||||
| 	<key>NSHumanReadableCopyright</key> | ||||
| 	<string>Copyright © 2017年 OneV's Den. All rights reserved.</string> | ||||
| </dict> | ||||
|  |  | |||
|  | @ -1,7 +0,0 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 1a152c5424cf14075ba6d3833802d47c | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -1,5 +1,5 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 9e7db360725374b64b0ec3f96cce3fd3 | ||||
| guid: 495312126946747a3b197461eaff9687 | ||||
| folderAsset: yes | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								Runtime/GuruWebview/UniWebView5/Plugins/UniWebView.bundle/Contents/MacOS/UniWebView
								
								
								
								
									
									
									Executable file → Normal file
								
							
							
						
						
									
										
											BIN
										
									
								
								Runtime/GuruWebview/UniWebView5/Plugins/UniWebView.bundle/Contents/MacOS/UniWebView
								
								
								
								
									
									
									Executable file → Normal file
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -1,7 +0,0 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: b22bec296e7344274920d89d60046b86 | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -1,5 +1,5 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 80556f40493b8401b9ed41de8942b552 | ||||
| guid: 80fcb824c01d44613803213f4b1ed096 | ||||
| folderAsset: yes | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|  |  | |||
|  | @ -1,9 +1,8 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 921d907cf956b4498afa0a728241d961 | ||||
| guid: 10cf9bec133da4d3b836f29204deca1b | ||||
| folderAsset: yes | ||||
| timeCreated: 1489325641 | ||||
| licenseType: Free | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							|  | @ -1,5 +1,5 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: c7db402cd440f49ce97da3366b3cb653 | ||||
| guid: 060e1768692e941c2aa0f7c9f9f10e32 | ||||
| PluginImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|  | @ -11,24 +11,6 @@ PluginImporter: | |||
|   isExplicitlyReferenced: 0 | ||||
|   validateReferences: 1 | ||||
|   platformData: | ||||
|   - first: | ||||
|       : Any | ||||
|     second: | ||||
|       enabled: 0 | ||||
|       settings: | ||||
|         Exclude Android: 1 | ||||
|         Exclude Editor: 1 | ||||
|         Exclude Linux64: 1 | ||||
|         Exclude OSXUniversal: 1 | ||||
|         Exclude Win: 1 | ||||
|         Exclude Win64: 1 | ||||
|         Exclude iOS: 0 | ||||
|   - first: | ||||
|       Android: Android | ||||
|     second: | ||||
|       enabled: 0 | ||||
|       settings: | ||||
|         CPU: ARMv7 | ||||
|   - first: | ||||
|       Any:  | ||||
|     second: | ||||
|  | @ -39,42 +21,13 @@ PluginImporter: | |||
|     second: | ||||
|       enabled: 0 | ||||
|       settings: | ||||
|         CPU: AnyCPU | ||||
|         DefaultValueInitialized: true | ||||
|         OS: AnyOS | ||||
|   - first: | ||||
|       Standalone: Linux64 | ||||
|     second: | ||||
|       enabled: 0 | ||||
|       settings: | ||||
|         CPU: None | ||||
|   - first: | ||||
|       Standalone: OSXUniversal | ||||
|     second: | ||||
|       enabled: 0 | ||||
|       settings: | ||||
|         CPU: None | ||||
|   - first: | ||||
|       Standalone: Win | ||||
|     second: | ||||
|       enabled: 0 | ||||
|       settings: | ||||
|         CPU: None | ||||
|   - first: | ||||
|       Standalone: Win64 | ||||
|     second: | ||||
|       enabled: 0 | ||||
|       settings: | ||||
|         CPU: None | ||||
|   - first: | ||||
|       iPhone: iOS | ||||
|     second: | ||||
|       enabled: 1 | ||||
|       settings: | ||||
|         AddToEmbeddedBinaries: false | ||||
|         CPU: AnyCPU | ||||
|         CompileFlags:  | ||||
|         FrameworkDependencies:  | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: a1424e00355294877b7da28b5968d7ec | ||||
| guid: 92454175c62504ab5bf7128917b56362 | ||||
| folderAsset: yes | ||||
| timeCreated: 1490878496 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| DefaultImporter: | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|  |  | |||
|  | @ -1,10 +1,246 @@ | |||
| # Release Note | ||||
| 
 | ||||
| ### 5.0.3 | ||||
| ### 5.11.1 (12 Sep, 2024) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * A build issue when running under Unity Editor on Windows when the target is not iOS. It was introduced by 5.11.0. | ||||
| 
 | ||||
| ### 5.11.0 (10 Sep, 2024) | ||||
| 
 | ||||
| #### Add | ||||
| 
 | ||||
| * Support setting an HTTPS link (universal link for iOS or app link for Android) as the callback URL of an OAuth 2.0 flow. (Before, only the custom scheme was supported) | ||||
| * iOS 18 & Xcode 16 build support. | ||||
| 
 | ||||
| ### 5.10.2 (3 Aug, 2024) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * Fix a potential freeze of the game when calling cookie related methods on iOS in some Unity versions. | ||||
| 
 | ||||
| ### 5.10.1 (21 Jun, 2024) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * Fixed a bug where certain comment lines were unexpectedly removed while patching Gradle files. | ||||
| * Resolved an issue where the `OnLoadingErrorReceived` event (along with the deprecated `OnPageErrorReceived` event) was triggered twice during a loading error on certain Android versions. | ||||
| 
 | ||||
| ### 5.10.0 (9 Jun, 2024) | ||||
| 
 | ||||
| #### Add | ||||
| 
 | ||||
| * A `RestoreViewHierarchyOnResume` property to make the web view restore its view hierarchy when the app resumes from background on Android. This is an issue in these Unity versions: from Unity 2021.3.31 to 2021.3.34, from Unity 2022.3.10 to 2022.3.15. If you are using UniWebView in these versions, you need to set this value to `true` after upgrading. | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * Some internal improvements. | ||||
| 
 | ||||
| ### 5.9.2 (10 Apr, 2024) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * An issue that missing the x86_64 slice for UniWebView on macOS. Now the macOS version can run on Unity editor under x86_64 mode again. | ||||
| 
 | ||||
| ### 5.9.1 (20 Mar, 2024) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * A compiling error of Android build system under Unity 2023.2.13 or later. This was due to Unity removed the related APIs in the new version. Now UniWebView reverts to use the old way (the same before UniWebView 5.8.0) to patch the Android project files. | ||||
| 
 | ||||
| ### 5.9.0 (28 Feb, 2024) | ||||
| 
 | ||||
| #### Add | ||||
| 
 | ||||
| * Now the WebRTC support on Android does not require adding trust domain by code. A prompt window will be shown to the user if the web page tries to access the camera or microphone. | ||||
| * Now it is possible to customize the permission request handler for WebRTC, such as grant the access request without prompt to the user. Check `RegisterOnRequestMediaCapturePermission` for more information. | ||||
| 
 | ||||
| #### Deprecate | ||||
| 
 | ||||
| * As `RegisterOnRequestMediaCapturePermission` is introduced, the current `AddPermissionTrustDomain` method is deprecated. Use the new method if you need to grant the permission by code. | ||||
| 
 | ||||
| ### 5.8.0 (28 Jan, 2024) | ||||
| 
 | ||||
| #### Add | ||||
| 
 | ||||
| * Supporting of specifying the cache mode used when loading a request. You can now use `SetCacheMode` and pick a `UniWebViewCacheMode` policy to determine whether and how to use the cache data when loading a request. | ||||
| * Adopt to use `AndroidProjectFilesModifier` to modify the exported Android project from Unity 2023.2. This prevents some potential edge issues when exporting the project if another custom Gradle template is used. | ||||
| * Supporting of the data URI on the page. Now the links starting with `data:` is treated as a valid URL and it trigger a standard downloading process. | ||||
| * Add a method `SetAllowUserEditFileNameBeforeDownloading` to provide a more customizable way of downloading. By setting it with `false`, UniWebView will skip the file name editing step and use the default file name instead, and start the downloading immediately. | ||||
| 
 | ||||
| ### 5.7.3 (25 Dec, 2023) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * The context menu image downloading now should work correctly on Android 14. | ||||
| * An issue that the downloading URL was converted to lower case when triggering a context menu download on Android. | ||||
| * Refine the gradle properties file patcher to not add a trailing new line when patching the file. | ||||
| * A potential vulnerability scanner warning that the file provider can read files from a wider scope than needed. | ||||
| 
 | ||||
| #### Deprecate | ||||
| 
 | ||||
| * Completely mark the legacy toolbar related methods as deprecated. Use the new embedded toolbar instead whenever possible. | ||||
| 
 | ||||
| ### 5.7.2 (4 Dec, 2023) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * A potential issue on some Android browser implementations, the OAuth support crashes due to a "null activity handler found!" error. Now UniWebView will use a workaround to prevent the crash. | ||||
| 
 | ||||
| ### 5.7.1 (24 Nov, 2023) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * The `SetDragInteractionEnabled` method also works on Android now. It allows you to disable the drag interaction on Android devices that support drag-and-drop gesture. | ||||
| * Mark several methods in `UniWebViewAuthenticationFlowCustomize` as `virtual` to allow overriding them in subclasses. | ||||
| * Now the OAuth 2.0 flow will ignore the letter case when receiving the response code URL from the server. It allows you to register the redirect URL with different letter cases in the OAuth provider and UniWebView. | ||||
| 
 | ||||
| ### 5.7.0 (25 Oct, 2023) | ||||
| 
 | ||||
| #### Add | ||||
| 
 | ||||
| * A new `SetForwardWebConsoleToNativeOutput` method which enables forwarding the web console log (such as `console.log` or `console.error`) to the native log output. On iOS, it prints the log to Xcode console, on Android to the Android logcat, and on macOS Editor to the Unity console. This is useful for debugging issues from the web page. | ||||
| * Now the `OnWebContentProcessTerminated` event will also be invoked when the render process is gone on Android (when the `OnRenderProcessGone` event is raise). This is a new behavior on Android 11 and above and the default handling will only prevent the whole app crash. You need to implement this event and  | ||||
| try to release resources and/or perform a reload to recover. | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * An issue that the keyboard avoidance behavior on Android is not working properly and contains an undesired offset when the web view is not placed at the top of the screen. | ||||
| 
 | ||||
| ### 5.6.3 (4 Oct, 2023) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * An issue that the web view disappears when switching back to foreground in some newer Unity versions (2021.3.31f1, 2022.3.10f1, 2023.3.0a1). This is a regression of the particular Unity versions when it tries to fix [UUM-30881](https://issuetracker.unity3d.com/issues/android-a-black-screen-appears-for-a-few-seconds-when-returning-to-the-game-from-the-lock-screen-after-idle-time). | ||||
| 
 | ||||
| ### 5.6.2 (29 Sep, 2023) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * Support for Android 14. Solve a crash when setting Android 14 (API Level 34) as the target SDK for the game, and opening a web view on Android 14 devices. | ||||
| 
 | ||||
| ### 5.6.1 (9 Sep, 2023) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * A compile error when using UniWebView on Windows Editor was introduced in 5.6.0. Now you should be able to compile the project on Windows Editor. Mac Editor is not affected. | ||||
| 
 | ||||
| ### 5.6.0 (8 Sep, 2023) | ||||
| 
 | ||||
| #### Add | ||||
| 
 | ||||
| * New feature of rendering the web view to a texture. Now you can use `CreateRenderedTexture` to render the web view to a texture and use it in the game world. Check [this guide](https://docs.uniwebview.com/guide/render-to-texture.html) for more information and the implementation steps. | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * Replace using of `NameValueCollection` in OAuth flows to prevent potential issues with different .Net versions. | ||||
| * An issue that a crash may happen when adding downloading URL or MIME type with `UniWebViewDownloadMatchingType.RegularExpression` option. | ||||
| 
 | ||||
| 
 | ||||
| ### 5.5.1 (29 Aug, 2023) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * An issue that causes OAuth 2.0 flow crash on some browser implementations (such as Firefox and Amazon Silk) which does not provide the correct support. Chrome and some other browsers are not affected. You need a clean build after upgrading to get this fix. | ||||
| 
 | ||||
| ### 5.5.0 (6 Aug, 2023) | ||||
| 
 | ||||
| #### Add | ||||
| 
 | ||||
| * A new `OnLoadingErrorReceived` event, which is triggered when the web view loading encounters an error. This replaces the original `OnPageErrorReceived` event. The new event contains an additional `payload` parameter, in which some error detail (such as the failing URL) is contained. | ||||
| * Add a build option for adding `androidx.core` package in UniWebView's preference panel. This should fix the problem of crashing due to missing class `FileProvider` on Android 11 or above on Android when disabling the `androidx.browser` package. For more about building issues on Android, we also prepared a new [trouble shooting guide page](https://docs.uniwebview.com/guide/trouble-shooting.html). | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * A potential issue that cookie values sometimes are not treated correctly when requesting following resource request inside an iframe target. | ||||
| 
 | ||||
| #### Deprecate | ||||
| 
 | ||||
| * `OnPageErrorReceived` is now deprecated. Use `OnLoadingErrorReceived` instead. | ||||
| 
 | ||||
| ### 5.4.3 (26 Jul, 2023) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * A potential issue that code obfuscation on Android might cause class conflicting with other plugins which also perform a full obfuscation. Now UniWebView uses a more stable way to obfuscate its native code. | ||||
| * A bug that `OnOrientationChanged` event might be called too early in some cases, which causes the received value is not yet correct. | ||||
| 
 | ||||
| ### 5.4.2 (9 Jun, 2023) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * The method `AddDownloadMIMEType` did not work as expected even when the target MIME type is detected. Now it should trigger the download as expected. | ||||
| 
 | ||||
| ### 5.4.1 (17 May, 2023) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * An issue that the web view crashes on some Android devices when there is no loading callback is registered under Release mode. This was a regression introduced in 5.4.0. If you are using 5.4.0 and has this crash, please upgrade to 5.4.1 to get a fix. | ||||
| 
 | ||||
| ### 5.4.0 (13 May, 2023) | ||||
| 
 | ||||
| #### Add | ||||
| 
 | ||||
| * A method to control whether a loading should be started or not. Use `RegisterShouldHandleRequest` to register a callback and returns whether the loading should be started or not. Check [its reference](https://docs.uniwebview.com/api/#registershouldhandlerequest) for more. | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * Now web view on iOS 16.4 can also be inspected. However, different from previous versions, you need to call `SetWebContentsDebuggingEnabled` to enable the debugging mode before inspecting the web view on all platforms. | ||||
| 
 | ||||
| ### 5.3.2 (5 Apr, 2023) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * Strip using of methods from `System.Web.HttpUtility` internally to resolve a runtime issue in older Unity versions. | ||||
| 
 | ||||
| ### 5.3.1 (18 Feb, 2023) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * An issue that `SetOpenLinksInExternalBrowser` setting is ignored for links with "target=_blank" attribute. Now the external browser will be used for such links instead of opening the page in place. If `SetOpenLinksInExternalBrowser` and `SetSupportMultipleWindows` is set to `true` at the same time, the external browser will be used. | ||||
| * Now the activity for OAuth 2.0 flow on Android allows orientation changes. It won't throw an exception anymore when the device is rotated during the flow. | ||||
| 
 | ||||
| ### 5.3.0 (28 Jan, 2023) | ||||
| 
 | ||||
| #### Add | ||||
| 
 | ||||
| * Support for customization of Kotlin and Android Browser package versions. This can help to resolve the conflict with other plugins which use another version of these packages. | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * Improve the visual effect of the Embedded Toolbar on iOS. Now it has a larger and bold font for the title. | ||||
| * A better way to find the root view controller for adding UniWebView to on iOS. Now you can implement a `rootViewControllerForUniWebView` method in your app delegate and return the view controller you want UniWebView to be added to. By default, if the app delegate does not implement either `rootViewControllerForUniWebView` or `rootViewController`, UniWebView now also tries to find the top-most view controller instead of the window's direct root view controller. | ||||
| 
 | ||||
| ### 5.2.1 (4 Jan, 2023) | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * A crash when an SSL error is encountered during loading a scheme other than "http" or "https" (for example, "wss://" links) on Android. | ||||
| 
 | ||||
| ### 5.2.0 (23 Dec, 2022) | ||||
| 
 | ||||
| #### Add | ||||
| 
 | ||||
| * Most of the OAuth flows now support refresh token. Use `StartRefreshTokenFlow` on the flow to start token refresh. For more about OAuth 2.0 support in UniWebView, check [this guide](https://docs.uniwebview.com/guide/oauth2.html#refresh-token). | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * A workaround for Facebook login on Android. Facebook is preventing a web view to perform OAuth login on Android, now the `UniWebViewAuthenticationFlowFacebook` will use a desktop browser user-agent to open the login page. | ||||
| * A crash that caused by orientation change when the camera is presented on Android while the game is landscape but the camera is portrait. | ||||
| * A crash when taking screenshot when the web view has a zero or negative size. Now under this case, the `CaptureSnapshot` method will give an error `-1002` in its `OnCaptureSnapshotFinished` event. | ||||
| 
 | ||||
| ### 5.1.0 (21 Nov, 2022) | ||||
| 
 | ||||
| #### Add | ||||
| 
 | ||||
| * New methods to control the behavior of the loading spinner. Use `SetAllowUserDismissSpinner` to allow or forbid users dismissing the loading indicator; Use `ShowSpinner` and `HideSpinner` to control its visibility programmatically. | ||||
| * A new API `SetLimitsNavigationsToAppBoundDomains`, it is a wrapper of `limitsNavigationsToAppBoundDomains` for iOS. It limits user's navigation to only pre-defined domains on iOS. | ||||
| 
 | ||||
| #### Fix | ||||
| 
 | ||||
| * Improve the delay that on Android the received cookies are not flushed fast enough in some cases. Now a forcibly cookie flush is always performed when closing the web view. | ||||
| * The Unity InputSystem assembly reference is added explicitly to prevent compiling error on Unity's versions without InputSystem support. | ||||
| * An issue prevents the package built on macOS 13 SDK. | ||||
| 
 | ||||
| ### 5.0.2 (24 Oct, 2022) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: f7ee8c3eb642344b08f4ceeb53d70cd1 | ||||
| timeCreated: 1497057465 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| DefaultImporter: | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ fileFormatVersion: 2 | |||
| guid: 565d674726c3d499cade2709dd955ad0 | ||||
| folderAsset: yes | ||||
| timeCreated: 1536753564 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| DefaultImporter: | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 938c077f40a814d4584ce2ad17947cf3 | ||||
| timeCreated: 1536753573 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| DefaultImporter: | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|  |  | |||
|  | @ -1,17 +1,14 @@ | |||
| using System.Xml; | ||||
| using System.Collections; | ||||
| using System.Text; | ||||
| using System.IO; | ||||
| using System; | ||||
| using UnityEditor; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| internal class UniWebViewAndroidXmlDocument : XmlDocument { | ||||
|     private string path; | ||||
|     protected XmlNamespaceManager nameSpaceManager; | ||||
|     public readonly string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android"; | ||||
|     private readonly string path; | ||||
|     protected readonly XmlNamespaceManager nameSpaceManager; | ||||
|     protected const string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android"; | ||||
| 
 | ||||
|     public UniWebViewAndroidXmlDocument(string path) { | ||||
|     protected UniWebViewAndroidXmlDocument(string path) { | ||||
|         this.path = path; | ||||
|         using (var reader = new XmlTextReader(path)) { | ||||
|             reader.Read(); | ||||
|  | @ -21,26 +18,25 @@ internal class UniWebViewAndroidXmlDocument : XmlDocument { | |||
|         nameSpaceManager.AddNamespace("android", AndroidXmlNamespace); | ||||
|     } | ||||
| 
 | ||||
|     public string Save() { | ||||
|         return SaveAs(path); | ||||
|     public void Save() { | ||||
|         SaveAs(path); | ||||
|     } | ||||
| 
 | ||||
|     public string SaveAs(string path) { | ||||
|         using (var writer = new XmlTextWriter(path, new UTF8Encoding(false))) { | ||||
|             writer.Formatting = Formatting.Indented; | ||||
|             Save(writer); | ||||
|         } | ||||
|         return path; | ||||
|     private void SaveAs(string path) | ||||
|     { | ||||
|         using var writer = new XmlTextWriter(path, new UTF8Encoding(false)); | ||||
|         writer.Formatting = Formatting.Indented; | ||||
|         Save(writer); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| internal class UniWebViewAndroidManifest : UniWebViewAndroidXmlDocument { | ||||
|     private readonly XmlElement ManifestElement; | ||||
|     private readonly XmlElement ApplicationElement; | ||||
|     private readonly XmlElement manifestElement; | ||||
|     private readonly XmlElement applicationElement; | ||||
| 
 | ||||
|     public UniWebViewAndroidManifest(string path) : base(path) { | ||||
|         ManifestElement = SelectSingleNode("/manifest") as XmlElement; | ||||
|         ApplicationElement = SelectSingleNode("/manifest/application") as XmlElement; | ||||
|         manifestElement = SelectSingleNode("/manifest") as XmlElement; | ||||
|         applicationElement = SelectSingleNode("/manifest/application") as XmlElement; | ||||
|     } | ||||
| 
 | ||||
|     private XmlAttribute CreateAndroidAttribute(string key, string value) { | ||||
|  | @ -58,17 +54,24 @@ internal class UniWebViewAndroidManifest : UniWebViewAndroidXmlDocument { | |||
|     } | ||||
| 
 | ||||
|     internal bool SetUsesCleartextTraffic() { | ||||
|         bool changed = false; | ||||
|         if (ApplicationElement.GetAttribute("usesCleartextTraffic", AndroidXmlNamespace) != "true") { | ||||
|             ApplicationElement.SetAttribute("usesCleartextTraffic", AndroidXmlNamespace, "true"); | ||||
|         var changed = false; | ||||
|         if (applicationElement.GetAttribute("usesCleartextTraffic", AndroidXmlNamespace) != "true") { | ||||
|             applicationElement.SetAttribute("usesCleartextTraffic", AndroidXmlNamespace, "true"); | ||||
|             changed = true; | ||||
|         } | ||||
|         return changed; | ||||
|     } | ||||
| 
 | ||||
|     internal bool SetHardwareAccelerated() { | ||||
|         bool changed = false; | ||||
|         var changed = false; | ||||
|         var activity = GetActivityWithLaunchIntent() as XmlElement; | ||||
|         if (activity == null) | ||||
|         { | ||||
|             Debug.LogError( | ||||
|                 "There is no launch intent activity in the AndroidManifest.xml." + | ||||
|                 " Please check your AndroidManifest.xml file and make sure it has a main activity with intent filter"); | ||||
|             return false; | ||||
|         } | ||||
|         if (activity.GetAttribute("hardwareAccelerated", AndroidXmlNamespace) != "true") { | ||||
|             activity.SetAttribute("hardwareAccelerated", AndroidXmlNamespace, "true"); | ||||
|             changed = true; | ||||
|  | @ -77,17 +80,22 @@ internal class UniWebViewAndroidManifest : UniWebViewAndroidXmlDocument { | |||
|     } | ||||
| 
 | ||||
|     internal bool AddCameraPermission() { | ||||
|         bool changed = false; | ||||
|         if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.CAMERA']", nameSpaceManager).Count == 0) { | ||||
|         var changed = false; | ||||
|         var cameraPermission = "/manifest/uses-permission[@android:name='android.permission.CAMERA']"; | ||||
|         var cameraPermissionNode = SelectNodes(cameraPermission, nameSpaceManager); | ||||
|         if (cameraPermissionNode == null || cameraPermissionNode.Count == 0) { | ||||
|             var elem = CreateElement("uses-permission"); | ||||
|             elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.CAMERA")); | ||||
|             ManifestElement.AppendChild(elem); | ||||
|             manifestElement.AppendChild(elem); | ||||
|             changed = true; | ||||
|         } | ||||
|         if (SelectNodes("/manifest/uses-feature[@android:name='android.hardware.camera']", nameSpaceManager).Count == 0) { | ||||
|          | ||||
|         var hardwareCamera = "/manifest/uses-feature[@android:name='android.hardware.camera']"; | ||||
|         var hardwareCameraNode = SelectNodes(hardwareCamera, nameSpaceManager); | ||||
|         if (hardwareCameraNode == null || hardwareCameraNode.Count == 0) { | ||||
|             var elem = CreateElement("uses-feature"); | ||||
|             elem.Attributes.Append(CreateAndroidAttribute("name", "android.hardware.camera")); | ||||
|             ManifestElement.AppendChild(elem); | ||||
|             manifestElement.AppendChild(elem); | ||||
|             changed = true; | ||||
|         } | ||||
|         return changed; | ||||
|  | @ -95,80 +103,96 @@ internal class UniWebViewAndroidManifest : UniWebViewAndroidXmlDocument { | |||
| 
 | ||||
|     internal bool AddMicrophonePermission() { | ||||
|         bool changed = false; | ||||
|         if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.MICROPHONE']", nameSpaceManager).Count == 0) { | ||||
|         var microphonePermission = "/manifest/uses-permission[@android:name='android.permission.MICROPHONE']"; | ||||
|         var microphonePermissionNode = SelectNodes(microphonePermission, nameSpaceManager); | ||||
|         if (microphonePermissionNode == null || microphonePermissionNode.Count == 0) { | ||||
|             var elem = CreateElement("uses-permission"); | ||||
|             elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.MICROPHONE")); | ||||
|             ManifestElement.AppendChild(elem); | ||||
|             manifestElement.AppendChild(elem); | ||||
|             changed = true; | ||||
|         } | ||||
|         if (SelectNodes("/manifest/uses-feature[@android:name='android.hardware.microphone']", nameSpaceManager).Count == 0) { | ||||
|          | ||||
|         var microphoneHardware = "/manifest/uses-feature[@android:name='android.hardware.microphone']"; | ||||
|         var microphoneHardwareNode = SelectNodes(microphoneHardware, nameSpaceManager); | ||||
|         if (microphoneHardwareNode == null || microphoneHardwareNode.Count == 0) { | ||||
|             var elem = CreateElement("uses-feature"); | ||||
|             elem.Attributes.Append(CreateAndroidAttribute("name", "android.hardware.microphone")); | ||||
|             ManifestElement.AppendChild(elem); | ||||
|             manifestElement.AppendChild(elem); | ||||
|             changed = true; | ||||
|         } | ||||
|         return changed; | ||||
|     } | ||||
| 
 | ||||
|     internal bool AddReadExternalStoragePermission() { | ||||
|         bool changed = false; | ||||
|         if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.READ_EXTERNAL_STORAGE']", nameSpaceManager).Count == 0) { | ||||
|         var changed = false; | ||||
|         var externalPermission = "/manifest/uses-permission[@android:name='android.permission.READ_EXTERNAL_STORAGE']"; | ||||
|         var externalNode = SelectNodes(externalPermission, nameSpaceManager); | ||||
|         if (externalNode == null || externalNode.Count == 0) { | ||||
|             var elem = CreateElement("uses-permission"); | ||||
|             elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.READ_EXTERNAL_STORAGE")); | ||||
|             ManifestElement.AppendChild(elem); | ||||
|             manifestElement.AppendChild(elem); | ||||
|             changed = true; | ||||
|         } | ||||
|         return changed; | ||||
|     } | ||||
| 
 | ||||
|     internal bool AddWriteExternalStoragePermission() { | ||||
|         bool changed = false; | ||||
|         if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.WRITE_EXTERNAL_STORAGE']", nameSpaceManager).Count == 0) { | ||||
|         var changed = false; | ||||
|         var externalPermission = "/manifest/uses-permission[@android:name='android.permission.WRITE_EXTERNAL_STORAGE']"; | ||||
|         var externalNode = SelectNodes(externalPermission, nameSpaceManager); | ||||
|         if (externalNode == null || externalNode.Count == 0) { | ||||
|             var elem = CreateElement("uses-permission"); | ||||
|             elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.WRITE_EXTERNAL_STORAGE")); | ||||
|             ManifestElement.AppendChild(elem); | ||||
|             manifestElement.AppendChild(elem); | ||||
|             changed = true; | ||||
|         } | ||||
|         return changed; | ||||
|     } | ||||
| 
 | ||||
|     internal bool AddAccessFineLocationPermission() { | ||||
|         bool changed = false; | ||||
|         if (SelectNodes("/manifest/uses-permission[@android:name='android.permission.ACCESS_FINE_LOCATION']", nameSpaceManager).Count == 0) { | ||||
|         var changed = false; | ||||
|         var locationPermission = "/manifest/uses-permission[@android:name='android.permission.ACCESS_FINE_LOCATION']"; | ||||
|         var locationNode = SelectNodes(locationPermission, nameSpaceManager); | ||||
|         if (locationNode == null || locationNode.Count == 0) { | ||||
|             var elem = CreateElement("uses-permission"); | ||||
|             elem.Attributes.Append(CreateAndroidAttribute("name", "android.permission.ACCESS_FINE_LOCATION")); | ||||
|             ManifestElement.AppendChild(elem); | ||||
|             manifestElement.AppendChild(elem); | ||||
|             changed = true; | ||||
|         } | ||||
|         return changed; | ||||
|     } | ||||
| 
 | ||||
|     internal bool AddAuthCallbacksIntentFilter(string[] authCallbackUrls) { | ||||
|         bool changed = false; | ||||
|         var changed = false; | ||||
|         XmlElement authActivityNode; | ||||
|         if (authCallbackUrls.Length > 0) { | ||||
|             var list = SelectNodes("/manifest/application/activity[@android:name='com.onevcat.uniwebview.UniWebViewAuthenticationActivity']", nameSpaceManager); | ||||
|             if (list.Count == 0) { | ||||
|             var authActivity = "/manifest/application/activity[@android:name='com.onevcat.uniwebview.UniWebViewAuthenticationActivity']"; | ||||
|             var list = SelectNodes(authActivity, nameSpaceManager); | ||||
|             if (list == null || list.Count == 0) { | ||||
|                 var created = CreateElement("activity"); | ||||
|                 created.SetAttribute("name", AndroidXmlNamespace, "com.onevcat.uniwebview.UniWebViewAuthenticationActivity"); | ||||
|                 created.SetAttribute("exported", AndroidXmlNamespace, "true"); | ||||
|                 created.SetAttribute("launchMode", AndroidXmlNamespace, "singleTop"); | ||||
|                 created.SetAttribute("launchMode", AndroidXmlNamespace, "singleTask"); | ||||
|                 created.SetAttribute("configChanges", AndroidXmlNamespace, "orientation|screenSize|keyboardHidden"); | ||||
|                 authActivityNode = created; | ||||
|             } else { | ||||
|                 authActivityNode = list[0] as XmlElement; | ||||
|             } | ||||
|         } else { | ||||
|             return changed; | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         foreach (var url in authCallbackUrls) { | ||||
|             var intentFilter = CreateIntentFilter(url); | ||||
|             if (intentFilter != null) { | ||||
|                 authActivityNode.AppendChild(intentFilter); | ||||
|                 authActivityNode?.AppendChild(intentFilter); | ||||
|                 changed = true; | ||||
|             } | ||||
|         } | ||||
|         ApplicationElement.AppendChild(authActivityNode); | ||||
| 
 | ||||
|         if (authActivityNode != null) { | ||||
|             applicationElement.AppendChild(authActivityNode); | ||||
|         } | ||||
|         return changed; | ||||
|     } | ||||
| 
 | ||||
|  | @ -176,12 +200,22 @@ internal class UniWebViewAndroidManifest : UniWebViewAndroidXmlDocument { | |||
|          | ||||
|         var uri = new Uri(url); | ||||
|         var scheme = uri.Scheme; | ||||
|         if (String.IsNullOrEmpty(scheme)) { | ||||
|         if (string.IsNullOrEmpty(scheme)) { | ||||
|             Debug.LogError("<UniWebView> Auth callback url contains an empty scheme. Please check the url: " + url); | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         var filter = CreateElement("intent-filter"); | ||||
| 
 | ||||
|         // Check if the scheme is HTTPS | ||||
|         // For Android App Links, we need to set the autoVerify attribute to true | ||||
|         // This allows Android to automatically verify the association between the app and the website | ||||
|         // Note: Only applies to HTTPS URLs, although Android App Links also support HTTP, we do not want it in  | ||||
|         // UniWebView. | ||||
|         // https://developer.android.com/training/app-links | ||||
|         if (scheme == "https") { | ||||
|             filter.SetAttribute("autoVerify", AndroidXmlNamespace, "true"); | ||||
|         } | ||||
|          | ||||
|         var action = CreateElement("action"); | ||||
|         action.SetAttribute("name", AndroidXmlNamespace, "android.intent.action.VIEW"); | ||||
|  | @ -201,9 +235,13 @@ internal class UniWebViewAndroidManifest : UniWebViewAndroidXmlDocument { | |||
|             data.SetAttribute("host", AndroidXmlNamespace, uri.Host); | ||||
|         } | ||||
|         if (uri.Port != -1) { | ||||
|             data.SetAttribute("port", AndroidXmlNamespace, uri.Port.ToString()); | ||||
|             // For HTTPS, the default port is 443. We only need to set the port explicitly | ||||
|             // if we're not using HTTPS or if we're using a non-standard HTTPS port. | ||||
|             if (scheme != "https" || uri.Port != 443) { | ||||
|                 data.SetAttribute("port", AndroidXmlNamespace, uri.Port.ToString()); | ||||
|             } | ||||
|         } | ||||
|         if (!String.IsNullOrEmpty(uri.PathAndQuery) && uri.PathAndQuery != "/") { | ||||
|         if (!string.IsNullOrEmpty(uri.PathAndQuery) && uri.PathAndQuery != "/") { | ||||
|             data.SetAttribute("path", AndroidXmlNamespace, uri.PathAndQuery); | ||||
|         } | ||||
|          | ||||
|  |  | |||
|  | @ -6,28 +6,23 @@ using System; | |||
| 
 | ||||
| public class UniWebViewGradleConfig | ||||
| { | ||||
|     private UniWebViewGradleNode m_root; | ||||
|     private String m_filePath; | ||||
|     private UniWebViewGradleNode m_curNode; | ||||
|     private readonly string m_filePath; | ||||
| 
 | ||||
|     public UniWebViewGradleNode ROOT | ||||
|     { | ||||
|         get { return m_root; } | ||||
|     } | ||||
|     public UniWebViewGradleNode Root { get; } | ||||
| 
 | ||||
|     public UniWebViewGradleConfig(string filePath) | ||||
|     { | ||||
|         string file = File.ReadAllText(filePath); | ||||
|         var file = File.ReadAllText(filePath); | ||||
|         TextReader reader = new StringReader(file); | ||||
| 
 | ||||
|         m_filePath = filePath; | ||||
|         m_root = new UniWebViewGradleNode("root"); | ||||
|         m_curNode = m_root; | ||||
|         Root = new UniWebViewGradleNode("root"); | ||||
|         var curNode = Root; | ||||
| 
 | ||||
|         StringBuilder str = new StringBuilder(); | ||||
|         bool inDoubleQuote = false; | ||||
|         bool inSingleQuote = false; | ||||
|         bool inDollarVariable = false; | ||||
|         var str = new StringBuilder(); | ||||
|         var inDoubleQuote = false; | ||||
|         var inSingleQuote = false; | ||||
|         var inDollarVariable = false; | ||||
| 
 | ||||
|         while (reader.Peek() > 0) | ||||
|         { | ||||
|  | @ -40,7 +35,7 @@ public class UniWebViewGradleConfig | |||
|                 //         reader.Read(); | ||||
|                 //         string comment = reader.ReadLine(); | ||||
|                 //         Debug.Log("Comment line: " + comment); | ||||
|                 //         m_curNode.AppendChildNode(new UniWebViewGradleCommentNode(comment, m_curNode)); | ||||
|                 //         curNode.AppendChildNode(new UniWebViewGradleCommentNode(comment, curNode)); | ||||
|                 //     } | ||||
|                 //     else | ||||
|                 //     { | ||||
|  | @ -53,7 +48,7 @@ public class UniWebViewGradleConfig | |||
|                         var strf = FormatStr(str); | ||||
|                         if (!string.IsNullOrEmpty(strf)) | ||||
|                         { | ||||
|                             m_curNode.AppendChildNode(new UniWebViewGradleContentNode(strf, m_curNode)); | ||||
|                             curNode.AppendChildNode(new UniWebViewGradleContentNode(strf, curNode)); | ||||
|                         } | ||||
|                     } | ||||
|                     inDollarVariable = false; | ||||
|  | @ -77,9 +72,9 @@ public class UniWebViewGradleConfig | |||
|                         var n = FormatStr(str); | ||||
|                         if (!string.IsNullOrEmpty(n)) | ||||
|                         { | ||||
|                             UniWebViewGradleNode node = new UniWebViewGradleNode(n, m_curNode); | ||||
|                             m_curNode.AppendChildNode(node); | ||||
|                             m_curNode = node; | ||||
|                             UniWebViewGradleNode node = new UniWebViewGradleNode(n, curNode); | ||||
|                             curNode.AppendChildNode(node); | ||||
|                             curNode = node; | ||||
|                         } | ||||
|                     } | ||||
|                     str = new StringBuilder(); | ||||
|  | @ -93,9 +88,9 @@ public class UniWebViewGradleConfig | |||
|                         var strf = FormatStr(str); | ||||
|                         if (!string.IsNullOrEmpty(strf)) | ||||
|                         { | ||||
|                             m_curNode.AppendChildNode(new UniWebViewGradleContentNode(strf, m_curNode)); | ||||
|                             curNode.AppendChildNode(new UniWebViewGradleContentNode(strf, curNode)); | ||||
|                         } | ||||
|                         m_curNode = m_curNode.PARENT; | ||||
|                         curNode = curNode.Parent; | ||||
|                     } | ||||
|                     str = new StringBuilder(); | ||||
|                     break; | ||||
|  | @ -131,27 +126,36 @@ public class UniWebViewGradleConfig | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // End of file. | ||||
|         var endline = FormatStr(str); | ||||
|         if (!string.IsNullOrEmpty(endline)) | ||||
|         { | ||||
|             curNode.AppendChildNode(new UniWebViewGradleContentNode(endline, curNode)); | ||||
|         } | ||||
|         //Debug.Log("Gradle parse done!"); | ||||
|     } | ||||
| 
 | ||||
|     public void Save(string path = null) | ||||
|     { | ||||
|         if (path == null) | ||||
|         if (path == null) { | ||||
|             path = m_filePath; | ||||
|         } | ||||
|              | ||||
|         File.WriteAllText(path, Print()); | ||||
|         //Debug.Log("Gradle parse done! " + path); | ||||
|     } | ||||
| 
 | ||||
|     private string FormatStr(StringBuilder sb) | ||||
|     private static string FormatStr(StringBuilder sb) | ||||
|     { | ||||
|         string str = sb.ToString(); | ||||
|         var str = sb.ToString(); | ||||
|         str = str.Trim(); | ||||
|         return str; | ||||
|     } | ||||
|     public string Print() | ||||
|     { | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         printNode(sb, m_root, -1); | ||||
|         PrintNode(sb, Root, -1); | ||||
|         // Remove the first empty line. | ||||
|         sb.Remove(0, 1); | ||||
|         return sb.ToString(); | ||||
|     } | ||||
|     private string GetLevelIndent(int level) | ||||
|  | @ -164,69 +168,52 @@ public class UniWebViewGradleConfig | |||
|         } | ||||
|         return sb.ToString(); | ||||
|     } | ||||
|     private void printNode(StringBuilder stringBuilder, UniWebViewGradleNode node, int level) | ||||
|     private void PrintNode(StringBuilder stringBuilder, UniWebViewGradleNode node, int level) | ||||
|     { | ||||
|         if (node.PARENT != null) | ||||
|         { | ||||
|         if (node.Parent != null) { | ||||
|             if (node is UniWebViewGradleCommentNode) | ||||
|             { | ||||
|                 stringBuilder.Append("\n" + GetLevelIndent(level) + @"//" + node.NAME); | ||||
|                 stringBuilder.Append("\n" + GetLevelIndent(level) + @"//" + node.Name); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 stringBuilder.Append("\n" + GetLevelIndent(level) + node.NAME); | ||||
|                 stringBuilder.Append("\n" + GetLevelIndent(level) + node.Name); | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         if (!(node is UniWebViewGradleContentNode) && !(node is UniWebViewGradleCommentNode)) | ||||
|         { | ||||
|             if (node.PARENT != null) | ||||
|             { | ||||
|                 stringBuilder.Append(" {"); | ||||
|             } | ||||
|             foreach (var c in node.CHILDREN) | ||||
|             { | ||||
|                 printNode(stringBuilder, c, level + 1); | ||||
|             } | ||||
|             if (node.PARENT != null) | ||||
|             { | ||||
|                 stringBuilder.Append("\n" + GetLevelIndent(level) + "}"); | ||||
|             } | ||||
|         if (node is UniWebViewGradleContentNode || node is UniWebViewGradleCommentNode) return; | ||||
|         if (node.Parent != null)  { | ||||
|             stringBuilder.Append(" {"); | ||||
|         } | ||||
|         foreach (var c in node.Children) { | ||||
|             PrintNode(stringBuilder, c, level + 1); | ||||
|         } | ||||
|         if (node.Parent != null) { | ||||
|             stringBuilder.Append("\n" + GetLevelIndent(level) + "}"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| public class UniWebViewGradleNode | ||||
| { | ||||
|     protected List<UniWebViewGradleNode> m_children = new List<UniWebViewGradleNode>(); | ||||
|     protected UniWebViewGradleNode m_parent; | ||||
|     protected String m_name; | ||||
|     public UniWebViewGradleNode PARENT | ||||
|     { | ||||
|         get { return m_parent; } | ||||
|     } | ||||
|     protected string m_name; | ||||
|     public UniWebViewGradleNode Parent { get; private set; } | ||||
| 
 | ||||
|     public string NAME | ||||
|     { | ||||
|         get { return m_name; } | ||||
|     } | ||||
|     public string Name => m_name; | ||||
| 
 | ||||
|     public List<UniWebViewGradleNode> CHILDREN | ||||
|     { | ||||
|         get { return m_children; } | ||||
|     } | ||||
|     public List<UniWebViewGradleNode> Children { get; private set; } = new List<UniWebViewGradleNode>(); | ||||
| 
 | ||||
|     public UniWebViewGradleNode(string name, UniWebViewGradleNode parent = null) | ||||
|     { | ||||
|         m_parent = parent; | ||||
|         Parent = parent; | ||||
|         m_name = name; | ||||
|     } | ||||
| 
 | ||||
|     public void Each(Action<UniWebViewGradleNode> f) | ||||
|     { | ||||
|         f(this); | ||||
|         foreach (var n in m_children) | ||||
|         foreach (var n in Children) | ||||
|         { | ||||
|             n.Each(f); | ||||
|         } | ||||
|  | @ -234,20 +221,19 @@ public class UniWebViewGradleNode | |||
| 
 | ||||
|     public void AppendChildNode(UniWebViewGradleNode node) | ||||
|     { | ||||
|         if (m_children == null) m_children = new List<UniWebViewGradleNode>(); | ||||
|         m_children.Add(node); | ||||
|         node.m_parent = this; | ||||
|         if (Children == null) Children = new List<UniWebViewGradleNode>(); | ||||
|         Children.Add(node); | ||||
|         node.Parent = this; | ||||
|     } | ||||
| 
 | ||||
|     public UniWebViewGradleNode TryGetNode(string path) | ||||
|     { | ||||
|         string[] subpath = path.Split('/'); | ||||
|         UniWebViewGradleNode cnode = this; | ||||
|         for (int i = 0; i < subpath.Length; i++) | ||||
|         var subpath = path.Split('/'); | ||||
|         var cnode = this; | ||||
|         foreach (var p in subpath) | ||||
|         { | ||||
|             var p = subpath[i]; | ||||
|             if (string.IsNullOrEmpty(p)) continue; | ||||
|             UniWebViewGradleNode tnode = cnode.FindChildNodeByName(p); | ||||
|             var tnode = cnode.FindChildNodeByName(p); | ||||
|             if (tnode == null) | ||||
|             { | ||||
|                 Debug.Log("Can't find Node:" + p); | ||||
|  | @ -255,7 +241,6 @@ public class UniWebViewGradleNode | |||
|             } | ||||
| 
 | ||||
|             cnode = tnode; | ||||
|             tnode = null; | ||||
|         } | ||||
| 
 | ||||
|         return cnode; | ||||
|  | @ -263,22 +248,22 @@ public class UniWebViewGradleNode | |||
| 
 | ||||
|     public UniWebViewGradleNode FindChildNodeByName(string name) | ||||
|     { | ||||
|         foreach (var n in m_children) | ||||
|         foreach (var n in Children) | ||||
|         { | ||||
|             if (n is UniWebViewGradleCommentNode || n is UniWebViewGradleContentNode) | ||||
|                 continue; | ||||
|             if (n.NAME == name) | ||||
|             if (n.Name == name) | ||||
|                 return n; | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     public bool ReplaceContenStartsWith(string patten, string value) | ||||
|     public bool ReplaceContentStartsWith(string pattern, string value) | ||||
|     { | ||||
|         foreach (var n in m_children) | ||||
|         foreach (var n in Children) | ||||
|         { | ||||
|             if (!(n is UniWebViewGradleContentNode)) continue; | ||||
|             if (n.m_name.StartsWith(patten)) | ||||
|             if (n.m_name.StartsWith(pattern)) | ||||
|             { | ||||
|                 n.m_name = value; | ||||
|                 return true; | ||||
|  | @ -287,12 +272,12 @@ public class UniWebViewGradleNode | |||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     public UniWebViewGradleContentNode ReplaceContenOrAddStartsWith(string patten, string value) | ||||
|     public UniWebViewGradleContentNode ReplaceContentOrAddStartsWith(string pattern, string value) | ||||
|     { | ||||
|         foreach (var n in m_children) | ||||
|         foreach (var n in Children) | ||||
|         { | ||||
|             if (!(n is UniWebViewGradleContentNode)) continue; | ||||
|             if (n.m_name.StartsWith(patten)) | ||||
|             if (n.m_name.StartsWith(pattern)) | ||||
|             { | ||||
|                 n.m_name = value; | ||||
|                 return (UniWebViewGradleContentNode)n; | ||||
|  | @ -300,15 +285,10 @@ public class UniWebViewGradleNode | |||
|         } | ||||
|         return AppendContentNode(value); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 添加子节点 | ||||
|     /// </summary> | ||||
|     /// <param name="content"></param> | ||||
|     /// <returns></returns> | ||||
|      | ||||
|     public UniWebViewGradleContentNode AppendContentNode(string content) | ||||
|     { | ||||
|         foreach (var n in m_children) | ||||
|         foreach (var n in Children) | ||||
|         { | ||||
|             if (!(n is UniWebViewGradleContentNode)) continue; | ||||
|             if (n.m_name == content) | ||||
|  | @ -325,12 +305,12 @@ public class UniWebViewGradleNode | |||
| 
 | ||||
|     public bool RemoveContentNode(string contentPattern) | ||||
|     { | ||||
|         for(int i=0;i<m_children.Count;i++) | ||||
|         for(int i=0;i<Children.Count;i++) | ||||
|         { | ||||
|             if (!(m_children[i] is UniWebViewGradleContentNode)) continue; | ||||
|             if(m_children[i].m_name.Contains(contentPattern)) | ||||
|             if (!(Children[i] is UniWebViewGradleContentNode)) continue; | ||||
|             if(Children[i].m_name.Contains(contentPattern)) | ||||
|             { | ||||
|                 m_children.RemoveAt(i); | ||||
|                 Children.RemoveAt(i); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -1,32 +1,47 @@ | |||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using UnityEngine; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.IO; | ||||
| 
 | ||||
| 
 | ||||
| public class UniWebViewGradlePropertyPatcher { | ||||
|     public static void Patch(string filePath) { | ||||
|         string[] lines = File.ReadAllLines(filePath); | ||||
|      | ||||
|     private readonly string filePath; | ||||
|     private readonly UniWebViewEditorSettings settings; | ||||
| 
 | ||||
|         bool hasAndroidXProperty = lines.Any(text => text.Contains("android.useAndroidX")); | ||||
|         bool hasJetifierProperty = lines.Any(text => text.Contains("android.enableJetifier")); | ||||
|     // Construct a patcher with file path. | ||||
|     public UniWebViewGradlePropertyPatcher(string filePath, UniWebViewEditorSettings settings) | ||||
|     { | ||||
|         this.filePath = filePath; | ||||
|         this.settings = settings; | ||||
|     } | ||||
|      | ||||
|     public void Patch() | ||||
|     { | ||||
|         var result = UpdatedString(); | ||||
|         File.WriteAllText(filePath, result); | ||||
|     } | ||||
| 
 | ||||
|         StringBuilder builder = new StringBuilder(); | ||||
|     public string UpdatedString() | ||||
|     { | ||||
|         var lines = File.ReadAllLines(filePath); | ||||
| 
 | ||||
|         foreach(string each in lines) { | ||||
|         var hasAndroidXProperty = lines.Any(text => text.Contains("android.useAndroidX")); | ||||
|         var hasJetifierProperty = lines.Any(text => text.Contains("android.enableJetifier")); | ||||
| 
 | ||||
|         var builder = new StringBuilder(); | ||||
| 
 | ||||
|         foreach(var each in lines) { | ||||
|             builder.AppendLine(each); | ||||
|         } | ||||
| 
 | ||||
|          | ||||
|         if (!hasAndroidXProperty) { | ||||
|             builder.AppendLine("android.useAndroidX=true"); | ||||
|         } | ||||
| 
 | ||||
|         if (!hasJetifierProperty && UniWebViewEditorSettings.GetOrCreateSettings().enableJetifier) { | ||||
|         if (!hasJetifierProperty && settings.enableJetifier) { | ||||
|             builder.AppendLine("android.enableJetifier=true"); | ||||
|         } | ||||
| 
 | ||||
|         File.WriteAllText(filePath, builder.ToString()); | ||||
|         // AppendLine will add a new line at the end of the string, so we need to trim it to keep the file clean. | ||||
|         return builder.ToString().Trim(); | ||||
|     } | ||||
| } | ||||
|  | @ -4,9 +4,9 @@ using System.Collections.Generic; | |||
| using System; | ||||
| using System.IO; | ||||
| 
 | ||||
| class UniWebViewEditorSettings: ScriptableObject | ||||
| public class UniWebViewEditorSettings: ScriptableObject | ||||
| { | ||||
|     const string assetPath = "Assets/Guru/Editor/UniWebView/settings.asset"; | ||||
|     private const string AssetPath = "Assets/Editor/UniWebView/settings.asset"; | ||||
| 
 | ||||
|     [SerializeField] | ||||
|     internal bool usesCleartextTraffic = false; | ||||
|  | @ -20,9 +20,21 @@ class UniWebViewEditorSettings: ScriptableObject | |||
|     [SerializeField] | ||||
|     internal bool addsKotlin = true; | ||||
| 
 | ||||
|     [SerializeField]  | ||||
|     internal string kotlinVersion = null; | ||||
| 
 | ||||
|     [SerializeField] | ||||
|     internal bool addsAndroidBrowser = true; | ||||
| 
 | ||||
|     [SerializeField] | ||||
|     internal string androidBrowserVersion = null; | ||||
|      | ||||
|     [SerializeField] | ||||
|     internal bool addsAndroidXCore = false; | ||||
|      | ||||
|     [SerializeField] | ||||
|     internal string androidXCoreVersion = null; | ||||
| 
 | ||||
|     [SerializeField] | ||||
|     internal bool enableJetifier = true; | ||||
| 
 | ||||
|  | @ -32,14 +44,18 @@ class UniWebViewEditorSettings: ScriptableObject | |||
|     [SerializeField] | ||||
|     internal bool supportLINELogin = false; | ||||
| 
 | ||||
|     internal static string defaultKotlinVersion = "1.6.21"; | ||||
|     internal static string defaultAndroidBrowserVersion = "1.2.0"; | ||||
|     internal static string defaultAndroidXCoreVersion = "1.5.0"; | ||||
| 
 | ||||
|     internal static UniWebViewEditorSettings GetOrCreateSettings() { | ||||
|         var settings = AssetDatabase.LoadAssetAtPath<UniWebViewEditorSettings>(assetPath); | ||||
|         var settings = AssetDatabase.LoadAssetAtPath<UniWebViewEditorSettings>(AssetPath); | ||||
| 
 | ||||
|         if (settings == null) { | ||||
|             settings = ScriptableObject.CreateInstance<UniWebViewEditorSettings>(); | ||||
| 
 | ||||
|             Directory.CreateDirectory("Assets/Guru/Editor/UniWebView/"); | ||||
|             AssetDatabase.CreateAsset(settings, assetPath); | ||||
|             Directory.CreateDirectory("Assets/Editor/UniWebView/"); | ||||
|             AssetDatabase.CreateAsset(settings, AssetPath); | ||||
|             AssetDatabase.SaveAssets(); | ||||
|         } | ||||
| 
 | ||||
|  | @ -51,6 +67,24 @@ class UniWebViewEditorSettings: ScriptableObject | |||
|     } | ||||
| } | ||||
| 
 | ||||
| // UniWebViewEditorSettings is not working well with AndroidProjectFilesModifier. | ||||
| // (reading it requires main thread, but the OnModifyAndroidProjectFiles is not in main thread) | ||||
| [Serializable] | ||||
| public class UniWebViewEditorSettingsReading { | ||||
|     public bool usesCleartextTraffic = false; | ||||
|     public bool writeExternalStorage = false; | ||||
|     public bool accessFineLocation = false; | ||||
|     public bool addsKotlin = true; | ||||
|     public string kotlinVersion = null; | ||||
|     public bool addsAndroidBrowser = true; | ||||
|     public string androidBrowserVersion = null; | ||||
|     public bool addsAndroidXCore = false; | ||||
|     public string androidXCoreVersion = null; | ||||
|     public bool enableJetifier = true; | ||||
|     public string[] authCallbackUrls = { }; | ||||
|     public bool supportLINELogin = false; | ||||
| } | ||||
| 
 | ||||
| static class UniWebViewSettingsProvider { | ||||
|     static SerializedObject settings; | ||||
| 
 | ||||
|  | @ -70,6 +104,7 @@ static class UniWebViewSettingsProvider { | |||
|     #endif | ||||
|     static void DrawPref() { | ||||
|         EditorGUIUtility.labelWidth = 320; | ||||
|         EditorGUIUtility.fieldWidth = 20; | ||||
|         if (settings == null) { | ||||
|             settings = UniWebViewEditorSettings.GetSerializedSettings(); | ||||
|         } | ||||
|  | @ -100,8 +135,40 @@ static class UniWebViewSettingsProvider { | |||
|         EditorGUI.indentLevel++; | ||||
|         EditorGUILayout.PropertyField(settings.FindProperty("addsKotlin")); | ||||
|         DrawDetailLabel("Turn off this if another library is already adding Kotlin runtime."); | ||||
|         var addingKotlin = settings.FindProperty("addsKotlin").boolValue; | ||||
|         if (addingKotlin) { | ||||
|             EditorGUI.indentLevel++; | ||||
|             EditorGUILayout.PropertyField(settings.FindProperty("kotlinVersion"), GUILayout.Width(400)); | ||||
|             DrawDetailLabel("If not specified, use the default version: " + UniWebViewEditorSettings.defaultKotlinVersion); | ||||
|             EditorGUI.indentLevel--;             | ||||
|         } | ||||
| 
 | ||||
|         EditorGUILayout.PropertyField(settings.FindProperty("addsAndroidBrowser")); | ||||
|         DrawDetailLabel("Turn off this if another library is already adding 'androidx.browser:browser'."); | ||||
|         var addingBrowser = settings.FindProperty("addsAndroidBrowser").boolValue; | ||||
|         if (addingBrowser) { | ||||
|             EditorGUI.indentLevel++; | ||||
|             EditorGUILayout.PropertyField(settings.FindProperty("androidBrowserVersion"), GUILayout.Width(400)); | ||||
|             DrawDetailLabel("If not specified, use the default version: " + UniWebViewEditorSettings.defaultAndroidBrowserVersion); | ||||
|             EditorGUI.indentLevel--;             | ||||
|         } | ||||
| 
 | ||||
|         if (!addingBrowser) { | ||||
|             EditorGUILayout.BeginVertical("box"); | ||||
|             EditorGUILayout.HelpBox("UniWebView at least requires `androidx.core` to run. Without it, your game will crash when launching.\nIf you do not have another `androidx.core` package in the project, enable the option below.", MessageType.Warning); | ||||
|             EditorGUILayout.PropertyField(settings.FindProperty("addsAndroidXCore")); | ||||
|             DrawDetailLabel("Turn on this if you disabled `Adds Android Browser` and there is no other library adding 'androidx.core:core'."); | ||||
|             var addingCore = settings.FindProperty("addsAndroidXCore").boolValue; | ||||
|             if (addingCore) { | ||||
|                 EditorGUI.indentLevel++; | ||||
|                 EditorGUILayout.PropertyField(settings.FindProperty("androidXCoreVersion"), GUILayout.Width(400)); | ||||
|                 DrawDetailLabel("If not specified, use the default version: " + UniWebViewEditorSettings.defaultAndroidXCoreVersion); | ||||
|                 EditorGUI.indentLevel--;             | ||||
|             } | ||||
|             EditorGUILayout.EndVertical(); | ||||
|         } | ||||
|          | ||||
|          | ||||
|         EditorGUILayout.PropertyField(settings.FindProperty("enableJetifier")); | ||||
|         DrawDetailLabel("Turn off this if you do not need Jetifier (for converting other legacy support dependencies to Android X)."); | ||||
|         EditorGUI.indentLevel--; | ||||
|  |  | |||
|  | @ -1,15 +1,215 @@ | |||
| 
 | ||||
| // #if UNITY_2023_2_OR_NEWER | ||||
| #if UNIWEBVIEW_NEW_ANDROID_BUILD_SYSTEM | ||||
| 
 | ||||
| using System; | ||||
| using System.Linq; | ||||
| using Unity.Android.Gradle; | ||||
| using Unity.Android.Gradle.Manifest; | ||||
| using UnityEditor.Android; | ||||
| using UnityEngine; | ||||
| using Action = Unity.Android.Gradle.Manifest.Action; | ||||
| using UnityEditor.iOS.Xcode; | ||||
| 
 | ||||
| class UniWebViewPostBuildModifier : AndroidProjectFilesModifier { | ||||
|      | ||||
|     const string ContextSettingsKey = "uniwebview_settings"; | ||||
|      | ||||
|     public override AndroidProjectFilesModifierContext Setup() { | ||||
|         var context = new AndroidProjectFilesModifierContext { | ||||
|             Dependencies = { | ||||
|                 DependencyFiles = new[] { | ||||
|                     // Set UniWebView editor settings asset path to be included in the build. With that we do not need | ||||
|                     // a clean build to update the settings anymore (maybe, not confirmed). | ||||
|                     UniWebViewEditorSettings.AssetPath | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         var settings = UniWebViewEditorSettings.GetOrCreateSettings(); | ||||
|         context.SetData(ContextSettingsKey, settings); | ||||
|         return context; | ||||
|     } | ||||
| 
 | ||||
|     public override void OnModifyAndroidProjectFiles(AndroidProjectFiles projectFiles) { | ||||
|         var settings = projectFiles.GetData<UniWebViewEditorSettingsReading>(ContextSettingsKey); | ||||
|         PatchUnityLibraryAndroidManifest(projectFiles.UnityLibraryManifest, settings); | ||||
|         PatchUnityLibraryBuildGradle(projectFiles.UnityLibraryBuildGradle, settings); | ||||
|         PatchGradleProperty(projectFiles.GradleProperties, settings); | ||||
|     } | ||||
| 
 | ||||
|     private void PatchUnityLibraryAndroidManifest( | ||||
|         AndroidManifestFile manifest, UniWebViewEditorSettingsReading settings | ||||
|     ) { | ||||
|         // Set hardwareAccelerated | ||||
|         var launcherActivities = manifest.Manifest.GetActivitiesWithLauncherIntent(); | ||||
|         foreach (var activity in launcherActivities)  { | ||||
|             // Required for playing video in web view. | ||||
|             activity.Attributes.HardwareAccelerated.Set(true); | ||||
|         } | ||||
|          | ||||
|         // Set usesCleartextTraffic | ||||
|         if (settings.usesCleartextTraffic) { | ||||
|             manifest.Manifest.Application.Attributes.UsesCleartextTraffic.Set(true); | ||||
|         } | ||||
|          | ||||
|         // Set WRITE_EXTERNAL_STORAGE permission | ||||
|         if (settings.writeExternalStorage) { | ||||
|             AddUsesPermission(manifest, "android.permission.WRITE_EXTERNAL_STORAGE"); | ||||
|         } | ||||
| 
 | ||||
|         // Set ACCESS_FINE_LOCATION permission | ||||
|         if (settings.accessFineLocation) { | ||||
|             AddUsesPermission(manifest, "android.permission.ACCESS_FINE_LOCATION"); | ||||
|         } | ||||
|          | ||||
|         // Set auth callback intent filter | ||||
|         if (settings.authCallbackUrls.Length > 0 || settings.supportLINELogin) { | ||||
|             var authActivity = new Activity(); | ||||
|             authActivity.Attributes.Name.Set("com.onevcat.uniwebview.UniWebViewAuthenticationActivity"); | ||||
|             authActivity.Attributes.Exported.Set(true); | ||||
|             authActivity.Attributes.LaunchMode.Set(LaunchMode.SingleTask); | ||||
|             authActivity.Attributes.ConfigChanges.Set( | ||||
|                 new[] { | ||||
|                     ConfigChanges.Orientation, ConfigChanges.ScreenSize, ConfigChanges.KeyboardHidden | ||||
|                 }); | ||||
|              | ||||
|             foreach (var url in settings.authCallbackUrls) { | ||||
|                 AddAuthCallbacksIntentFilter(authActivity, url); | ||||
|             } | ||||
|             if (settings.supportLINELogin) { | ||||
|                 AddAuthCallbacksIntentFilter(authActivity, "lineauth://auth"); | ||||
|             } | ||||
|             manifest.Manifest.Application.ActivityList.AddElement(authActivity);             | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void PatchUnityLibraryBuildGradle(ModuleBuildGradleFile gradleFile, UniWebViewEditorSettingsReading settings) { | ||||
|         if (settings.addsKotlin) { | ||||
|             var kotlinPrefix = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:"; | ||||
|             var kotlinVersion = String.IsNullOrWhiteSpace(settings.kotlinVersion)  | ||||
|                 ? UniWebViewEditorSettings.defaultKotlinVersion : settings.kotlinVersion; | ||||
|             ReplaceContentOrAddStartsWith(gradleFile.Dependencies,  | ||||
|                 kotlinPrefix,  | ||||
|                 kotlinVersion); | ||||
|             Debug.Log("<UniWebView> Updated Kotlin dependency in build.gradle."); | ||||
|         } | ||||
| 
 | ||||
|         if (settings.addsAndroidBrowser) { | ||||
|             var browserPrefix = "androidx.browser:browser:"; | ||||
|             var browserVersion = String.IsNullOrWhiteSpace(settings.androidBrowserVersion)  | ||||
|                 ? UniWebViewEditorSettings.defaultAndroidBrowserVersion : settings.androidBrowserVersion; | ||||
|             ReplaceContentOrAddStartsWith(gradleFile.Dependencies, | ||||
|                 browserPrefix,  | ||||
|                 browserVersion); | ||||
|             Debug.Log("<UniWebView> Updated Browser dependency in build.gradle."); | ||||
|         } | ||||
| 
 | ||||
|         if (!settings.addsAndroidBrowser && settings.addsAndroidXCore) { | ||||
|             var androidXCorePrefix = "androidx.core:core:"; | ||||
|             var androidXCoreVersion = String.IsNullOrWhiteSpace(settings.androidXCoreVersion)  | ||||
|                 ? UniWebViewEditorSettings.defaultAndroidXCoreVersion : settings.androidXCoreVersion; | ||||
|             ReplaceContentOrAddStartsWith(gradleFile.Dependencies, | ||||
|                 androidXCorePrefix,  | ||||
|                 androidXCoreVersion); | ||||
|             Debug.Log("<UniWebView> Updated Android X Core dependency in build.gradle."); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void PatchGradleProperty(GradlePropertiesFile file, UniWebViewEditorSettingsReading settings) { | ||||
|         var values = file.GetElements(); | ||||
|         foreach (var ele in values) { | ||||
|             if (ele.GetRaw().Contains("android.enableJetifier")) { | ||||
|                 ele.SetRaw("android.enableJetifier=" + (settings.enableJetifier ? "true": "false")); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void AddUsesPermission(AndroidManifestFile manifest, string name) { | ||||
| 
 | ||||
|         var list = manifest.Manifest.UsesPermissionList; | ||||
|         foreach (var item in list) { | ||||
|             Debug.LogError(item.GetName()); | ||||
|         } | ||||
|          | ||||
|         var existing = manifest.Manifest.UsesPermissionList | ||||
|             .FirstOrDefault(ele => ele.Attributes.Name.Get() == name); | ||||
|         if (existing != null) { | ||||
|             return; | ||||
|         } | ||||
|         var permission = new UsesPermission(); | ||||
|         permission.Attributes.Name.Set(name); | ||||
|         manifest.Manifest.UsesPermissionList.AddElement(permission); | ||||
|     } | ||||
| 
 | ||||
|     private void AddAuthCallbacksIntentFilter(Activity activity, string callbackUrl) { | ||||
|         var uri = new Uri(callbackUrl); | ||||
|         var scheme = uri.Scheme; | ||||
|          | ||||
|         if (String.IsNullOrEmpty(scheme)) { | ||||
|             Debug.LogError("<UniWebView> Auth callback url contains an empty scheme. Please check the url: " + callbackUrl); | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         var intentFilter = new IntentFilter(); | ||||
|          | ||||
|         var action = new Action(); | ||||
|         action.Attributes.Name.Set("android.intent.action.VIEW"); | ||||
|         intentFilter.ActionList.AddElement(action); | ||||
|          | ||||
|         var defaultCategory = new Category(); | ||||
|         intentFilter.CategoryList.AddElement(defaultCategory); | ||||
|         defaultCategory.Attributes.Name.Set("android.intent.category.DEFAULT"); | ||||
| 
 | ||||
|         var browsableCategory = new Category(); | ||||
|         browsableCategory.Attributes.Name.Set("android.intent.category.BROWSABLE"); | ||||
|         intentFilter.CategoryList.AddElement(browsableCategory); | ||||
| 
 | ||||
|         var data = new Data(); | ||||
|         data.Attributes.Scheme.Set(scheme); | ||||
|         if (!String.IsNullOrEmpty(uri.Host)) { | ||||
|             data.Attributes.Host.Set(uri.Host); | ||||
|         } | ||||
|         if (uri.Port != -1) { | ||||
|             data.Attributes.Port.Set(uri.Port.ToString()); | ||||
|         } | ||||
|         if (!String.IsNullOrEmpty(uri.PathAndQuery) && uri.PathAndQuery != "/") { | ||||
|             data.Attributes.Path.Set(uri.PathAndQuery); | ||||
|         } | ||||
|         intentFilter.DataList.AddElement(data); | ||||
|         activity.IntentFilterList.AddElement(intentFilter); | ||||
|     } | ||||
| 
 | ||||
|     private void ReplaceContentOrAddStartsWith(Dependencies dependencies, string prefix, string version) { | ||||
|         var all = dependencies.GetElements(); | ||||
|         var matching = "implementation '" + prefix; | ||||
|         var found = all.FirstOrDefault(ele => ele.GetRaw().StartsWith(matching)); | ||||
|         if (found != null) { | ||||
|             found.SetRaw($"implementation '{prefix}{version}'"); | ||||
|         } else { | ||||
|             dependencies.AddDependencyImplementationRaw($"'{prefix}{version}'"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| using System; | ||||
| using UnityEditor; | ||||
| using UnityEditor.Android; | ||||
| using UnityEngine; | ||||
| using System.IO; | ||||
| using System.Text; | ||||
| 
 | ||||
| using UnityEditor.Callbacks; | ||||
| #if UNITY_IOS | ||||
| using UnityEditor.iOS.Xcode; | ||||
| #endif | ||||
| using System.Linq; | ||||
| 
 | ||||
| class UniWebViewPostBuildProcessor : IPostGenerateGradleAndroidProject | ||||
| { | ||||
|     public int callbackOrder { get { return 1; } } | ||||
|     public void OnPostGenerateGradleAndroidProject(string path) { | ||||
|         Debug.Log("<UniWebView> UniWebView Post Build Scirpt is patching manifest file and gradle file..."); | ||||
|         Debug.Log("<UniWebView> UniWebView Post Build Script is patching manifest file and gradle file..."); | ||||
|         PatchAndroidManifest(path); | ||||
|         PatchBuildGradle(path); | ||||
|         PatchGradleProperty(path); | ||||
|  | @ -51,47 +251,66 @@ class UniWebViewPostBuildProcessor : IPostGenerateGradleAndroidProject | |||
|         var gradleFilePath = GetGradleFilePath(root); | ||||
|         var config = new UniWebViewGradleConfig(gradleFilePath); | ||||
| 
 | ||||
|         var settings = UniWebViewEditorSettings.GetOrCreateSettings(); | ||||
|          | ||||
|         var kotlinPrefix = "implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:"; | ||||
|         var kotlinVersion = "1.6.20'"; | ||||
|         var kotlinVersion = String.IsNullOrWhiteSpace(settings.kotlinVersion)  | ||||
|             ? UniWebViewEditorSettings.defaultKotlinVersion : settings.kotlinVersion; | ||||
| 
 | ||||
|         var browserPrefix = "implementation 'androidx.browser:browser:"; | ||||
|         var browserVersion = "1.2.0'"; | ||||
|         var browserVersion = String.IsNullOrWhiteSpace(settings.androidBrowserVersion)  | ||||
|             ? UniWebViewEditorSettings.defaultAndroidBrowserVersion : settings.androidBrowserVersion; | ||||
| 
 | ||||
|         var settings = UniWebViewEditorSettings.GetOrCreateSettings(); | ||||
| 
 | ||||
|         var dependenciesNode = config.ROOT.FindChildNodeByName("dependencies"); | ||||
|         var androidXCorePrefix = "implementation 'androidx.core:core:"; | ||||
|         var androidXCoreVersion = String.IsNullOrWhiteSpace(settings.androidXCoreVersion)  | ||||
|             ? UniWebViewEditorSettings.defaultAndroidXCoreVersion : settings.androidXCoreVersion; | ||||
|          | ||||
|         var dependenciesNode = config.Root.FindChildNodeByName("dependencies"); | ||||
|         if (dependenciesNode != null) { | ||||
|             // Add kotlin | ||||
|             if (settings.addsKotlin) { | ||||
|                 dependenciesNode.ReplaceContenOrAddStartsWith(kotlinPrefix, kotlinPrefix + kotlinVersion); | ||||
|                 dependenciesNode.ReplaceContentOrAddStartsWith(kotlinPrefix, kotlinPrefix + kotlinVersion + "'"); | ||||
|                 Debug.Log("<UniWebView> Updated Kotlin dependency in build.gradle."); | ||||
|             } | ||||
| 
 | ||||
|             // Add browser package | ||||
|             if (settings.addsAndroidBrowser) { | ||||
|                 dependenciesNode.ReplaceContenOrAddStartsWith(browserPrefix, browserPrefix + browserVersion); | ||||
|                 dependenciesNode.ReplaceContentOrAddStartsWith(browserPrefix, browserPrefix + browserVersion + "'"); | ||||
|                 Debug.Log("<UniWebView> Updated Browser dependency in build.gradle."); | ||||
|             } | ||||
| 
 | ||||
|             // Add Android X Core package | ||||
|             if (!settings.addsAndroidBrowser && settings.addsAndroidXCore) { | ||||
|                 // When adding android browser to the project, we don't need to add Android X Core package, since gradle resolves for it. | ||||
|                 dependenciesNode.ReplaceContentOrAddStartsWith(androidXCorePrefix, androidXCorePrefix + androidXCoreVersion + "'"); | ||||
|                 Debug.Log("<UniWebView> Updated Android X Core dependency in build.gradle."); | ||||
|             } | ||||
|         } else { | ||||
|             Debug.LogError("UniWebViewPostBuildProcessor didn't find the `dependencies` field in build.gradle."); | ||||
|             Debug.LogError("Although we can continue to add a `dependencies`, make sure you have setup Gradle and the template correctly."); | ||||
| 
 | ||||
|             var newNode = new UniWebViewGradleNode("dependencies", config.ROOT); | ||||
|             var newNode = new UniWebViewGradleNode("dependencies", config.Root); | ||||
|             if (settings.addsKotlin) { | ||||
|                 newNode.AppendContentNode(kotlinPrefix + kotlinVersion); | ||||
|                 newNode.AppendContentNode(kotlinPrefix + kotlinVersion + "'"); | ||||
|             } | ||||
|             if (settings.addsAndroidBrowser) { | ||||
|                 newNode.AppendContentNode(browserPrefix + browserVersion); | ||||
|                 newNode.AppendContentNode(browserPrefix + browserVersion + "'"); | ||||
|             } | ||||
| 
 | ||||
|             if (settings.addsAndroidXCore) { | ||||
|                 newNode.AppendContentNode(androidXCorePrefix + androidXCoreVersion + "'"); | ||||
|             } | ||||
|             newNode.AppendContentNode("implementation(name: 'UniWebView', ext:'aar')"); | ||||
|             config.ROOT.AppendChildNode(newNode); | ||||
|             config.Root.AppendChildNode(newNode); | ||||
|         } | ||||
|         config.Save(); | ||||
|     } | ||||
| 
 | ||||
|     private void PatchGradleProperty(string root) { | ||||
|         var gradlePropertyFilePath = GetGradlePropertyFilePath(root); | ||||
|         UniWebViewGradlePropertyPatcher.Patch(gradlePropertyFilePath); | ||||
|         var patcher = | ||||
|             new UniWebViewGradlePropertyPatcher(gradlePropertyFilePath, UniWebViewEditorSettings.GetOrCreateSettings()); | ||||
|         patcher.Patch(); | ||||
|     } | ||||
| 
 | ||||
|     private string CombinePaths(string[] paths) { | ||||
|  | @ -120,4 +339,50 @@ class UniWebViewPostBuildProcessor : IPostGenerateGradleAndroidProject | |||
|         #endif | ||||
|         return CombinePaths(compos); | ||||
|     } | ||||
| } | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #if UNITY_IOS | ||||
| public class UniWebViewPostBuildProcessorIOS | ||||
| { | ||||
|     [PostProcessBuild(1)] | ||||
|     public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) | ||||
|     { | ||||
|         if (target == BuildTarget.iOS) { | ||||
|             var projectPath = PBXProject.GetPBXProjectPath(pathToBuiltProject); | ||||
|              | ||||
|             var settings = UniWebViewEditorSettings.GetOrCreateSettings(); | ||||
|             if (settings.authCallbackUrls.Length > 0) { | ||||
|                 var domains = GetHttpsAssociatedDomains(settings.authCallbackUrls); | ||||
|                 if (domains.Length > 0) { | ||||
|                     Debug.Log("<UniWebView> UniWebView Post Build Script is patching associated domains for auth callbacks..."); | ||||
|                     AddAssociatedDomain(projectPath, domains); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static string[] GetHttpsAssociatedDomains(string[] urls) { | ||||
|         return urls | ||||
|             .Where(url => Uri.TryCreate(url, UriKind.Absolute, out Uri uri) && uri.Scheme == "https") | ||||
|             .Select(url => new Uri(url).Host) | ||||
|             .Distinct() | ||||
|             .Select(domain => "applinks:" + domain) | ||||
|             .ToArray(); | ||||
|     } | ||||
| 
 | ||||
|     public static void AddAssociatedDomain(string projectPath, string[] domains) { | ||||
|          | ||||
|         PBXProject project = new PBXProject(); | ||||
|         project.ReadFromString(File.ReadAllText(projectPath)); | ||||
| 
 | ||||
|         var entitlementsFileName = "Unity-iPhone.entitlements"; | ||||
|         var targetGUID = project.GetUnityMainTargetGuid(); | ||||
|         var capabilityManager = new ProjectCapabilityManager(projectPath, entitlementsFileName, null, targetGUID); | ||||
| 
 | ||||
|         capabilityManager.AddAssociatedDomains(domains); | ||||
| 
 | ||||
|         capabilityManager.WriteToFile(); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | @ -2,7 +2,7 @@ fileFormatVersion: 2 | |||
| guid: dc0f52a2d219347b1a6390b753d6ac97 | ||||
| folderAsset: yes | ||||
| timeCreated: 1491898971 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| DefaultImporter: | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|  |  | |||
|  | @ -2,6 +2,17 @@ | |||
| 
 | ||||
| using UnityEngine; | ||||
| 
 | ||||
| class UniWebViewMethodChannel: AndroidJavaProxy | ||||
|     { | ||||
|         public UniWebViewMethodChannel() : base("com.onevcat.uniwebview.UniWebViewNativeChannel") { } | ||||
| 
 | ||||
|         string invokeChannelMethod(string name, string method, string parameters) { | ||||
|             UniWebViewLogger.Instance.Verbose("invokeChannelMethod invoked by native side. Name: " + name + " Method: "  | ||||
|                                           + method + " Params: " + parameters); | ||||
|             return UniWebViewChannelMethodManager.Instance.InvokeMethod(name, method, parameters); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| public class UniWebViewInterface { | ||||
|     private static readonly AndroidJavaClass plugin; | ||||
|     private static bool correctPlatform = Application.platform == RuntimePlatform.Android; | ||||
|  | @ -10,39 +21,15 @@ public class UniWebViewInterface { | |||
|         var go = new GameObject("UniWebViewAndroidStaticListener"); | ||||
|         go.AddComponent<UniWebViewAndroidStaticListener>(); | ||||
|         plugin = new AndroidJavaClass("com.onevcat.uniwebview.UniWebViewInterface"); | ||||
|          | ||||
|         CheckPlatform(); | ||||
| 
 | ||||
|         plugin.CallStatic("prepare"); | ||||
| 
 | ||||
|         UniWebViewLogger.Instance.Info("Connecting to native side method channel."); | ||||
|         plugin.CallStatic("registerChannel", new UniWebViewMethodChannel()); | ||||
|     } | ||||
| 
 | ||||
|     //OMSDK part------- | ||||
| 
 | ||||
|     public static void InitOMSDK(string jsonSettings, string omidJSServiceContent) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("initOMSDK",jsonSettings,omidJSServiceContent);  | ||||
|     } | ||||
| 
 | ||||
|     public static void InitOMSDKSession(string resourceUrl) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("initOMSDKSession",resourceUrl);  | ||||
|     } | ||||
| 
 | ||||
|     public static void StartImpression(string resourceUrl) {  | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("startImpression",resourceUrl);  | ||||
|     } | ||||
| 
 | ||||
|     public static void StopImpression(string resourceUrl) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("stopImpression",resourceUrl);  | ||||
|     } | ||||
| 
 | ||||
|     public static void StopOMIDAdSession(string name) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("stopOMIDAdSession",name);  | ||||
|     } | ||||
| 
 | ||||
|     //-------- | ||||
| 
 | ||||
|     public static void SetLogLevel(int level) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("setLogLevel", level);  | ||||
|  | @ -212,6 +199,15 @@ public class UniWebViewInterface { | |||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("setAllowUniversalAccessFromFileURLs", flag); | ||||
|     } | ||||
|     public static void BringContentToFront(string name) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("bringContentToFront", name); | ||||
|     } | ||||
| 
 | ||||
|     public static void SetForwardWebConsoleToNativeOutput(bool flag) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("setForwardWebConsoleToNativeOutput", flag); | ||||
|     } | ||||
| 
 | ||||
|     public static void SetEnableKeyboardAvoidance(bool flag) { | ||||
|         CheckPlatform(); | ||||
|  | @ -228,6 +224,11 @@ public class UniWebViewInterface { | |||
|         plugin.CallStatic("cleanCache", name); | ||||
|     } | ||||
| 
 | ||||
|     public static void SetCacheMode(string name, int mode) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("setCacheMode", name, mode); | ||||
|     } | ||||
| 
 | ||||
|     public static void ClearCookies() { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("clearCookies"); | ||||
|  | @ -283,6 +284,21 @@ public class UniWebViewInterface { | |||
|         plugin.CallStatic("setSpinnerText", name, text); | ||||
|     } | ||||
| 
 | ||||
|     public static void SetAllowUserDismissSpinnerByGesture(string name, bool flag) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("setAllowUserDismissSpinnerByGesture", name, flag); | ||||
|     } | ||||
| 
 | ||||
|     public static void ShowSpinner(string name) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("showSpinner", name); | ||||
|     } | ||||
| 
 | ||||
|     public static void HideSpinner(string name) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("hideSpinner", name); | ||||
|     } | ||||
| 
 | ||||
|     public static bool CanGoBack(string name) { | ||||
|         CheckPlatform(); | ||||
|         return plugin.CallStatic<bool>("canGoBack", name); | ||||
|  | @ -387,6 +403,11 @@ public class UniWebViewInterface { | |||
|         plugin.CallStatic("setSupportMultipleWindows", name, enabled, allowJavaScriptOpening); | ||||
|     } | ||||
| 
 | ||||
|     public static void SetDragInteractionEnabled(string name, bool flag) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("setDragInteractionEnabled", name, flag); | ||||
|     } | ||||
| 
 | ||||
|     public static void SetDefaultFontSize(string name, int size) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("setDefaultFontSize", name, size); | ||||
|  | @ -412,6 +433,11 @@ public class UniWebViewInterface { | |||
|         plugin.CallStatic("setDownloadEventForContextMenuEnabled", name, enabled); | ||||
|     } | ||||
| 
 | ||||
|     public static void SetAllowUserEditFileNameBeforeDownloading(string name, bool allowed) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("setAllowUserEditFileNameBeforeDownloading", name, allowed); | ||||
|     } | ||||
| 
 | ||||
|     // Safe Browsing | ||||
| 
 | ||||
|     public static bool IsSafeBrowsingSupported() { | ||||
|  | @ -506,6 +532,31 @@ public class UniWebViewInterface { | |||
|         plugin.CallStatic("setEmbeddedToolbarNavigationButtonsShow", name, show); | ||||
|     } | ||||
| 
 | ||||
|     public static void StartSnapshotForRendering(string name, string identifier) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("startSnapshotForRendering", name, identifier); | ||||
|     } | ||||
| 
 | ||||
|     public static void StopSnapshotForRendering(string name) { | ||||
|         CheckPlatform(); | ||||
|         plugin.CallStatic("stopSnapshotForRendering", name); | ||||
|     } | ||||
| 
 | ||||
|     public static byte[] GetRenderedData(string name, int x, int y, int width, int height) { | ||||
|         CheckPlatform(); | ||||
|         var sbyteArray = plugin.CallStatic<sbyte[]>("getRenderedData", name, x, y, width, height); | ||||
|         if (sbyteArray == null) { | ||||
|             return null; | ||||
|         } | ||||
|         int length = sbyteArray.Length; | ||||
|         byte[] byteArray = new byte[length]; | ||||
|          | ||||
|         for (int i = 0; i < length; i++) { | ||||
|             byteArray[i] = (byte)sbyteArray[i]; | ||||
|         }    | ||||
|         return byteArray; | ||||
|     } | ||||
| 
 | ||||
|     // Platform | ||||
| 
 | ||||
|     public static void CheckPlatform() { | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 5a1d3cecc27d64565835e14b493c935b | ||||
| timeCreated: 1490880130 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ public class UniWebViewAndroidStaticListener: MonoBehaviour { | |||
| 
 | ||||
|     void OnJavaMessage(string message) { | ||||
|         // {listener_name}@{method_name}@parameters | ||||
|         string[] parts = message.Split("@"[0]); | ||||
|         var parts = message.Split("@"[0]); | ||||
|         if (parts.Length < 3) { | ||||
|             Debug.Log("Not enough parts for receiving a message."); | ||||
|             return; | ||||
|  | @ -25,6 +25,7 @@ public class UniWebViewAndroidStaticListener: MonoBehaviour { | |||
|         MethodInfo methodInfo = typeof(UniWebViewNativeListener).GetMethod(parts[1]); | ||||
|         if (methodInfo == null) { | ||||
|             Debug.Log("Cannot find correct method to invoke: " + parts[1]); | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         var leftLength = parts.Length - 2; | ||||
|  | @ -32,7 +33,7 @@ public class UniWebViewAndroidStaticListener: MonoBehaviour { | |||
|         for (int i = 0; i < leftLength; i++) { | ||||
|             left[i] = parts[i + 2]; | ||||
|         } | ||||
|         methodInfo.Invoke(listener, new object[] { String.Join("@", left) }); | ||||
|         methodInfo.Invoke(listener, new object[] { string.Join("@", left) }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 2704cf8e127d541f1888d96429308645 | ||||
| timeCreated: 1514387178 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|  |  | |||
|  | @ -23,8 +23,12 @@ using AOT; | |||
| using System.Reflection; | ||||
| 
 | ||||
| public class UniWebViewInterface { | ||||
|      | ||||
|     private const string StaticListenerName = "UniWebView-static"; | ||||
|      | ||||
|     static UniWebViewInterface() { | ||||
|         ConnectMessageSender(); | ||||
|         RegisterChannel(); | ||||
|     } | ||||
| 
 | ||||
|     delegate void UnitySendMessageDelegate(IntPtr objectName, IntPtr methodName, IntPtr parameter); | ||||
|  | @ -67,6 +71,13 @@ public class UniWebViewInterface { | |||
|             "Received message sent from native. Name: " + name + " Method: " + method + " Params: " + parameters | ||||
|         ); | ||||
| 
 | ||||
|         if (name == StaticListenerName) { | ||||
|             MethodInfo methodInfo = typeof(UniWebViewStaticListener) | ||||
|                 .GetMethod(method, BindingFlags.Static | BindingFlags.Public); | ||||
|             methodInfo.Invoke(null, new object[] { parameters }); | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         var listener = UniWebViewNativeListener.GetListener(name); | ||||
|         if (listener) { | ||||
|             MethodInfo methodInfo = typeof(UniWebViewNativeListener).GetMethod(method); | ||||
|  | @ -75,63 +86,28 @@ public class UniWebViewInterface { | |||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     //------- | ||||
| 
 | ||||
|      | ||||
|     delegate string ChannelMethodDelegate(IntPtr namePtr, IntPtr methodPtr, IntPtr parameterPtr); | ||||
|      | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_stopOMIDAdSession(string name); | ||||
|     public static void StopOMIDAdSession(string name) | ||||
|     { | ||||
|     private static extern void uv_registerChannel([MarshalAs(UnmanagedType.FunctionPtr)] ChannelMethodDelegate channel); | ||||
|     public static void RegisterChannel() { | ||||
|         UniWebViewLogger.Instance.Info("Connecting to native side method channel."); | ||||
|         CheckPlatform(); | ||||
| 
 | ||||
|         if (String.IsNullOrEmpty(name)) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         uv_stopOMIDAdSession(name); | ||||
|         uv_registerChannel(ChannelFunc); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_initOMSDK(string jsonSettings, string omidJSServiceContent); | ||||
|     public static void InitOMSDK(string jsonSettings, string omidJSServiceContent) | ||||
|     { | ||||
|         CheckPlatform(); | ||||
|     [MonoPInvokeCallback(typeof(ChannelMethodDelegate))] | ||||
|     private static string ChannelFunc(IntPtr namePtr, IntPtr methodPtr, IntPtr parameterPtr) { | ||||
|         string name = Marshal.PtrToStringAuto(namePtr); | ||||
|         string method = Marshal.PtrToStringAuto(methodPtr); | ||||
|         string parameters = Marshal.PtrToStringAuto(parameterPtr); | ||||
| 
 | ||||
|         uv_initOMSDK(jsonSettings, omidJSServiceContent); | ||||
|         UniWebViewLogger.Instance.Verbose("ChannelFunc invoked by native side. Name: " + name + " Method: "  | ||||
|                                           + method + " Params: " + parameters); | ||||
|         return UniWebViewChannelMethodManager.Instance.InvokeMethod(name, method, parameters); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_initOMSDKSession(string resourceUrl); | ||||
|     public static void InitOMSDKSession(string resourceUrl) | ||||
|     { | ||||
|         CheckPlatform(); | ||||
| 
 | ||||
|         uv_initOMSDKSession(resourceUrl); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_startImpression(string resourceUrl); | ||||
|     public static void StartImpression(string resourceUrl) | ||||
|     { | ||||
|         CheckPlatform(); | ||||
| 
 | ||||
|         uv_startImpression(resourceUrl); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_stopImpression(string resourceUrl); | ||||
|     public static void StopImpression(string resourceUrl) | ||||
|     { | ||||
|         CheckPlatform(); | ||||
| 
 | ||||
|         uv_stopImpression(resourceUrl); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     //-------- | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_setLogLevel(int level); | ||||
|     public static void SetLogLevel(int level) { | ||||
|  | @ -344,6 +320,13 @@ public class UniWebViewInterface { | |||
|         CheckPlatform(); | ||||
|         uv_setAllowUniversalAccessFromFileURLs(flag); | ||||
|     } | ||||
|      | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_setForwardWebConsoleToNativeOutput(bool flag); | ||||
|     public static void SetForwardWebConsoleToNativeOutput(bool flag) { | ||||
|         CheckPlatform(); | ||||
|         uv_setForwardWebConsoleToNativeOutput(flag); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_setAllowJavaScriptOpenWindow(bool flag); | ||||
|  | @ -358,6 +341,13 @@ public class UniWebViewInterface { | |||
|         CheckPlatform(); | ||||
|         uv_setJavaScriptEnabled(flag); | ||||
|     } | ||||
|      | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_setLimitsNavigationsToAppBoundDomains(bool flag); | ||||
|     public static void SetLimitsNavigationsToAppBoundDomains(bool flag) { | ||||
|         CheckPlatform(); | ||||
|         uv_setLimitsNavigationsToAppBoundDomains(flag); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_cleanCache(string name); | ||||
|  | @ -366,6 +356,13 @@ public class UniWebViewInterface { | |||
|         uv_cleanCache(name); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_setCacheMode(string name, int mode); | ||||
|     public static void SetCacheMode(string name, int mode) { | ||||
|         CheckPlatform(); | ||||
|         uv_setCacheMode(name, mode); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_clearCookies(); | ||||
|     public static void ClearCookies() { | ||||
|  | @ -443,6 +440,27 @@ public class UniWebViewInterface { | |||
|         uv_setSpinnerText(name, text); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_setAllowUserDismissSpinnerByGesture(string name, bool flag); | ||||
|     public static void SetAllowUserDismissSpinnerByGesture(string name, bool flag) { | ||||
|         CheckPlatform(); | ||||
|         uv_setAllowUserDismissSpinnerByGesture(name, flag); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_showSpinner(string name); | ||||
|     public static void ShowSpinner(string name) { | ||||
|         CheckPlatform(); | ||||
|         uv_showSpinner(name); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_hideSpinner(string name); | ||||
|     public static void HideSpinner(string name) { | ||||
|         CheckPlatform(); | ||||
|         uv_hideSpinner(name); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern bool uv_canGoBack(string name); | ||||
|     public static bool CanGoBack(string name) { | ||||
|  | @ -637,7 +655,14 @@ public class UniWebViewInterface { | |||
|         CheckPlatform(); | ||||
|         uv_removeDownloadMIMETypes(name, MIMEType, type); | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_setAllowUserEditFileNameBeforeDownloading(string name, bool allowed); | ||||
|     public static void SetAllowUserEditFileNameBeforeDownloading(string name, bool allowed) { | ||||
|         CheckPlatform(); | ||||
|         uv_setAllowUserEditFileNameBeforeDownloading(name, allowed); | ||||
|     } | ||||
|      | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_setAllowUserChooseActionAfterDownloading(string name, bool allowed); | ||||
|     public static void SetAllowUserChooseActionAfterDownloading(string name, bool allowed) { | ||||
|  | @ -703,6 +728,13 @@ public class UniWebViewInterface { | |||
|         CheckPlatform(); | ||||
|         uv_authenticationStart(name); | ||||
|     } | ||||
|      | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_authenticationCancel(string name); | ||||
|     public static void AuthenticationCancel(string name) { | ||||
|         CheckPlatform(); | ||||
|         uv_authenticationCancel(name); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_authenticationSetPrivateMode(string name, bool flag); | ||||
|  | @ -781,6 +813,33 @@ public class UniWebViewInterface { | |||
|         uv_setEmbeddedToolbarNavigationButtonsShow(name, show); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_startSnapshotForRendering(string name, string identifier); | ||||
|     public static void StartSnapshotForRendering(string name, string identifier) { | ||||
|         CheckPlatform(); | ||||
|         uv_startSnapshotForRendering(name, identifier); | ||||
|     } | ||||
| 
 | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern void uv_stopSnapshotForRendering(string name); | ||||
|     public static void StopSnapshotForRendering(string name) { | ||||
|         CheckPlatform(); | ||||
|         uv_stopSnapshotForRendering(name); | ||||
|     } | ||||
|      | ||||
|     [DllImport(DllLib)] | ||||
|     private static extern IntPtr uv_getRenderedData(string name, int x, int y, int width, int height, out int length); | ||||
|     public static byte[] GetRenderedData(string name, int x, int y, int width, int height) { | ||||
|         CheckPlatform(); | ||||
| 
 | ||||
|         IntPtr dataPtr = uv_getRenderedData(name, x, y, width, height, out var length); | ||||
|          | ||||
|         byte[] managedData = new byte[length]; | ||||
|         Marshal.Copy(dataPtr, managedData, 0, length); | ||||
|          | ||||
|         return managedData; | ||||
|     } | ||||
| 
 | ||||
|     #region Deprecated | ||||
|      | ||||
|     [DllImport(DllLib)] | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 2e905ba0b47304f4cbf9e3e3345f84eb | ||||
| timeCreated: 1492400358 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|  |  | |||
|  | @ -6,12 +6,6 @@ public class UniWebViewInterface { | |||
|      | ||||
|     private static bool alreadyLoggedWarning = false; | ||||
| 
 | ||||
|     public static void StopOMIDAdSession(string name) { CheckPlatform(); }   | ||||
|     public static void InitOMSDK(string jsonSettings, string omidJSServiceContent) { CheckPlatform(); }   | ||||
|     public static void InitOMSDKSession(string resourceUrl) { CheckPlatform(); }   | ||||
|     public static void StartImpression(string resourceUrl) { CheckPlatform(); }   | ||||
|     public static void StopImpression(string resourceUrl) { CheckPlatform(); }   | ||||
| 
 | ||||
|     public static void SetLogLevel(int level) { CheckPlatform(); } | ||||
|     public static void Init(string name, int x, int y, int width, int height) { CheckPlatform(); } | ||||
|     public static void Destroy(string name) { CheckPlatform(); } | ||||
|  | @ -38,11 +32,14 @@ public class UniWebViewInterface { | |||
|     public static void SetAllowAutoPlay(bool flag) { CheckPlatform(); } | ||||
|     public static void SetAllowInlinePlay(bool flag) { CheckPlatform(); } | ||||
|     public static void SetAllowJavaScriptOpenWindow(bool flag) { CheckPlatform(); } | ||||
|     public static void SetForwardWebConsoleToNativeOutput(bool flag) { CheckPlatform(); } | ||||
|     public static void SetAllowFileAccess(string name, bool flag) { CheckPlatform(); } | ||||
|     public static void SetAllowFileAccessFromFileURLs(string name, bool flag) { CheckPlatform(); } | ||||
|     public static void SetAllowUniversalAccessFromFileURLs(bool flag) { CheckPlatform(); } | ||||
|     public static void SetJavaScriptEnabled(bool flag) { CheckPlatform(); } | ||||
|     public static void SetLimitsNavigationsToAppBoundDomains(bool enabled) { CheckPlatform(); } | ||||
|     public static void CleanCache(string name) { CheckPlatform(); } | ||||
|     public static void SetCacheMode(string name, int mode) { CheckPlatform(); } | ||||
|     public static void ClearCookies() { CheckPlatform(); } | ||||
|     public static void SetCookie(string url, string cookie, bool skipEncoding) { CheckPlatform(); } | ||||
|     public static void RemoveCookies(string url, bool skipEncoding) { CheckPlatform(); } | ||||
|  | @ -54,6 +51,9 @@ public class UniWebViewInterface { | |||
|     public static float GetWebViewAlpha(string name) { CheckPlatform(); return 0.0f; } | ||||
|     public static void SetShowSpinnerWhileLoading(string name, bool show) { CheckPlatform(); } | ||||
|     public static void SetSpinnerText(string name, string text) { CheckPlatform(); } | ||||
|     public static void SetAllowUserDismissSpinnerByGesture(string name, bool flag) { CheckPlatform(); } | ||||
|     public static void ShowSpinner(string name) { CheckPlatform(); } | ||||
|     public static void HideSpinner(string name) { CheckPlatform(); } | ||||
|     public static bool CanGoBack(string name) { CheckPlatform(); return false; } | ||||
|     public static bool CanGoForward(string name) { CheckPlatform(); return false; } | ||||
|     public static void GoBack(string name) { CheckPlatform(); } | ||||
|  | @ -73,6 +73,7 @@ public class UniWebViewInterface { | |||
|     public static void SetTransparencyClickingThroughEnabled(string name, bool enabled) { CheckPlatform(); } | ||||
|     public static void SetWebContentsDebuggingEnabled(bool enabled) { CheckPlatform(); } | ||||
|     public static void SetAllowHTTPAuthPopUpWindow(string name, bool flag) { CheckPlatform(); } | ||||
|     public static void SetAllowUserEditFileNameBeforeDownloading(string name, bool allowed) { CheckPlatform(); } | ||||
|     public static void Print(string name) { CheckPlatform(); } | ||||
|     public static void CaptureSnapshot(string name, string filename) { CheckPlatform(); } | ||||
|     public static void SetCalloutEnabled(string name, bool flag) { CheckPlatform(); } | ||||
|  | @ -98,6 +99,9 @@ public class UniWebViewInterface { | |||
|     public static void SetEmbeddedToolbarButtonTextColor(string name, Color color) { CheckPlatform(); } | ||||
|     public static void SetEmbeddedToolbarTitleTextColor(string name, Color color) { CheckPlatform(); } | ||||
|     public static void SetEmeddedToolbarNavigationButtonsShow(string name, bool show) { CheckPlatform(); } | ||||
|     public static void StartSnapshotForRendering(string name, string identifier) { CheckPlatform(); } | ||||
|     public static void StopSnapshotForRendering(string name) { CheckPlatform(); } | ||||
|     public static byte[] GetRenderedData(string name, int x, int y, int width, int height) { CheckPlatform(); return null; } | ||||
| 
 | ||||
|     public static void CheckPlatform() { | ||||
|         if (!alreadyLoggedWarning) { | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 0b3bad20d12b1433ab8927c3effc605b | ||||
| timeCreated: 1497403102 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ fileFormatVersion: 2 | |||
| guid: d5d657f2ee1114e20bccaace74235a99 | ||||
| folderAsset: yes | ||||
| timeCreated: 1491898971 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| DefaultImporter: | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 7e3f16a6f6303419cbd9837f6c746de4 | ||||
| timeCreated: 1496204510 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| NativeFormatImporter: | ||||
|   mainObjectFileID: 100100000 | ||||
|   userData:  | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 95440ce70c5e14d6e96e166c45f7cf6d | ||||
| timeCreated: 1499302025 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| TextScriptImporter: | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ fileFormatVersion: 2 | |||
| guid: b34f36f9836464e04893f632434d0862 | ||||
| folderAsset: yes | ||||
| timeCreated: 1491898971 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| DefaultImporter: | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: e0dc774ebc4264ab3982157e12fd428b | ||||
| guid: 43c2ab2efc7244e0293d0a1cb0d869e8 | ||||
| folderAsset: yes | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|  | @ -0,0 +1,551 @@ | |||
| /* | ||||
|  * Copyright (c) 2013 Calvin Rien | ||||
|  * | ||||
|  * Based on the JSON parser by Patrick van Bergen | ||||
|  * http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html | ||||
|  * | ||||
|  * Simplified it so that it doesn't throw exceptions | ||||
|  * and can be used in Unity iPhone with maximum code stripping. | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining | ||||
|  * a copy of this software and associated documentation files (the | ||||
|  * "Software"), to deal in the Software without restriction, including | ||||
|  * without limitation the rights to use, copy, modify, merge, publish, | ||||
|  * distribute, sublicense, and/or sell copies of the Software, and to | ||||
|  * permit persons to whom the Software is furnished to do so, subject to | ||||
|  * the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be | ||||
|  * included in all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||
|  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
|  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||
|  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||||
|  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||||
|  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| using System; | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using System.Text; | ||||
| 
 | ||||
| namespace UniWebViewExternal { | ||||
|     // Example usage: | ||||
|     // | ||||
|     //  using UnityEngine; | ||||
|     //  using System.Collections; | ||||
|     //  using System.Collection | ||||
|     // | ||||
|     // | ||||
|     // | ||||
|     // | ||||
|     // | ||||
|     // | ||||
|     // | ||||
|     // | ||||
|     // | ||||
|     // | ||||
|     // | ||||
|     //   s.Generic; | ||||
|     //  using MiniJSON; | ||||
|     // | ||||
|     //  public class MiniJSONTest : MonoBehaviour { | ||||
|     //      void Start () { | ||||
|     //          var jsonString = "{ \"array\": [1.44,2,3], " + | ||||
|     //                          "\"object\": {\"key1\":\"value1\", \"key2\":256}, " + | ||||
|     //                          "\"string\": \"The quick brown fox \\\"jumps\\\" over the lazy dog \", " + | ||||
|     //                          "\"unicode\": \"\\u3041 Men\u00fa sesi\u00f3n\", " + | ||||
|     //                          "\"int\": 65536, " + | ||||
|     //                          "\"float\": 3.1415926, " + | ||||
|     //                          "\"bool\": true, " + | ||||
|     //                          "\"null\": null }"; | ||||
|     // | ||||
|     //          var dict = Json.Deserialize(jsonString) as Dictionary<string,object>; | ||||
|     // | ||||
|     //          Debug.Log("deserialized: " + dict.GetType()); | ||||
|     //          Debug.Log("dict['array'][0]: " + ((List<object>) dict["array"])[0]); | ||||
|     //          Debug.Log("dict['string']: " + (string) dict["string"]); | ||||
|     //          Debug.Log("dict['float']: " + (double) dict["float"]); // floats come out as doubles | ||||
|     //          Debug.Log("dict['int']: " + (long) dict["int"]); // ints come out as longs | ||||
|     //          Debug.Log("dict['unicode']: " + (string) dict["unicode"]); | ||||
|     // | ||||
|     //          var str = Json.Serialize(dict); | ||||
|     // | ||||
|     //          Debug.Log("serialized: " + str); | ||||
|     //      } | ||||
|     //  } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// This class encodes and decodes JSON strings. | ||||
|     /// Spec. details, see http://www.json.org/ | ||||
|     /// | ||||
|     /// JSON uses Arrays and Objects. These correspond here to the datatypes IList and IDictionary. | ||||
|     /// All numbers are parsed to doubles. | ||||
|     /// </summary> | ||||
|     public static class Json { | ||||
|         /// <summary> | ||||
|         /// Parses the string json into a value | ||||
|         /// </summary> | ||||
|         /// <param name="json">A JSON string.</param> | ||||
|         /// <returns>An List<object>, a Dictionary<string, object>, a double, an integer,a string, null, true, or false</returns> | ||||
|         public static object Deserialize(string json) { | ||||
|             // save the string for debug information | ||||
|             if (json == null) { | ||||
|                 return null; | ||||
|             } | ||||
| 
 | ||||
|             return Parser.Parse(json); | ||||
|         } | ||||
| 
 | ||||
|         sealed class Parser : IDisposable { | ||||
|             const string WORD_BREAK = "{}[],:\""; | ||||
| 
 | ||||
|             public static bool IsWordBreak(char c) { | ||||
|                 return Char.IsWhiteSpace(c) || WORD_BREAK.IndexOf(c) != -1; | ||||
|             } | ||||
| 
 | ||||
|             enum TOKEN { | ||||
|                 NONE, | ||||
|                 CURLY_OPEN, | ||||
|                 CURLY_CLOSE, | ||||
|                 SQUARED_OPEN, | ||||
|                 SQUARED_CLOSE, | ||||
|                 COLON, | ||||
|                 COMMA, | ||||
|                 STRING, | ||||
|                 NUMBER, | ||||
|                 TRUE, | ||||
|                 FALSE, | ||||
|                 NULL | ||||
|             }; | ||||
| 
 | ||||
|             StringReader json; | ||||
| 
 | ||||
|             Parser(string jsonString) { | ||||
|                 json = new StringReader(jsonString); | ||||
|             } | ||||
| 
 | ||||
|             public static object Parse(string jsonString) { | ||||
|                 using (var instance = new Parser(jsonString)) { | ||||
|                     return instance.ParseValue(); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             public void Dispose() { | ||||
|                 json.Dispose(); | ||||
|                 json = null; | ||||
|             } | ||||
| 
 | ||||
|             Dictionary<string, object> ParseObject() { | ||||
|                 Dictionary<string, object> table = new Dictionary<string, object>(); | ||||
| 
 | ||||
|                 // ditch opening brace | ||||
|                 json.Read(); | ||||
| 
 | ||||
|                 // { | ||||
|                 while (true) { | ||||
|                     switch (NextToken) { | ||||
|                     case TOKEN.NONE: | ||||
|                         return null; | ||||
|                     case TOKEN.COMMA: | ||||
|                         continue; | ||||
|                     case TOKEN.CURLY_CLOSE: | ||||
|                         return table; | ||||
|                     default: | ||||
|                         // name | ||||
|                         string name = ParseString(); | ||||
|                         if (name == null) { | ||||
|                             return null; | ||||
|                         } | ||||
| 
 | ||||
|                         // : | ||||
|                         if (NextToken != TOKEN.COLON) { | ||||
|                             return null; | ||||
|                         } | ||||
|                         // ditch the colon | ||||
|                         json.Read(); | ||||
| 
 | ||||
|                         // value | ||||
|                         table[name] = ParseValue(); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             List<object> ParseArray() { | ||||
|                 List<object> array = new List<object>(); | ||||
| 
 | ||||
|                 // ditch opening bracket | ||||
|                 json.Read(); | ||||
| 
 | ||||
|                 // [ | ||||
|                 var parsing = true; | ||||
|                 while (parsing) { | ||||
|                     TOKEN nextToken = NextToken; | ||||
| 
 | ||||
|                     switch (nextToken) { | ||||
|                     case TOKEN.NONE: | ||||
|                         return null; | ||||
|                     case TOKEN.COMMA: | ||||
|                         continue; | ||||
|                     case TOKEN.SQUARED_CLOSE: | ||||
|                         parsing = false; | ||||
|                         break; | ||||
|                     default: | ||||
|                         object value = ParseByToken(nextToken); | ||||
| 
 | ||||
|                         array.Add(value); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 return array; | ||||
|             } | ||||
| 
 | ||||
|             object ParseValue() { | ||||
|                 TOKEN nextToken = NextToken; | ||||
|                 return ParseByToken(nextToken); | ||||
|             } | ||||
| 
 | ||||
|             object ParseByToken(TOKEN token) { | ||||
|                 switch (token) { | ||||
|                 case TOKEN.STRING: | ||||
|                     return ParseString(); | ||||
|                 case TOKEN.NUMBER: | ||||
|                     return ParseNumber(); | ||||
|                 case TOKEN.CURLY_OPEN: | ||||
|                     return ParseObject(); | ||||
|                 case TOKEN.SQUARED_OPEN: | ||||
|                     return ParseArray(); | ||||
|                 case TOKEN.TRUE: | ||||
|                     return true; | ||||
|                 case TOKEN.FALSE: | ||||
|                     return false; | ||||
|                 case TOKEN.NULL: | ||||
|                     return null; | ||||
|                 default: | ||||
|                     return null; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             string ParseString() { | ||||
|                 StringBuilder s = new StringBuilder(); | ||||
|                 char c; | ||||
| 
 | ||||
|                 // ditch opening quote | ||||
|                 json.Read(); | ||||
| 
 | ||||
|                 bool parsing = true; | ||||
|                 while (parsing) { | ||||
| 
 | ||||
|                     if (json.Peek() == -1) { | ||||
|                         parsing = false; | ||||
|                         break; | ||||
|                     } | ||||
| 
 | ||||
|                     c = NextChar; | ||||
|                     switch (c) { | ||||
|                     case '"': | ||||
|                         parsing = false; | ||||
|                         break; | ||||
|                     case '\\': | ||||
|                         if (json.Peek() == -1) { | ||||
|                             parsing = false; | ||||
|                             break; | ||||
|                         } | ||||
| 
 | ||||
|                         c = NextChar; | ||||
|                         switch (c) { | ||||
|                         case '"': | ||||
|                         case '\\': | ||||
|                         case '/': | ||||
|                             s.Append(c); | ||||
|                             break; | ||||
|                         case 'b': | ||||
|                             s.Append('\b'); | ||||
|                             break; | ||||
|                         case 'f': | ||||
|                             s.Append('\f'); | ||||
|                             break; | ||||
|                         case 'n': | ||||
|                             s.Append('\n'); | ||||
|                             break; | ||||
|                         case 'r': | ||||
|                             s.Append('\r'); | ||||
|                             break; | ||||
|                         case 't': | ||||
|                             s.Append('\t'); | ||||
|                             break; | ||||
|                         case 'u': | ||||
|                             var hex = new char[4]; | ||||
| 
 | ||||
|                             for (int i=0; i< 4; i++) { | ||||
|                                 hex[i] = NextChar; | ||||
|                             } | ||||
| 
 | ||||
|                             s.Append((char) Convert.ToInt32(new string(hex), 16)); | ||||
|                             break; | ||||
|                         } | ||||
|                         break; | ||||
|                     default: | ||||
|                         s.Append(c); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 return s.ToString(); | ||||
|             } | ||||
| 
 | ||||
|             object ParseNumber() { | ||||
|                 string number = NextWord; | ||||
| 
 | ||||
|                 if (number.IndexOf('.') == -1) { | ||||
|                     long parsedInt; | ||||
|                     Int64.TryParse(number, out parsedInt); | ||||
|                     return parsedInt; | ||||
|                 } | ||||
| 
 | ||||
|                 double parsedDouble; | ||||
|                 Double.TryParse(number, out parsedDouble); | ||||
|                 return parsedDouble; | ||||
|             } | ||||
| 
 | ||||
|             void EatWhitespace() { | ||||
|                 while (Char.IsWhiteSpace(PeekChar)) { | ||||
|                     json.Read(); | ||||
| 
 | ||||
|                     if (json.Peek() == -1) { | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             char PeekChar => Convert.ToChar(json.Peek()); | ||||
| 
 | ||||
|             char NextChar => Convert.ToChar(json.Read()); | ||||
| 
 | ||||
|             string NextWord { | ||||
|                 get { | ||||
|                     StringBuilder word = new StringBuilder(); | ||||
| 
 | ||||
|                     while (!IsWordBreak(PeekChar)) { | ||||
|                         word.Append(NextChar); | ||||
| 
 | ||||
|                         if (json.Peek() == -1) { | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     return word.ToString(); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             TOKEN NextToken { | ||||
|                 get { | ||||
|                     EatWhitespace(); | ||||
| 
 | ||||
|                     if (json.Peek() == -1) { | ||||
|                         return TOKEN.NONE; | ||||
|                     } | ||||
| 
 | ||||
|                     switch (PeekChar) { | ||||
|                     case '{': | ||||
|                         return TOKEN.CURLY_OPEN; | ||||
|                     case '}': | ||||
|                         json.Read(); | ||||
|                         return TOKEN.CURLY_CLOSE; | ||||
|                     case '[': | ||||
|                         return TOKEN.SQUARED_OPEN; | ||||
|                     case ']': | ||||
|                         json.Read(); | ||||
|                         return TOKEN.SQUARED_CLOSE; | ||||
|                     case ',': | ||||
|                         json.Read(); | ||||
|                         return TOKEN.COMMA; | ||||
|                     case '"': | ||||
|                         return TOKEN.STRING; | ||||
|                     case ':': | ||||
|                         return TOKEN.COLON; | ||||
|                     case '0': | ||||
|                     case '1': | ||||
|                     case '2': | ||||
|                     case '3': | ||||
|                     case '4': | ||||
|                     case '5': | ||||
|                     case '6': | ||||
|                     case '7': | ||||
|                     case '8': | ||||
|                     case '9': | ||||
|                     case '-': | ||||
|                         return TOKEN.NUMBER; | ||||
|                     } | ||||
| 
 | ||||
|                     switch (NextWord) { | ||||
|                     case "false": | ||||
|                         return TOKEN.FALSE; | ||||
|                     case "true": | ||||
|                         return TOKEN.TRUE; | ||||
|                     case "null": | ||||
|                         return TOKEN.NULL; | ||||
|                     } | ||||
| 
 | ||||
|                     return TOKEN.NONE; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string | ||||
|         /// </summary> | ||||
|         /// <param name="json">A Dictionary<string, object> / List<object></param> | ||||
|         /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns> | ||||
|         public static string Serialize(object obj) { | ||||
|             return Serializer.Serialize(obj); | ||||
|         } | ||||
| 
 | ||||
|         sealed class Serializer { | ||||
|             StringBuilder builder; | ||||
| 
 | ||||
|             Serializer() { | ||||
|                 builder = new StringBuilder(); | ||||
|             } | ||||
| 
 | ||||
|             public static string Serialize(object obj) { | ||||
|                 var instance = new Serializer(); | ||||
| 
 | ||||
|                 instance.SerializeValue(obj); | ||||
| 
 | ||||
|                 return instance.builder.ToString(); | ||||
|             } | ||||
| 
 | ||||
|             void SerializeValue(object value) { | ||||
|                 IList asList; | ||||
|                 IDictionary asDict; | ||||
|                 string asStr; | ||||
| 
 | ||||
|                 if (value == null) { | ||||
|                     builder.Append("null"); | ||||
|                 } else if ((asStr = value as string) != null) { | ||||
|                     SerializeString(asStr); | ||||
|                 } else if (value is bool) { | ||||
|                     builder.Append((bool) value ? "true" : "false"); | ||||
|                 } else if ((asList = value as IList) != null) { | ||||
|                     SerializeArray(asList); | ||||
|                 } else if ((asDict = value as IDictionary) != null) { | ||||
|                     SerializeObject(asDict); | ||||
|                 } else if (value is char) { | ||||
|                     SerializeString(new string((char) value, 1)); | ||||
|                 } else { | ||||
|                     SerializeOther(value); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             void SerializeObject(IDictionary obj) { | ||||
|                 bool first = true; | ||||
| 
 | ||||
|                 builder.Append('{'); | ||||
| 
 | ||||
|                 foreach (object e in obj.Keys) { | ||||
|                     if (!first) { | ||||
|                         builder.Append(','); | ||||
|                     } | ||||
| 
 | ||||
|                     SerializeString(e.ToString()); | ||||
|                     builder.Append(':'); | ||||
| 
 | ||||
|                     SerializeValue(obj[e]); | ||||
| 
 | ||||
|                     first = false; | ||||
|                 } | ||||
| 
 | ||||
|                 builder.Append('}'); | ||||
|             } | ||||
| 
 | ||||
|             void SerializeArray(IList anArray) { | ||||
|                 builder.Append('['); | ||||
| 
 | ||||
|                 bool first = true; | ||||
| 
 | ||||
|                 foreach (object obj in anArray) { | ||||
|                     if (!first) { | ||||
|                         builder.Append(','); | ||||
|                     } | ||||
| 
 | ||||
|                     SerializeValue(obj); | ||||
| 
 | ||||
|                     first = false; | ||||
|                 } | ||||
| 
 | ||||
|                 builder.Append(']'); | ||||
|             } | ||||
| 
 | ||||
|             void SerializeString(string str) { | ||||
|                 builder.Append('\"'); | ||||
| 
 | ||||
|                 char[] charArray = str.ToCharArray(); | ||||
|                 foreach (var c in charArray) { | ||||
|                     switch (c) { | ||||
|                     case '"': | ||||
|                         builder.Append("\\\""); | ||||
|                         break; | ||||
|                     case '\\': | ||||
|                         builder.Append("\\\\"); | ||||
|                         break; | ||||
|                     case '\b': | ||||
|                         builder.Append("\\b"); | ||||
|                         break; | ||||
|                     case '\f': | ||||
|                         builder.Append("\\f"); | ||||
|                         break; | ||||
|                     case '\n': | ||||
|                         builder.Append("\\n"); | ||||
|                         break; | ||||
|                     case '\r': | ||||
|                         builder.Append("\\r"); | ||||
|                         break; | ||||
|                     case '\t': | ||||
|                         builder.Append("\\t"); | ||||
|                         break; | ||||
|                     default: | ||||
|                         int codepoint = Convert.ToInt32(c); | ||||
|                         if ((codepoint >= 32) && (codepoint <= 126)) { | ||||
|                             builder.Append(c); | ||||
|                         } else { | ||||
|                             builder.Append("\\u"); | ||||
|                             builder.Append(codepoint.ToString("x4")); | ||||
|                         } | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 builder.Append('\"'); | ||||
|             } | ||||
| 
 | ||||
|             void SerializeOther(object value) { | ||||
|                 // NOTE: decimals lose precision during serialization. | ||||
|                 // They always have, I'm just letting you know. | ||||
|                 // Previously floats and doubles lost precision too. | ||||
|                 if (value is float) { | ||||
|                     builder.Append(((float) value).ToString("R")); | ||||
|                 } else if (value is int | ||||
|                     || value is uint | ||||
|                     || value is long | ||||
|                     || value is sbyte | ||||
|                     || value is byte | ||||
|                     || value is short | ||||
|                     || value is ushort | ||||
|                     || value is ulong) { | ||||
|                     builder.Append(value); | ||||
|                 } else if (value is double | ||||
|                     || value is decimal) { | ||||
|                     builder.Append(Convert.ToDouble(value).ToString("R")); | ||||
|                 } else { | ||||
|                     SerializeString(value.ToString()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: f11796a30129b4c71aa6a52d59c6be19 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -15,7 +15,6 @@ | |||
| //  arising from, out of or in connection with the software or the use of other dealing in the software. | ||||
| // | ||||
| using UnityEngine; | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using System; | ||||
| 
 | ||||
|  | @ -49,13 +48,15 @@ public class UniWebView: MonoBehaviour { | |||
|     /// Raised when the web view finished to load a url successfully. | ||||
|     ///  | ||||
|     /// This method will be invoked when a valid response received from the url, regardless of the response status. | ||||
|     /// If a url loading fails before reaching to the server and getting a response, `OnPageErrorReceived` will be  | ||||
|     /// If a url loading fails before reaching to the server and getting a response, `OnLoadingErrorReceived` will be  | ||||
|     /// raised instead. | ||||
|     /// </summary> | ||||
|     public event PageFinishedDelegate OnPageFinished; | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Delegate for page error received event. | ||||
|     /// | ||||
|     /// Deprecated. Use `LoadingErrorReceivedDelegate` instead. | ||||
|     /// </summary> | ||||
|     /// <param name="webView">The web view component which raises this event.</param> | ||||
|     /// <param name="errorCode"> | ||||
|  | @ -63,12 +64,41 @@ public class UniWebView: MonoBehaviour { | |||
|     /// It can be different from systems and platforms. | ||||
|     /// </param> | ||||
|     /// <param name="errorMessage">The error message which indicates the error.</param> | ||||
|     [Obsolete("PageErrorReceivedDelegate is deprecated. Use `LoadingErrorReceivedDelegate` instead.", false)] | ||||
|     public delegate void PageErrorReceivedDelegate(UniWebView webView, int errorCode, string errorMessage); | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Raised when an error encountered during the loading process.  | ||||
|     /// Such as the "host not found" error or "no Internet connection" error will raise this event. | ||||
|     /// | ||||
|     /// Deprecated. Use `OnLoadingErrorReceived` instead. If both `OnPageErrorReceived` and `OnLoadingErrorReceived` are | ||||
|     /// listened, only the new `OnLoadingErrorReceived` will be raised, `OnPageErrorReceived` will not be called. | ||||
|     /// </summary> | ||||
|     [Obsolete("OnPageErrorReceived is deprecated. Use `OnLoadingErrorReceived` instead.", false)] | ||||
|     public event PageErrorReceivedDelegate OnPageErrorReceived; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Delegate for page loading error received event. | ||||
|     /// </summary> | ||||
|     /// <param name="webView">The web view component which raises this event.</param> | ||||
|     /// <param name="errorCode"> | ||||
|     /// The error code which indicates the error type.  | ||||
|     /// It can be different from systems and platforms. | ||||
|     /// </param> | ||||
|     /// <param name="errorMessage">The error message which indicates the error.</param> | ||||
|     /// <param name="payload">The payload received from native side, which contains the error information, such as the failing URL, in its `Extra`.</param> | ||||
|     public delegate void LoadingErrorReceivedDelegate( | ||||
|         UniWebView webView,  | ||||
|         int errorCode,  | ||||
|         string errorMessage,  | ||||
|         UniWebViewNativeResultPayload payload); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Raised when an error encountered during the loading process.  | ||||
|     /// Such as the "host not found" error or "no Internet connection" error will raise this event. | ||||
|     ///  | ||||
|     /// </summary> | ||||
|     public event LoadingErrorReceivedDelegate OnLoadingErrorReceived; | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Delegate for page progress changed event. | ||||
|  | @ -133,9 +163,14 @@ public class UniWebView: MonoBehaviour { | |||
|     /// <param name="webView">The web view component which raises this event.</param> | ||||
|     public delegate void OnWebContentProcessTerminatedDelegate(UniWebView webView); | ||||
|     /// <summary> | ||||
|     /// Raised when on iOS, when system calls `webViewWebContentProcessDidTerminate` method.  | ||||
|     /// It is usually due to a low memory when loading the web content and leave you a blank white screen.  | ||||
|     /// You need to free as much as memory you could and then do a page reload. | ||||
|     /// On iOS, raise when the system calls `webViewWebContentProcessDidTerminate` method. On Android, raise when the | ||||
|     /// system calls `onRenderProcessGone` method. | ||||
|     ///  | ||||
|     /// It is usually due to a low memory or the render process crashes when loading the web content. When this happens, | ||||
|     /// the web view will leave you a blank white screen. | ||||
|     ///  | ||||
|     /// Usually you should close the web view and clean all the resource since there is no good way to restore. In some | ||||
|     /// cases, you can also try to free as much as memory and do a page `Reload`. | ||||
|     /// </summary> | ||||
|     public event OnWebContentProcessTerminatedDelegate OnWebContentProcessTerminated; | ||||
| 
 | ||||
|  | @ -214,6 +249,8 @@ public class UniWebView: MonoBehaviour { | |||
|     /// </summary> | ||||
|     public event MultipleWindowClosedDelegate OnMultipleWindowClosed; | ||||
| 
 | ||||
|     private static readonly Rect snapshotFullViewRect = new Rect(-1, -1, -1, -1); | ||||
|      | ||||
|     private string id = Guid.NewGuid().ToString(); | ||||
|     private UniWebViewNativeListener listener; | ||||
|      | ||||
|  | @ -221,8 +258,10 @@ public class UniWebView: MonoBehaviour { | |||
|     /// Represents the embedded toolbar in the current web view. | ||||
|     /// </summary> | ||||
|     public UniWebViewEmbeddedToolbar EmbeddedToolbar { get; private set; } | ||||
| 
 | ||||
|     private bool isPortrait; | ||||
|      | ||||
|     private ScreenOrientation currentOrientation; | ||||
|      | ||||
|      | ||||
|     [SerializeField] | ||||
| 
 | ||||
|     #pragma warning disable 0649  | ||||
|  | @ -260,7 +299,7 @@ public class UniWebView: MonoBehaviour { | |||
|     /// The first two values of `Rect` is `x` and `y` position and the followed two `width` and `height`. | ||||
|     /// </summary> | ||||
|     public Rect Frame { | ||||
|         get { return frame; } | ||||
|         get => frame; | ||||
|         set { | ||||
|             frame = value; | ||||
|             UpdateFrame(); | ||||
|  | @ -278,9 +317,7 @@ public class UniWebView: MonoBehaviour { | |||
|     ///  | ||||
|     /// </summary> | ||||
|     public RectTransform ReferenceRectTransform { | ||||
|         get { | ||||
|             return referenceRectTransform; | ||||
|         } | ||||
|         get => referenceRectTransform; | ||||
|         set { | ||||
|             referenceRectTransform = value; | ||||
|             UpdateFrame(); | ||||
|  | @ -294,9 +331,7 @@ public class UniWebView: MonoBehaviour { | |||
|     /// <summary> | ||||
|     /// The url of current loaded web page. | ||||
|     /// </summary> | ||||
|     public string Url { | ||||
|         get { return UniWebViewInterface.GetUrl(listener.Name); }  | ||||
|     } | ||||
|     public string Url => UniWebViewInterface.GetUrl(listener.Name); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Updates and sets current frame of web view to match the setting. | ||||
|  | @ -306,58 +341,63 @@ public class UniWebView: MonoBehaviour { | |||
|     /// </summary> | ||||
|     public void UpdateFrame() { | ||||
|         Rect rect = NextFrameRect(); | ||||
|         // Sync web view frame property. | ||||
|         frame = rect; | ||||
|         UniWebViewInterface.SetFrame(listener.Name, (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); | ||||
|     } | ||||
| 
 | ||||
|     Rect NextFrameRect() { | ||||
|     Rect NextFrameRect() | ||||
|     { | ||||
|         if (referenceRectTransform == null) { | ||||
|             UniWebViewLogger.Instance.Info("Using Frame setting to determine web view frame."); | ||||
|             return frame; | ||||
|         } else { | ||||
|             UniWebViewLogger.Instance.Info("Using reference RectTransform to determine web view frame."); | ||||
|             var worldCorners = new Vector3[4]; | ||||
|              | ||||
|             referenceRectTransform.GetWorldCorners(worldCorners); | ||||
|              | ||||
|             var bottomLeft = worldCorners[0]; | ||||
|             var topLeft = worldCorners[1]; | ||||
|             var topRight = worldCorners[2]; | ||||
|             var bottomRight = worldCorners[3]; | ||||
|         } | ||||
| 
 | ||||
|             var canvas = referenceRectTransform.GetComponentInParent<Canvas>(); | ||||
|             if (canvas == null) { | ||||
|                 return frame; | ||||
|             } | ||||
|         UniWebViewLogger.Instance.Info("Using reference RectTransform to determine web view frame."); | ||||
|         var worldCorners = new Vector3[4]; | ||||
|              | ||||
|         referenceRectTransform.GetWorldCorners(worldCorners); | ||||
|              | ||||
|         // var bottomLeft = worldCorners[0]; | ||||
|         var topLeft = worldCorners[1]; | ||||
|         // var topRight = worldCorners[2]; | ||||
|         var bottomRight = worldCorners[3]; | ||||
| 
 | ||||
|             switch (canvas.renderMode) { | ||||
|                 case RenderMode.ScreenSpaceOverlay: | ||||
|                     break; | ||||
|                 case RenderMode.ScreenSpaceCamera: | ||||
|                 case RenderMode.WorldSpace: | ||||
|                     var camera = canvas.worldCamera; | ||||
|                     if (camera == null) { | ||||
|                         UniWebViewLogger.Instance.Critical(@"You need a render camera 
 | ||||
|         var canvas = referenceRectTransform.GetComponentInParent<Canvas>(); | ||||
|         if (canvas == null) { | ||||
|             return frame; | ||||
|         } | ||||
| 
 | ||||
|         switch (canvas.renderMode) { | ||||
|             case RenderMode.ScreenSpaceOverlay: | ||||
|                 break; | ||||
|             case RenderMode.ScreenSpaceCamera: | ||||
|             case RenderMode.WorldSpace: | ||||
|                 var camera = canvas.worldCamera; | ||||
|                 if (camera == null) { | ||||
|                     UniWebViewLogger.Instance.Critical(@"You need a render camera 
 | ||||
|                         or event camera to use RectTransform to determine correct  | ||||
|                         frame for UniWebView.");
 | ||||
|                         UniWebViewLogger.Instance.Info("No camera found. Fall back to ScreenSpaceOverlay mode."); | ||||
|                     } else { | ||||
|                         bottomLeft = camera.WorldToScreenPoint(bottomLeft); | ||||
|                         topLeft = camera.WorldToScreenPoint(topLeft); | ||||
|                         topRight = camera.WorldToScreenPoint(topRight); | ||||
|                         bottomRight = camera.WorldToScreenPoint(bottomRight); | ||||
|                     } | ||||
|                     break; | ||||
|             } | ||||
| 
 | ||||
|             float widthFactor = (float)UniWebViewInterface.NativeScreenWidth() / (float)Screen.width; | ||||
|             float heightFactor = (float)UniWebViewInterface.NativeScreenHeight() / (float)Screen.height; | ||||
| 
 | ||||
|             float x = topLeft.x * widthFactor; | ||||
|             float y = (Screen.height - topLeft.y) * heightFactor; | ||||
|             float width = (bottomRight.x - topLeft.x) * widthFactor; | ||||
|             float height = (topLeft.y - bottomRight.y) * heightFactor; | ||||
|             return new Rect(x, y, width, height); | ||||
|                     UniWebViewLogger.Instance.Info("No camera found. Fall back to ScreenSpaceOverlay mode."); | ||||
|                 } else { | ||||
|                     // bottomLeft = camera.WorldToScreenPoint(bottomLeft); | ||||
|                     topLeft = camera.WorldToScreenPoint(topLeft); | ||||
|                     // topRight = camera.WorldToScreenPoint(topRight); | ||||
|                     bottomRight = camera.WorldToScreenPoint(bottomRight); | ||||
|                 } | ||||
|                 break; | ||||
|             default: | ||||
|                 throw new ArgumentOutOfRangeException(); | ||||
|         } | ||||
| 
 | ||||
|         var widthFactor = UniWebViewInterface.NativeScreenWidth() / Screen.width; | ||||
|         var heightFactor = UniWebViewInterface.NativeScreenHeight() / Screen.height; | ||||
| 
 | ||||
|         var x = topLeft.x * widthFactor; | ||||
|         var y = (Screen.height - topLeft.y) * heightFactor; | ||||
|         var width = (bottomRight.x - topLeft.x) * widthFactor; | ||||
|         var height = (topLeft.y - bottomRight.y) * heightFactor; | ||||
|         return new Rect(x, y, width, height); | ||||
|     } | ||||
| 
 | ||||
|     void Awake() { | ||||
|  | @ -369,15 +409,10 @@ public class UniWebView: MonoBehaviour { | |||
| 
 | ||||
|         EmbeddedToolbar = new UniWebViewEmbeddedToolbar(listener); | ||||
| 
 | ||||
|         Rect rect; | ||||
|         if (fullScreen) { | ||||
|             rect = new Rect(0, 0, Screen.width, Screen.height); | ||||
|         } else { | ||||
|             rect = NextFrameRect(); | ||||
|         } | ||||
|         var rect = fullScreen ? new Rect(0, 0, Screen.width, Screen.height) : NextFrameRect(); | ||||
| 
 | ||||
|         UniWebViewInterface.Init(listener.Name, (int)rect.x, (int)rect. y, (int)rect.width, (int)rect.height); | ||||
|         isPortrait = Screen.height >= Screen.width; | ||||
|         currentOrientation = Screen.orientation; | ||||
|     } | ||||
| 
 | ||||
|     void Start() { | ||||
|  | @ -400,11 +435,11 @@ public class UniWebView: MonoBehaviour { | |||
|     } | ||||
| 
 | ||||
|     void Update() { | ||||
|         var newIsPortrait = Screen.height >= Screen.width; | ||||
|         if (isPortrait != newIsPortrait) { | ||||
|             isPortrait = newIsPortrait; | ||||
|         var newOrientation = Screen.orientation; | ||||
|         if (currentOrientation != newOrientation) { | ||||
|             currentOrientation = newOrientation; | ||||
|             if (OnOrientationChanged != null) { | ||||
|                 OnOrientationChanged(this, Screen.orientation); | ||||
|                 OnOrientationChanged(this, newOrientation); | ||||
|             } | ||||
|             if (referenceRectTransform != null) { | ||||
|                 UpdateFrame(); | ||||
|  | @ -476,6 +511,24 @@ public class UniWebView: MonoBehaviour { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Sets whether this web view instance should try to restore its view hierarchy when resumed. | ||||
|     /// | ||||
|     /// In some versions of Unity when running on Android, the player view is brought to front when switching back | ||||
|     /// from a pause state, which causes the web view is invisible when the app is resumed. It requires an additional | ||||
|     /// step to bring the web view to front to make the web view visible. Set this to true to apply this workaround. | ||||
|     /// | ||||
|     /// Issue caused by: | ||||
|     /// https://issuetracker.unity3d.com/issues/android-a-black-screen-appears-for-a-few-seconds-when-returning-to-the-game-from-the-lock-screen-after-idle-time | ||||
|     /// | ||||
|     /// This issue is known in these released versions: | ||||
|     /// - Unity 2021.3.31, 2021.3.32, 2021.3.31, 2021.3.34 | ||||
|     /// - Unity 2022.3.10, 2022.3.11, 2022.3.12, 2022.3.13, 2022.3.14, 2022.3.15 | ||||
|     /// | ||||
|     /// If you are using UniWebView in these versions, you may want to set this value to `true`. | ||||
|     /// </summary> | ||||
|     public bool RestoreViewHierarchyOnResume { get; set; } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Loads a url in current web view. | ||||
|     /// </summary> | ||||
|  | @ -505,14 +558,6 @@ public class UniWebView: MonoBehaviour { | |||
|         UniWebViewInterface.LoadHTMLString(listener.Name, htmlString, baseUrl, skipEncoding); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Stop current OMID ad session | ||||
|     /// </summary> | ||||
|     public void StopOMIDAdSession() | ||||
|     { | ||||
|         UniWebViewInterface.StopOMIDAdSession(listener.Name); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Reloads the current page. | ||||
|     /// </summary> | ||||
|  | @ -530,20 +575,12 @@ public class UniWebView: MonoBehaviour { | |||
|     /// <summary> | ||||
|     /// Gets whether there is a back page in the back-forward list that can be navigated to. | ||||
|     /// </summary> | ||||
|     public bool CanGoBack { | ||||
|         get { | ||||
|             return UniWebViewInterface.CanGoBack(listener.Name); | ||||
|         } | ||||
|     } | ||||
|     public bool CanGoBack => UniWebViewInterface.CanGoBack(listener.Name); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Gets whether there is a forward page in the back-forward list that can be navigated to. | ||||
|     /// </summary> | ||||
|     public bool CanGoForward { | ||||
|         get { | ||||
|             return UniWebViewInterface.CanGoForward(listener.Name); | ||||
|         } | ||||
|     } | ||||
|     public bool CanGoForward => UniWebViewInterface.CanGoForward(listener.Name); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Navigates to the back item in the back-forward list. | ||||
|  | @ -858,7 +895,7 @@ public class UniWebView: MonoBehaviour { | |||
|     /// Sets whether loading a local file is allowed. | ||||
|     ///  | ||||
|     /// If set to `false`, any load from a file URL `file://` for `Load` method will be rejected and trigger an  | ||||
|     /// `OnPageErrorReceived` event. That means you cannot load a web page from any local file. If you are not going to  | ||||
|     /// `OnLoadingErrorReceived` event. That means you cannot load a web page from any local file. If you do not going to  | ||||
|     /// load any local files, setting it to `false` helps to reduce the interface of web view and improve the security. | ||||
|     ///  | ||||
|     /// By default, it is `true` and the local file URL loading is allowed. | ||||
|  | @ -917,12 +954,14 @@ public class UniWebView: MonoBehaviour { | |||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Sets whether the web view area should avoid soft keyboard. It `true`, when the keyboard shows up, the web views | ||||
|     /// Sets whether the web view area should avoid soft keyboard. If `true`, when the keyboard shows up, the web views | ||||
|     /// content view will resize itself to avoid keyboard overlap the web content. Otherwise, the web view will not resize | ||||
|     /// and just leave the content below under the soft keyboard. | ||||
|     ///  | ||||
|     /// This method is only for Android. On iOS, the keyboard avoidance is built into the system directly and there is  | ||||
|     /// no way to change its behavior. | ||||
|     ///  | ||||
|     /// Default is `true`. | ||||
|     /// </summary> | ||||
|     /// <param name="flag">Whether the keyboard should avoid web view content.</param> | ||||
|     public static void SetEnableKeyboardAvoidance(bool flag) { | ||||
|  | @ -939,6 +978,24 @@ public class UniWebView: MonoBehaviour { | |||
|         UniWebViewInterface.SetJavaScriptEnabled(enabled); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Sets whether the web view limits navigation to pages within the app’s domain. | ||||
|     /// | ||||
|     /// This only works on iOS 14.0+. For more information, refer to the Apple's documentation: | ||||
|     /// https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/3585117-limitsnavigationstoappbounddomai | ||||
|     /// and the App-Bound Domains page: https://webkit.org/blog/10882/app-bound-domains/ | ||||
|     /// | ||||
|     /// This requires additional setup in `WKAppBoundDomains` key in the Info.plist file. | ||||
|     /// | ||||
|     /// On Android, this method does nothing. | ||||
|     /// </summary> | ||||
|     /// <param name="enabled"></param> | ||||
|     public static void SetLimitsNavigationsToAppBoundDomains(bool enabled) { | ||||
|         #if (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS) && !UNITY_EDITOR_WIN && !UNITY_EDITOR_LINUX | ||||
|         UniWebViewInterface.SetLimitsNavigationsToAppBoundDomains(enabled); | ||||
|         #endif | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Sets whether JavaScript can open windows without user interaction. | ||||
|     ///  | ||||
|  | @ -949,6 +1006,30 @@ public class UniWebView: MonoBehaviour { | |||
|         UniWebViewInterface.SetAllowJavaScriptOpenWindow(flag); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Sets whether the web page console output should be forwarded to native console. | ||||
|     /// | ||||
|     /// By setting this to `true`, UniWebView will try to intercept the web page console output methods and forward | ||||
|     /// them to the native console, for example, Xcode console on iOS, Android logcat on Android and Unity Console when | ||||
|     /// using Unity Editor on macOS. It provides a way to debug the web page by using the native console without opening | ||||
|     /// the web inspector. The forwarded logs in native side contains a "<UniWebView-Web>" tag.  | ||||
|     /// | ||||
|     /// Default is `false`. You need to set it before you create a web view instance to apply this setting. Any existing | ||||
|     /// web views are not affected. | ||||
|     /// | ||||
|     /// Logs from the methods below will be forwarded: | ||||
|     ///  | ||||
|     /// - console.log | ||||
|     /// - console.warn | ||||
|     /// - console.error | ||||
|     /// - console.debug | ||||
|     ///  | ||||
|     /// </summary> | ||||
|     /// <param name="flag">Whether the web page console output should be forwarded to native output.</param> | ||||
|     public static void SetForwardWebConsoleToNativeOutput(bool flag) { | ||||
|         UniWebViewInterface.SetForwardWebConsoleToNativeOutput(flag); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Cleans web view cache. This removes cached local data of web view.  | ||||
|     ///  | ||||
|  | @ -958,6 +1039,16 @@ public class UniWebView: MonoBehaviour { | |||
|         UniWebViewInterface.CleanCache(listener.Name); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Sets the way of how the cache is used when loading a request. | ||||
|     /// | ||||
|     /// The default value is `UniWebViewCacheMode.Default`. | ||||
|     /// </summary> | ||||
|     /// <param name="cacheMode">The desired cache mode which the following request loading should be used.</param> | ||||
|     public void SetCacheMode(UniWebViewCacheMode cacheMode) { | ||||
|         UniWebViewInterface.SetCacheMode(listener.Name, (int)cacheMode); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Clears all cookies from web view. | ||||
|     ///  | ||||
|  | @ -1046,9 +1137,7 @@ public class UniWebView: MonoBehaviour { | |||
|     /// Gets or sets the background color of web view. The default value is `Color.white`. | ||||
|     /// </summary> | ||||
|     public Color BackgroundColor { | ||||
|         get { | ||||
|             return backgroundColor; | ||||
|         } | ||||
|         get => backgroundColor; | ||||
|         set { | ||||
|             backgroundColor = value; | ||||
|             UniWebViewInterface.SetBackgroundColor(listener.Name, value.r, value.g, value.b, value.a); | ||||
|  | @ -1063,12 +1152,8 @@ public class UniWebView: MonoBehaviour { | |||
|     /// Default is `1.0f`, which means totally opaque. Set it to `0.0f` will make the web view totally transparent. | ||||
|     /// </summary> | ||||
|     public float Alpha { | ||||
|         get { | ||||
|             return UniWebViewInterface.GetWebViewAlpha(listener.Name); | ||||
|         } | ||||
|         set { | ||||
|             UniWebViewInterface.SetWebViewAlpha(listener.Name, value); | ||||
|          } | ||||
|         get => UniWebViewInterface.GetWebViewAlpha(listener.Name); | ||||
|         set => UniWebViewInterface.SetWebViewAlpha(listener.Name, value); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|  | @ -1087,6 +1172,39 @@ public class UniWebView: MonoBehaviour { | |||
|         UniWebViewInterface.SetSpinnerText(listener.Name, text); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Sets whether the user can dismiss the loading indicator by tapping on it or the greyed-out background around. | ||||
|     /// | ||||
|     /// By default, when the loading spinner is shown, the user can dismiss it by a single tapping. Call this method | ||||
|     /// with `false` to disable this behavior. | ||||
|     /// </summary> | ||||
|     /// <param name="flag">Whether the user can dismiss the loading indicator.</param> | ||||
|     public void SetAllowUserDismissSpinner(bool flag) { | ||||
|         UniWebViewInterface.SetAllowUserDismissSpinnerByGesture(listener.Name, flag); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Shows the loading spinner. | ||||
|     /// | ||||
|     /// Calling this method will show the loading spinner, regardless if the `SetShowSpinnerWhileLoading` is set to | ||||
|     /// `true` or `false`. However, if `SetShowSpinnerWhileLoading` was called with `true`, UniWebView will still hide | ||||
|     /// the spinner when the loading finishes. | ||||
|     /// </summary> | ||||
|     public void ShowSpinner() { | ||||
|         UniWebViewInterface.ShowSpinner(listener.Name); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Hides the loading spinner. | ||||
|     /// | ||||
|     /// Calling this method will hide the loading spinner, regardless if the `SetShowSpinnerWhileLoading` is set to | ||||
|     /// `true` or `false`. However, if `SetShowSpinnerWhileLoading` was called with `false`, UniWebView will still show | ||||
|     /// the spinner when the loading starts. | ||||
|     /// </summary> | ||||
|     public void HideSpinner() { | ||||
|         UniWebViewInterface.HideSpinner(listener.Name); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Sets whether the horizontal scroll bar should show when the web content beyonds web view bounds. | ||||
|     ///  | ||||
|  | @ -1129,11 +1247,18 @@ public class UniWebView: MonoBehaviour { | |||
|     /// <summary> | ||||
|     /// Adds a trusted domain to white list and allow permission requests from the domain. | ||||
|     ///  | ||||
|     /// You only need this on Android devices with system before 6.0 when a site needs the location or camera  | ||||
|     /// permission. It will allow the permission gets approved so you could access the corresponding devices. | ||||
|     /// From Android 6.0, the permission requests method is changed and this is not needed anymore. | ||||
|     /// You need this on Android devices when a site needs the location or camera  permission. It will allow the | ||||
|     /// permission gets approved so you could access the corresponding devices. | ||||
|     /// | ||||
|     /// Deprecated. Use `RegisterOnRequestMediaCapturePermission` instead, which works for both Android and iOS and | ||||
|     /// provides a more flexible way to handle the permission requests. By default, if neither this method and | ||||
|     /// `RegisterOnRequestMediaCapturePermission` is called, the permission request will trigger a grant alert to ask | ||||
|     /// the user to decide whether to allow or deny the permission. | ||||
|     ///   | ||||
|     /// </summary> | ||||
|     /// <param name="domain">The domain to add to the white list.</param> | ||||
|     [Obsolete("Deprecated. Use `RegisterOnRequestMediaCapturePermission` instead. Check " +  | ||||
|               "https://docs.uniwebview.com/api/#registeronrequestmediacapturepermission", false)] | ||||
|     public void AddPermissionTrustDomain(string domain) { | ||||
|         #if UNITY_ANDROID && !UNITY_EDITOR | ||||
|         UniWebViewInterface.AddPermissionTrustDomain(listener.Name, domain); | ||||
|  | @ -1144,6 +1269,7 @@ public class UniWebView: MonoBehaviour { | |||
|     /// Removes a trusted domain from white list. | ||||
|     /// </summary> | ||||
|     /// <param name="domain">The domain to remove from white list.</param> | ||||
|     [Obsolete("Deprecated. Use `UnregisterOnRequestMediaCapturePermission` instead.", false)] | ||||
|     public void RemovePermissionTrustDomain(string domain) { | ||||
|         #if UNITY_ANDROID && !UNITY_EDITOR | ||||
|         UniWebViewInterface.RemovePermissionTrustDomain(listener.Name, domain); | ||||
|  | @ -1206,6 +1332,8 @@ public class UniWebView: MonoBehaviour { | |||
|     /// Default is `true`</param> | ||||
|     /// <param name="adjustInset">Whether the toolbar transition should also adjust web view position and size | ||||
|     ///  if overlapped. Default is `false`</param> | ||||
|     [Obsolete("`SetShowToolbar` is deprecated. Use `EmbeddedToolbar.Show()` or `EmbeddedToolbar.Hide()`" +  | ||||
|               "instead.", false)] | ||||
|     public void SetShowToolbar(bool show, bool animated = false, bool onTop = true, bool adjustInset = false) { | ||||
|         #if (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS) && !UNITY_EDITOR_WIN && !UNITY_EDITOR_LINUX | ||||
|         UniWebViewInterface.SetShowToolbar(listener.Name, show, animated, onTop, adjustInset); | ||||
|  | @ -1221,6 +1349,8 @@ public class UniWebView: MonoBehaviour { | |||
|     /// This method is only for iOS, since there is no toolbar on Android. | ||||
|     /// </summary> | ||||
|     /// <param name="text">The text needed to be set as done button title.</param> | ||||
|     [Obsolete("`SetToolbarDoneButtonText` is deprecated. Use `EmbeddedToolbar.SetDoneButtonText` " +  | ||||
|               "instead.", false)] | ||||
|     public void SetToolbarDoneButtonText(string text) { | ||||
|         #if UNITY_IOS && !UNITY_EDITOR | ||||
|         UniWebViewInterface.SetToolbarDoneButtonText(listener.Name, text); | ||||
|  | @ -1236,6 +1366,8 @@ public class UniWebView: MonoBehaviour { | |||
|     /// This method is only for iOS and macOS Editor, since there is no toolbar on Android. | ||||
|     /// </summary> | ||||
|     /// <param name="text">The text needed to be set as go back button.</param> | ||||
|     [Obsolete("`SetToolbarGoBackButtonText` is deprecated. Use `EmbeddedToolbar.SetGoBackButtonText` " +  | ||||
|               "instead.", false)] | ||||
|     public void SetToolbarGoBackButtonText(string text) { | ||||
|         #if (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS) && !UNITY_EDITOR_WIN && !UNITY_EDITOR_LINUX | ||||
|         UniWebViewInterface.SetToolbarGoBackButtonText(listener.Name, text); | ||||
|  | @ -1251,6 +1383,8 @@ public class UniWebView: MonoBehaviour { | |||
|     /// This method is only for iOS and macOS Editor, since there is no toolbar on Android. | ||||
|     /// </summary> | ||||
|     /// <param name="text">The text needed to be set as go forward button.</param> | ||||
|     [Obsolete("`SetToolbarGoForwardButtonText` is deprecated. Use `EmbeddedToolbar.SetGoForwardButtonText` " +  | ||||
|               "instead.", false)] | ||||
|     public void SetToolbarGoForwardButtonText(string text) { | ||||
|         #if (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS) && !UNITY_EDITOR_WIN && !UNITY_EDITOR_LINUX | ||||
|         UniWebViewInterface.SetToolbarGoForwardButtonText(listener.Name, text); | ||||
|  | @ -1266,6 +1400,8 @@ public class UniWebView: MonoBehaviour { | |||
|     /// This method is only for iOS, since there is no toolbar on Android. | ||||
|     /// </summary> | ||||
|     /// <param name="color">The color should be used for the background tint of the toolbar.</param> | ||||
|     [Obsolete("`SetToolbarTintColor` is deprecated. Use `EmbeddedToolbar.SetBackgroundColor` " +  | ||||
|               "instead.", false)] | ||||
|     public void SetToolbarTintColor(Color color) { | ||||
|         #if UNITY_IOS && !UNITY_EDITOR | ||||
|         UniWebViewInterface.SetToolbarTintColor(listener.Name, color.r, color.g, color.b); | ||||
|  | @ -1281,6 +1417,8 @@ public class UniWebView: MonoBehaviour { | |||
|     /// This method is only for iOS, since there is no toolbar on Android. | ||||
|     /// </summary> | ||||
|     /// <param name="color">The color should be used for the button text of the toolbar.</param> | ||||
|     [Obsolete("`SetToolbarTextColor` is deprecated. Use `EmbeddedToolbar.SetButtonTextColor` or " +  | ||||
|               "`EmbeddedToolbar.SetTitleTextColor` instead.", false)] | ||||
|     public void SetToolbarTextColor(Color color) { | ||||
|         #if UNITY_IOS && !UNITY_EDITOR | ||||
|         UniWebViewInterface.SetToolbarTextColor(listener.Name, color.r, color.g, color.b); | ||||
|  | @ -1297,6 +1435,8 @@ public class UniWebView: MonoBehaviour { | |||
|     /// This method is only for iOS, since there is no toolbar on Android. | ||||
|     /// </summary> | ||||
|     /// <param name="show">Whether the navigation buttons on the toolbar should show or hide.</param> | ||||
|     [Obsolete("`SetShowToolbarNavigationButtons` is deprecated. Use `EmbeddedToolbar.ShowNavigationButtons` or " +  | ||||
|               "`EmbeddedToolbar.HideNavigationButtons` instead.", false)] | ||||
|     public void SetShowToolbarNavigationButtons(bool show) { | ||||
|         #if (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS) && !UNITY_EDITOR_WIN && !UNITY_EDITOR_LINUX | ||||
|         UniWebViewInterface.SetShowToolbarNavigationButtons(listener.Name, show); | ||||
|  | @ -1500,19 +1640,21 @@ public class UniWebView: MonoBehaviour { | |||
|     /// <summary> | ||||
|     /// Sets whether the drag interaction should be enabled on iOS. | ||||
|     ///  | ||||
|     /// From iOS 11, the iPad web view supports the drag interaction when user long presses an image, link or text. | ||||
|     /// From iOS 11, the web view on iOS supports the drag interaction when user long presses an image, link or text. | ||||
|     /// Setting this to `false` would disable the drag feather on the web view. | ||||
|     /// | ||||
|     /// On Android, there is no direct native way to disable drag only. This method instead disables the long touch | ||||
|     /// event, which is used as a trigger for drag interaction. So, setting this to `false` would disable the long | ||||
|     /// touch gesture as a side effect.  | ||||
|     ///  | ||||
|     /// This method only works on iOS. It does nothing on Android or macOS editor. Default is `true`, which means | ||||
|     /// drag interaction on iPad is enabled. | ||||
|     /// It does nothing on macOS editor. Default is `true`, which means drag interaction is enabled if the device and | ||||
|     /// system version supports it. | ||||
|     /// </summary> | ||||
|     /// <param name="enabled"> | ||||
|     /// Whether the drag interaction should be enabled. | ||||
|     /// </param> | ||||
|     public void SetDragInteractionEnabled(bool enabled) { | ||||
|         #if (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS) && !UNITY_EDITOR_WIN && !UNITY_EDITOR_LINUX | ||||
|         UniWebViewInterface.SetDragInteractionEnabled(listener.Name, enabled); | ||||
|         #endif | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|  | @ -1627,6 +1769,23 @@ public class UniWebView: MonoBehaviour { | |||
|         #endif | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Sets whether allowing users to edit the file name before downloading. Default is `true`. | ||||
|     /// | ||||
|     /// If `true`, when a download task is triggered, a dialog will be shown to ask user to edit the file name and the | ||||
|     /// user has a chance to choose if the they actually want to download the file. If `false`, the file download will | ||||
|     /// start immediately without asking user to edit the file name. The file name is generated by guessing from the | ||||
|     /// content disposition header and the MIME type. If the guessing of the file name fails, a random string will be | ||||
|     /// used. | ||||
|     /// | ||||
|     /// </summary> | ||||
|     /// <param name="allowed"> | ||||
|     /// Whether the user can edit the file name and determine whether actually starting the downloading. | ||||
|     /// </param> | ||||
|     public void SetAllowUserEditFileNameBeforeDownloading(bool allowed) { | ||||
|         UniWebViewInterface.SetAllowUserEditFileNameBeforeDownloading(listener.Name, allowed); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Sets whether allowing users to choose the way to handle the downloaded file. Default is `true`. | ||||
|     ///  | ||||
|  | @ -1639,7 +1798,7 @@ public class UniWebView: MonoBehaviour { | |||
|     /// This method does not have any effect on Android. On Android, the file is downloaded to the Download folder. | ||||
|     ///  | ||||
|     /// </summary> | ||||
|     /// <param name="allowed"></param> | ||||
|     /// <param name="allowed">Whether the user can choose the way to handle the downloaded file.</param> | ||||
|     public void SetAllowUserChooseActionAfterDownloading(bool allowed) { | ||||
|         #if (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS) && !UNITY_EDITOR_WIN && !UNITY_EDITOR_LINUX | ||||
|         UniWebViewInterface.SetAllowUserChooseActionAfterDownloading(listener.Name, allowed); | ||||
|  | @ -1666,12 +1825,239 @@ public class UniWebView: MonoBehaviour { | |||
|         #endif | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Starts the process of continually rendering the snapshot. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// <para> | ||||
|     /// You take the responsibility of calling this method before you use either <see cref="GetRenderedData(Rect?)"/> or | ||||
|     /// <see cref="CreateRenderedTexture(Rect?)"/> to get the rendered data or texture. It prepares a render buffer for the image | ||||
|     /// data and performs the initial rendering for later use. | ||||
|     /// </para> | ||||
|     /// <para> | ||||
|     /// If this method is not called, the related data or texture methods will not work and will only return <c>null</c>. Once you | ||||
|     /// no longer need the web view to be rendered as a texture, you should call <see cref="StopSnapshotForRendering"/> to clean up | ||||
|     /// the associated resources. | ||||
|     /// </para> | ||||
|     /// </remarks> | ||||
|     /// <param name="rect">The optional rectangle to specify the area for rendering. If <c>null</c>, the entire view is rendered.</param> | ||||
|     /// <param name="onStarted"> | ||||
|     /// An optional callback to execute when rendering has started. The callback receives a <see cref="Texture2D"/> parameter | ||||
|     /// representing the rendered texture. | ||||
|     /// </param> | ||||
|     public void StartSnapshotForRendering(Rect? rect = null, Action<Texture2D> onStarted = null) { | ||||
|         string identifier = null; | ||||
|         if (onStarted != null) { | ||||
|             identifier = Guid.NewGuid().ToString(); | ||||
|             actions.Add(identifier, () => { | ||||
|                 var texture = CreateRenderedTexture(rect); | ||||
|                 onStarted(texture); | ||||
|             }); | ||||
|         } | ||||
|         UniWebViewInterface.StartSnapshotForRendering(listener.Name, identifier); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Stops the process of continually rendering the snapshot. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// <para> | ||||
|     /// You should call this method when you no longer need any further data or texture from the | ||||
|     /// <see cref="GetRenderedData(Rect?)"/> or <see cref="CreateRenderedTexture(Rect?)"/> methods. This helps in releasing | ||||
|     /// resources and terminating the rendering process. | ||||
|     /// </para> | ||||
|     /// </remarks> | ||||
|     public void StopSnapshotForRendering() { | ||||
|         UniWebViewInterface.StopSnapshotForRendering(listener.Name); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Gets the data of the rendered image for the current web view. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// <para> | ||||
|     /// This method provides you with the raw bytes of the rendered image data in PNG format. To successfully retrieve the | ||||
|     /// current rendered data, you should first call <see cref="StartSnapshotForRendering"/> to initiate the rendering process. | ||||
|     /// If <see cref="StartSnapshotForRendering"/> has not been called, this method will return <c>null</c>. | ||||
|     /// </para> | ||||
|     /// <para> | ||||
|     /// The rendering area specified by the <paramref name="rect"/> parameter is based on the local coordinates of the web view. | ||||
|     /// For example, <c>new Rect(webView.frame.width / 2, webView.frame.height / 2, 100, 100)</c> means setting the origin to the | ||||
|     /// center of the web view and taking a 100x100 square as the snapshot area. | ||||
|     /// </para> | ||||
|     /// <para> | ||||
|     /// Please note that this method supports only software-rendered content. Content rendered by hardware, such as videos | ||||
|     /// and WebGL content, will appear as a black rectangle in the rendered image. | ||||
|     /// </para> | ||||
|     /// </remarks> | ||||
|     /// <param name="rect"> | ||||
|     /// The desired rectangle within which the snapshot rendering should occur in the web view. If default value `null` is used, | ||||
|     /// the whole web view frame will be used as the snapshot area. | ||||
|     /// </param> | ||||
|     /// <returns> | ||||
|     /// An array of raw bytes representing the rendered image data in PNG format, or <c>null</c> if the rendering process fails | ||||
|     /// or if the data is not prepared. | ||||
|     /// </returns> | ||||
|     /// <seealso cref="StartSnapshotForRendering"/> | ||||
|     public byte[] GetRenderedData(Rect? rect = null) { | ||||
|         var r = rect ?? snapshotFullViewRect; | ||||
|         return UniWebViewInterface.GetRenderedData( | ||||
|             listener.Name, (int)r.x, (int)r.y, (int)r.width, (int)r.height | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Creates a rendered texture for the current web view. | ||||
|     /// </summary> | ||||
|     /// <remarks> | ||||
|     /// <para> | ||||
|     /// You should destroy the returned texture using the `Destroy` method when you no longer need it to free up resources. | ||||
|     /// </para> | ||||
|     /// <para> | ||||
|     /// This method provides you with a texture of the rendered image for the web view, which you can use in your 3D game world. | ||||
|     /// To obtain the current rendered data, you should call <see cref="StartSnapshotForRendering"/> before using this method. | ||||
|     /// If <see cref="StartSnapshotForRendering"/> has not been called, this method will return <c>null</c>. | ||||
|     /// </para> | ||||
|     /// <para> | ||||
|     /// Please note that this method supports only software-rendered content. Content rendered by hardware, such as videos | ||||
|     /// and WebGL content, will appear as a black rectangle in the rendered image. | ||||
|     /// </para> | ||||
|     /// <para> | ||||
|     /// This method returns a plain <see cref="Texture2D"/> object. The texture is not user interactive and can only be used for | ||||
|     /// display purposes. It is your responsibility to call the `Destroy` method on this texture when you no longer need it. | ||||
|     /// </para> | ||||
|     /// </remarks> | ||||
|     /// <param name="rect"> | ||||
|     /// The desired rectangle within which the snapshot rendering should occur in the web view. If default value `null` is used, | ||||
|     /// the whole web view frame will be used as the snapshot area. | ||||
|     /// </param> | ||||
|     /// <returns> | ||||
|     /// A rendered texture of the current web view, or <c>null</c> if the rendering process fails or if the data is not prepared. | ||||
|     /// </returns> | ||||
|     /// <seealso cref="StartSnapshotForRendering"/> | ||||
|     public Texture2D CreateRenderedTexture(Rect? rect = null) { | ||||
|         var bytes = GetRenderedData(rect); | ||||
|         if (bytes == null) { | ||||
|             return null; | ||||
|         } | ||||
|         Texture2D texture = new Texture2D(2, 2, TextureFormat.RGB24, false); | ||||
|         texture.LoadImage(bytes); | ||||
|         return texture; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Registers a method handler for deciding whether UniWebView should handle the request received by the web view. | ||||
|     /// | ||||
|     /// The handler is called before the web view actually starts to load the new request. You can check the request | ||||
|     /// properties, such as the URL, to decide whether UniWebView should continue to handle the request or not. If you | ||||
|     /// return `true` from the handler function, UniWebView will continue to load the request. Otherwise, UniWebView | ||||
|     /// will stop the loading. | ||||
|     /// </summary> | ||||
|     /// <param name="handler"> | ||||
|     /// A handler you can implement your own logic against the input request value. You need to return a boolean value | ||||
|     /// to indicate whether UniWebView should continue to load the request or not as soon as possible. | ||||
|     /// </param> | ||||
|     public void RegisterShouldHandleRequest(Func<UniWebViewChannelMethodHandleRequest, bool> handler) { | ||||
|         object Func(object obj) => handler((UniWebViewChannelMethodHandleRequest)obj); | ||||
|         UniWebViewChannelMethodManager.Instance.RegisterChannelMethod( | ||||
|             listener.Name,  | ||||
|             UniWebViewChannelMethod.ShouldUniWebViewHandleRequest, | ||||
|             Func | ||||
|         ); | ||||
|     } | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Unregisters the method handler for handling request received by the web view. | ||||
|     /// | ||||
|     /// This clears the handler registered by `RegisterHandlingRequest` method. | ||||
|     /// </summary> | ||||
|     public void UnregisterShouldHandleRequest() { | ||||
|         UniWebViewChannelMethodManager.Instance.UnregisterChannelMethod( | ||||
|             listener.Name,  | ||||
|             UniWebViewChannelMethod.ShouldUniWebViewHandleRequest | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Registers a method handler for deciding whether UniWebView should allow a media request from the web page or | ||||
|     /// not. | ||||
|     /// | ||||
|     /// The handler is called when the web view receives a request to capture media, such as camera or microphone. It | ||||
|     /// usually happens when the web view is trying to access the camera or microphone by using the "getUserMedia" APIs | ||||
|     /// in WebRTC. You can check the request properties in the input `UniWebViewChannelMethodMediaCapturePermission` | ||||
|     /// instance, which contains information like the media type, the request origin (protocol and host), then decide | ||||
|     /// whether this media request should be allowed or not. | ||||
|     /// | ||||
|     /// According to the `UniWebViewMediaCapturePermissionDecision` value you return from the handler function, | ||||
|     /// UniWebView behaves differently: | ||||
|     ///   | ||||
|     /// - `Grant`: UniWebView allows the access without asking the user. | ||||
|     /// - `Deny`: UniWebView forbids the access and the web page will receive an error. | ||||
|     /// - `Prompt`: UniWebView asks the user for permission. The web page will receive a prompt to ask the user if they | ||||
|     /// allow the access to the requested media resources (camera or/and microphone). | ||||
|     /// | ||||
|     /// If this method is never called or the handler is unregistered, UniWebView will prompt the user for the | ||||
|     /// permission. | ||||
|     /// | ||||
|     /// On iOS, this method is available from iOS 15.0 or later. On earlier version of iOS, the handler will be ignored | ||||
|     /// and the web view will always prompt the user for the permission. | ||||
|     ///  | ||||
|     /// </summary> | ||||
|     /// <param name="handler"> | ||||
|     /// A handler you can implement your own logic to decide whether UniWebView should allow, deny or prompt the media | ||||
|     /// resource access request. | ||||
|     /// | ||||
|     /// You need to return a `UniWebViewMediaCapturePermissionDecision` value to indicate the decision as soon as | ||||
|     /// possible. | ||||
|     /// </param> | ||||
|     public void RegisterOnRequestMediaCapturePermission( | ||||
|         Func< | ||||
|             UniWebViewChannelMethodMediaCapturePermission,  | ||||
|             UniWebViewMediaCapturePermissionDecision | ||||
|         > handler | ||||
|         ) | ||||
|     { | ||||
|         object Func(object obj) => handler((UniWebViewChannelMethodMediaCapturePermission)obj); | ||||
|         UniWebViewChannelMethodManager.Instance.RegisterChannelMethod( | ||||
|             listener.Name,  | ||||
|             UniWebViewChannelMethod.RequestMediaCapturePermission, | ||||
|             Func | ||||
|         ); | ||||
|     } | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Unregisters the method handler for handling media capture permission request. | ||||
|     /// | ||||
|     /// This clears the handler registered by `RegisterOnRequestMediaCapturePermission` method. | ||||
|     /// </summary> | ||||
|     public void UnregisterOnRequestMediaCapturePermission() { | ||||
|         UniWebViewChannelMethodManager.Instance.UnregisterChannelMethod( | ||||
|             listener.Name,  | ||||
|             UniWebViewChannelMethod.RequestMediaCapturePermission | ||||
|         ); | ||||
|     } | ||||
|      | ||||
|     void OnDestroy() { | ||||
|         UniWebViewNativeListener.RemoveListener(listener.Name); | ||||
|         UniWebViewChannelMethodManager.Instance.UnregisterChannel(listener.Name); | ||||
|         UniWebViewInterface.Destroy(listener.Name); | ||||
|         Destroy(listener.gameObject); | ||||
|     } | ||||
| 
 | ||||
| #if UNITY_ANDROID && !UNITY_EDITOR | ||||
|     // From Unity 2022.3.10, the player view is brought to front when switching back from a pause | ||||
|     // state. Requiring to bring the web view to front to make the web view visible. | ||||
|     // Issue caused by: | ||||
|     // https://issuetracker.unity3d.com/issues/android-a-black-screen-appears-for-a-few-seconds-when-returning-to-the-game-from-the-lock-screen-after-idle-time | ||||
|     //  | ||||
|     // Ref: UWV-1061 | ||||
|     void OnApplicationPause(bool pauseStatus) { | ||||
|         if (RestoreViewHierarchyOnResume && !pauseStatus) { | ||||
|             UniWebViewInterface.BringContentToFront(listener.Name); | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
|     /* ////////////////////////////////////////////////////// | ||||
|     // Internal Listener Interface | ||||
|     ////////////////////////////////////////////////////// */ | ||||
|  | @ -1735,9 +2121,14 @@ public class UniWebView: MonoBehaviour { | |||
|     } | ||||
| 
 | ||||
|     internal void InternalOnPageErrorReceived(UniWebViewNativeResultPayload payload) { | ||||
|         if (OnPageErrorReceived != null) { | ||||
|             int code = -1; | ||||
|             if (int.TryParse(payload.resultCode, out code)) { | ||||
|         if (OnLoadingErrorReceived != null) { | ||||
|             if (int.TryParse(payload.resultCode, out var code)) { | ||||
|                 OnLoadingErrorReceived(this, code, payload.data, payload); | ||||
|             } else { | ||||
|                 UniWebViewLogger.Instance.Critical("Invalid error code received: " + payload.resultCode); | ||||
|             } | ||||
|         } else if (OnPageErrorReceived != null) { | ||||
|             if (int.TryParse(payload.resultCode, out var code)) { | ||||
|                 OnPageErrorReceived(this, code, payload.data); | ||||
|             } else { | ||||
|                 UniWebViewLogger.Instance.Critical("Invalid error code received: " + payload.resultCode); | ||||
|  | @ -1806,6 +2197,14 @@ public class UniWebView: MonoBehaviour { | |||
|             OnCaptureSnapshotFinished(this, errorCode,  payload.data); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     internal void InternalOnSnapshotRenderingStarted(string identifier) { | ||||
|         Action action; | ||||
|         if (actions.TryGetValue(identifier, out action)) { | ||||
|             action(); | ||||
|             actions.Remove(identifier); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Sets whether the web view should behave in immersive mode, that is,  | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 598e18fb001004a81960f552978ecf4e | ||||
| timeCreated: 1491898971 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ public abstract class UniWebViewAuthenticationCommonFlow: MonoBehaviour { | |||
|     /// Whether to start authentication as soon as the script `Start`s. | ||||
|     /// </summary> | ||||
|     public bool authorizeOnStart; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Whether to use private mode to authenticate the user. If `true` and the device supports, the authentication | ||||
|     /// will begin under the incognito mode. | ||||
|  | @ -45,21 +46,29 @@ public abstract class UniWebViewAuthenticationCommonFlow: MonoBehaviour { | |||
|      | ||||
|     // Security. Store the state. | ||||
|     private string state; | ||||
| 
 | ||||
|     // Security. Store the code challenge verifier.  | ||||
|     private string codeVerify; | ||||
|      | ||||
|     protected string CodeVerify => codeVerify; | ||||
|      | ||||
|     protected string CodeVerify { get; private set; } | ||||
| 
 | ||||
|     public void Start() { | ||||
|         if (authorizeOnStart) { | ||||
|             StartAuthenticationFlow(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Subclass should override this method to start the authentication flow. Usually it starts | ||||
|     // a `UniWebViewAuthenticationFlow`. But you can also choose whatever you need to do. | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Subclass should override this method to start the authentication flow. Usually it starts | ||||
|     /// a `UniWebViewAuthenticationFlow`. But you can also choose whatever you need to do.  | ||||
|     /// </summary> | ||||
|     public abstract void StartAuthenticationFlow(); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Subclass should override this method to start the authentication flow. Usually it starts | ||||
|     /// a Unity Web Request against the authentication flow's token entry point to refresh the token. | ||||
|     /// </summary> | ||||
|     /// <param name="refreshToken">The refresh token.</param> | ||||
|     public abstract void StartRefreshTokenFlow(string refreshToken); | ||||
| 
 | ||||
|     // Child classes are expected to call this method to request a `state` (and store it for later check) if the  | ||||
|     // `state` verification is enabled. | ||||
|     protected string GenerateAndStoreState() { | ||||
|  | @ -70,8 +79,8 @@ public abstract class UniWebViewAuthenticationCommonFlow: MonoBehaviour { | |||
|     // Child classes are expected to call this method to request a `code_challenge`. Later when exchanging the access | ||||
|     // token, the `code_verifier` will be used to verify the `code_challenge`. Subclass can read it from `CodeVerify`. | ||||
|     protected string GenerateCodeChallengeAndStoreCodeVerify(UniWebViewAuthenticationPKCE method) { | ||||
|         codeVerify = UniWebViewAuthenticationUtils.GenerateCodeVerifier(); | ||||
|         return UniWebViewAuthenticationUtils.CalculateCodeChallenge(codeVerify, method); | ||||
|         CodeVerify = UniWebViewAuthenticationUtils.GenerateCodeVerifier(); | ||||
|         return UniWebViewAuthenticationUtils.CalculateCodeChallenge(CodeVerify, method); | ||||
|     } | ||||
|      | ||||
|     // Perform verifying for `state`. | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ using System; | |||
| using System.Collections; | ||||
| using UnityEngine.Networking; | ||||
| using System.Collections.Generic; | ||||
| using System.Collections.Specialized; | ||||
| using UnityEngine; | ||||
| using UnityEngine.Events; | ||||
| 
 | ||||
|  | @ -64,6 +65,19 @@ public interface IUniWebViewAuthenticationFlow<TTokenType> | |||
|     /// <returns>The dictionary indicates parameters that are used to perform the authentication request.</returns> | ||||
|     Dictionary<string, string> GetAuthenticationUriArguments(); | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Returns additional query arguments that are used to construct the query string of the authentication request. | ||||
|     ///  | ||||
|     /// If you want to add some extra query arguments to the authentication request, you can override this method and  | ||||
|     /// return a string that contains the additional query arguments. The returned string will be appended to the query  | ||||
|     /// string that constructed from `GetAuthenticationUriArguments`. | ||||
|     ///  | ||||
|     /// </summary> | ||||
|     /// <returns> | ||||
|     /// The additional query arguments that are used to construct the query string of the authentication request. | ||||
|     /// </returns> | ||||
|     string GetAdditionalAuthenticationUriQuery(); | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Returns a dictionary contains the parameters that are used to perform the access token exchange request. | ||||
|     /// The key value pairs in the dictionary are used to construct the HTTP form body of the access token exchange request. | ||||
|  | @ -77,7 +91,17 @@ public interface IUniWebViewAuthenticationFlow<TTokenType> | |||
|     /// The dictionary indicates parameters that are used to perform the access token exchange request. | ||||
|     /// </returns> | ||||
|     Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse); | ||||
|      | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Returns a dictionary contains the parameters that are used to perform the access token refresh request. | ||||
|     /// The key value pairs in the dictionary are used to construct the HTTP form body of the access token refresh request. | ||||
|     /// </summary> | ||||
|     /// <param name="refreshToken">The refresh token should be used to perform the refresh request.</param> | ||||
|     /// <returns> | ||||
|     /// The dictionary indicates parameters that are used to perform the access token refresh request. | ||||
|     /// </returns> | ||||
|     Dictionary<string, string> GetRefreshTokenRequestParameters(string refreshToken); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Returns the strong-typed token for the authentication process. | ||||
|     /// | ||||
|  | @ -97,11 +121,21 @@ public interface IUniWebViewAuthenticationFlow<TTokenType> | |||
|     /// Called when the authentication flow succeeds and a valid token is generated. | ||||
|     /// </summary> | ||||
|     UnityEvent<TTokenType> OnAuthenticationFinished { get; } | ||||
|      | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Called when any error (including user cancellation) happens during the authentication flow. | ||||
|     /// </summary> | ||||
|     UnityEvent<long, string> OnAuthenticationErrored { get; } | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Called when the access token refresh request finishes and a valid refreshed token is generated. | ||||
|     /// </summary> | ||||
|     UnityEvent<TTokenType> OnRefreshTokenFinished { get; } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Called when any error happens during the access token refresh flow. | ||||
|     /// </summary> | ||||
|     UnityEvent<long, string> OnRefreshTokenErrored { get; } | ||||
| } | ||||
| 
 | ||||
| /// <summary> | ||||
|  | @ -112,6 +146,8 @@ public interface IUniWebViewAuthenticationFlow<TTokenType> | |||
| public class UniWebViewAuthenticationFlow<TTokenType> { | ||||
|      | ||||
|     private IUniWebViewAuthenticationFlow<TTokenType> service; | ||||
|     private UniWebViewAuthenticationSession session; | ||||
|     private Uri callbackUri; | ||||
| 
 | ||||
|     public UniWebViewAuthenticationFlow( | ||||
|         IUniWebViewAuthenticationFlow<TTokenType> service | ||||
|  | @ -127,48 +163,96 @@ public class UniWebViewAuthenticationFlow<TTokenType> { | |||
|     { | ||||
|         var callbackUri = new Uri(service.GetCallbackUrl()); | ||||
|         var authUrl = GetAuthUrl(); | ||||
|         var session = UniWebViewAuthenticationSession.Create(authUrl, callbackUri.Scheme); | ||||
|         session = UniWebViewAuthenticationSession.Create(authUrl, callbackUri.Scheme); | ||||
|         var flow = service as UniWebViewAuthenticationCommonFlow; | ||||
|         if (flow != null && flow.privateMode) { | ||||
|             session.SetPrivateMode(true); | ||||
|         } | ||||
|          | ||||
|         #if UNITY_IOS | ||||
|         #if UNITY_EDITOR | ||||
|         // Editor does not support deep link. | ||||
|         UniWebViewLogger.Instance.Info("Seems you are trying to perform OAuth with a universal link. It is not supported in editor. Try on a real device."); | ||||
|         #else | ||||
|         // The callback URL seems to be a universal link. We need to handle it in a different way. | ||||
|         if (callbackUri.Scheme == "https") { | ||||
|             Application.deepLinkActivated += HandleUniversalLink; | ||||
|         } | ||||
|          | ||||
|         #endif // UNITY_EDITOR | ||||
|         #endif // UNITY_IOS | ||||
|          | ||||
|         session.OnAuthenticationFinished += (_, resultUrl) =>  { | ||||
|             UniWebViewLogger.Instance.Verbose("Auth flow received callback url: " + resultUrl); | ||||
|             ExchangeToken(resultUrl); | ||||
|             Application.deepLinkActivated -= HandleUniversalLink; | ||||
|         }; | ||||
| 
 | ||||
|         session.OnAuthenticationErrorReceived += (_, errorCode, message) => { | ||||
|             FlowErrored(errorCode, message); | ||||
|             UniWebViewLogger.Instance.Verbose("Auth flow received error: " + errorCode + ". Detail: " + message); | ||||
|             ExchangeTokenErrored(errorCode, message); | ||||
|             Application.deepLinkActivated -= HandleUniversalLink; | ||||
|         }; | ||||
|          | ||||
|         UniWebViewLogger.Instance.Verbose("Starting auth flow with url: " + authUrl + "; Callback scheme: " + callbackUri.Scheme); | ||||
|         session.Start(); | ||||
|     } | ||||
|      | ||||
|     private void HandleUniversalLink(string url) { | ||||
|         if (url.StartsWith(url, StringComparison.InvariantCultureIgnoreCase)) { | ||||
|             UniWebViewLogger.Instance.Verbose("HandleUniversalLink: " + url); | ||||
|             Application.deepLinkActivated -= HandleUniversalLink; | ||||
|             session.Cancel(); | ||||
|             ExchangeToken(url);            | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void ExchangeToken(string response) { | ||||
|         try { | ||||
|             var args = service.GetAccessTokenRequestParameters(response); | ||||
|             var request = GetTokenRequest(args); | ||||
|             MonoBehaviour context = (MonoBehaviour)service; | ||||
|             context.StartCoroutine(SendTokenRequest(request)); | ||||
|             context.StartCoroutine(SendExchangeTokenRequest(request)); | ||||
|         } catch (Exception e) { | ||||
|             var message = e.Message; | ||||
|             var code = -1; | ||||
|             if (e is AuthenticationResponseException ex) { | ||||
|                 code = ex.Code; | ||||
|             } | ||||
|             UniWebViewLogger.Instance.Critical("Exception on parsing response: " + e + ". Code: " + code + ". Message: " + message); | ||||
|             FlowErrored(code, message); | ||||
|             UniWebViewLogger.Instance.Critical("Exception on exchange token response: " + e + ". Code: " + code + ". Message: " + message); | ||||
|             ExchangeTokenErrored(code, message); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Refresh the access token with the given refresh token. | ||||
|     /// </summary> | ||||
|     /// <param name="refreshToken"></param> | ||||
|     public void RefreshToken(string refreshToken) { | ||||
|         try { | ||||
|             var args = service.GetRefreshTokenRequestParameters(refreshToken); | ||||
|             var request = GetTokenRequest(args); | ||||
|             MonoBehaviour context = (MonoBehaviour)service; | ||||
|             context.StartCoroutine(SendRefreshTokenRequest(request)); | ||||
|         } catch (Exception e) { | ||||
|             var message = e.Message; | ||||
|             var code = -1; | ||||
|             if (e is AuthenticationResponseException ex) { | ||||
|                 code = ex.Code; | ||||
|             } | ||||
|             UniWebViewLogger.Instance.Critical("Exception on refresh token response: " + e + ". Code: " + code + ". Message: " + message); | ||||
|             RefreshTokenErrored(code, message); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private string GetAuthUrl() { | ||||
|         var builder = new UriBuilder(service.GetAuthenticationConfiguration().authorizationEndpoint); | ||||
|         var query = System.Web.HttpUtility.ParseQueryString(""); | ||||
|         var query = new Dictionary<string, string>(); | ||||
|         foreach (var kv in service.GetAuthenticationUriArguments()) { | ||||
|             query.Add(kv.Key, kv.Value); | ||||
|         } | ||||
|         builder.Query = query.ToString(); | ||||
|         var additionalQuery = service.GetAdditionalAuthenticationUriQuery(); | ||||
|         builder.Query = UniWebViewAuthenticationUtils.CreateQueryString(query, additionalQuery); | ||||
|         return builder.ToString(); | ||||
|     } | ||||
| 
 | ||||
|  | @ -181,55 +265,78 @@ public class UniWebViewAuthenticationFlow<TTokenType> { | |||
|         return UnityWebRequest.Post(builder.ToString(), form); | ||||
|     } | ||||
|      | ||||
|     private IEnumerator SendTokenRequest(UnityWebRequest request) { | ||||
|         using (var www = request) { | ||||
|             yield return www.SendWebRequest(); | ||||
|             if (www.result != UnityWebRequest.Result.Success) { | ||||
|                 string errorMessage = null; | ||||
|                 string errorBody = null; | ||||
|                 if (www.error != null) { | ||||
|                     errorMessage = www.error; | ||||
|                 } | ||||
|                 if (www.downloadHandler != null && www.downloadHandler.text != null) { | ||||
|                     errorBody = www.downloadHandler.text; | ||||
|                 } | ||||
|                 UniWebViewLogger.Instance.Critical("Failed to get access token. Error: " + errorMessage + ". " + errorBody); | ||||
|                 FlowErrored(www.responseCode, errorBody ?? errorMessage); | ||||
|             } else { | ||||
|                 var responseText = www.downloadHandler.text; | ||||
|                 UniWebViewLogger.Instance.Info("Token exchange request succeeded. Response: " + responseText); | ||||
|                 try { | ||||
|                     var token = service.GenerateTokenFromExchangeResponse(www.downloadHandler.text); | ||||
|                     FlowFinished(token); | ||||
|                 } catch (Exception e) { | ||||
|                     var message = e.Message; | ||||
|                     var code = -1; | ||||
|                     if (e is AuthenticationResponseException ex) { | ||||
|                         code = ex.Code; | ||||
|                     } | ||||
|                     UniWebViewLogger.Instance.Critical( | ||||
|                         "Exception on parsing token response: " + e + ". Code: " + code + ". Message: " +  | ||||
|                         message + ". Response: " + responseText); | ||||
|                     FlowErrored(code, message); | ||||
|     private IEnumerator SendExchangeTokenRequest(UnityWebRequest request) { | ||||
|         return SendTokenRequest(request, ExchangeTokenFinished, ExchangeTokenErrored); | ||||
|     } | ||||
| 
 | ||||
|     private IEnumerator SendRefreshTokenRequest(UnityWebRequest request) { | ||||
|         return SendTokenRequest(request, RefreshTokenFinished, RefreshTokenErrored); | ||||
|     } | ||||
| 
 | ||||
|     private IEnumerator SendTokenRequest(UnityWebRequest request, Action<TTokenType> finishAction, Action<long, string>errorAction) | ||||
|     { | ||||
|         using var www = request; | ||||
|         yield return www.SendWebRequest(); | ||||
|         if (www.result != UnityWebRequest.Result.Success) { | ||||
|             string errorMessage = null; | ||||
|             string errorBody = null; | ||||
|             if (www.error != null) { | ||||
|                 errorMessage = www.error; | ||||
|             } | ||||
|             if (www.downloadHandler != null && www.downloadHandler.text != null) { | ||||
|                 errorBody = www.downloadHandler.text; | ||||
|             } | ||||
|             UniWebViewLogger.Instance.Critical("Failed to get access token. Error: " + errorMessage + ". " + errorBody); | ||||
|             errorAction(www.responseCode, errorBody ?? errorMessage); | ||||
|         } else { | ||||
|             var responseText = www.downloadHandler.text; | ||||
|             UniWebViewLogger.Instance.Info("Token exchange request succeeded. Response: " + responseText); | ||||
|             try { | ||||
|                 var token = service.GenerateTokenFromExchangeResponse(www.downloadHandler.text); | ||||
|                 finishAction(token); | ||||
|             } catch (Exception e) { | ||||
|                 var message = e.Message; | ||||
|                 var code = -1; | ||||
|                 if (e is AuthenticationResponseException ex) { | ||||
|                     code = ex.Code; | ||||
|                 } | ||||
|                 UniWebViewLogger.Instance.Critical( | ||||
|                     "Exception on parsing token response: " + e + ". Code: " + code + ". Message: " +  | ||||
|                     message + ". Response: " + responseText); | ||||
|                 errorAction(code, message); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     private void FlowFinished(TTokenType token) { | ||||
| 
 | ||||
|     private void ExchangeTokenFinished(TTokenType token) { | ||||
|         if (service.OnAuthenticationFinished != null) { | ||||
|             service.OnAuthenticationFinished.Invoke(token);             | ||||
|         } | ||||
|         service = null;; | ||||
|         service = null; | ||||
|     } | ||||
|      | ||||
|     private void FlowErrored(long code, string message) { | ||||
|     private void ExchangeTokenErrored(long code, string message) { | ||||
|         UniWebViewLogger.Instance.Info("Auth flow errored: " + code + ". Detail: " + message); | ||||
|         if (service.OnAuthenticationErrored != null) { | ||||
|             service.OnAuthenticationErrored.Invoke(code, message); | ||||
|         } | ||||
|         service = null; | ||||
|     } | ||||
|      | ||||
|     private void RefreshTokenFinished(TTokenType token) { | ||||
|         if (service.OnRefreshTokenFinished != null) { | ||||
|             service.OnRefreshTokenFinished.Invoke(token); | ||||
|         } | ||||
|         service = null; | ||||
|     } | ||||
|      | ||||
|     private void RefreshTokenErrored(long code, string message) { | ||||
|         UniWebViewLogger.Instance.Info("Refresh flow errored: " + code + ". Detail: " + message); | ||||
|         if (service.OnRefreshTokenErrored != null) { | ||||
|             service.OnRefreshTokenErrored.Invoke(code, message); | ||||
|         } | ||||
|         service = null; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// <summary> | ||||
|  |  | |||
|  | @ -49,7 +49,7 @@ public class UniWebViewAuthenticationFlowCustomize : UniWebViewAuthenticationCom | |||
|     /// The client Id of your OAuth application. | ||||
|     /// </summary> | ||||
|     public string clientId = ""; | ||||
|      | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// The redirect URI of your OAuth application. The service provider is expected to call this URI to pass back the | ||||
|     /// authorization code. It should be something also set to your OAuth application. | ||||
|  | @ -77,11 +77,21 @@ public class UniWebViewAuthenticationFlowCustomize : UniWebViewAuthenticationCom | |||
|         var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationStandardToken>(this); | ||||
|         flow.StartAuth(); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Starts the refresh flow with the standard OAuth 2.0. | ||||
|     /// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`. | ||||
|     /// </summary> | ||||
|     /// <param name="refreshToken">The refresh token received with a previous access token response.</param> | ||||
|     public override void StartRefreshTokenFlow(string refreshToken) { | ||||
|         var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationStandardToken>(this); | ||||
|         flow.RefreshToken(refreshToken); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public UniWebViewAuthenticationConfiguration GetAuthenticationConfiguration() { | ||||
|     public virtual UniWebViewAuthenticationConfiguration GetAuthenticationConfiguration() { | ||||
|         return new UniWebViewAuthenticationConfiguration( | ||||
|             config.authorizationEndpoint,  | ||||
|             config.tokenEndpoint | ||||
|  | @ -91,14 +101,14 @@ public class UniWebViewAuthenticationFlowCustomize : UniWebViewAuthenticationCom | |||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public string GetCallbackUrl() { | ||||
|     public virtual string GetCallbackUrl() { | ||||
|         return redirectUri; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public Dictionary<string, string> GetAuthenticationUriArguments() { | ||||
|     public virtual Dictionary<string, string> GetAuthenticationUriArguments() { | ||||
|         var authorizeArgs = new Dictionary<string, string> { | ||||
|             { "client_id", clientId }, | ||||
|             { "redirect_uri", redirectUri }, | ||||
|  | @ -122,12 +132,16 @@ public class UniWebViewAuthenticationFlowCustomize : UniWebViewAuthenticationCom | |||
| 
 | ||||
|         return authorizeArgs; | ||||
|     } | ||||
|      | ||||
|     public string GetAdditionalAuthenticationUriQuery() { | ||||
|         return optional.additionalAuthenticationUriQuery; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) { | ||||
|         if (!authResponse.StartsWith(redirectUri)) { | ||||
|     public virtual Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) { | ||||
|         if (!authResponse.StartsWith(redirectUri, StringComparison.InvariantCultureIgnoreCase)) { | ||||
|             throw AuthenticationResponseException.UnexpectedAuthCallbackUrl; | ||||
|         } | ||||
|          | ||||
|  | @ -136,7 +150,7 @@ public class UniWebViewAuthenticationFlowCustomize : UniWebViewAuthenticationCom | |||
|         if (!response.TryGetValue("code", out var code)) { | ||||
|             throw AuthenticationResponseException.InvalidResponse(authResponse); | ||||
|         } | ||||
|         if (optional.enableState) { | ||||
|         if (optional != null && optional.enableState) { | ||||
|             VerifyState(response); | ||||
|         } | ||||
|         var parameters = new Dictionary<string, string> { | ||||
|  | @ -148,6 +162,9 @@ public class UniWebViewAuthenticationFlowCustomize : UniWebViewAuthenticationCom | |||
|         if (CodeVerify != null) { | ||||
|             parameters.Add("code_verifier", CodeVerify); | ||||
|         } | ||||
|         if (optional != null && !String.IsNullOrEmpty(optional.clientSecret)) { | ||||
|             parameters.Add("client_secret", optional.clientSecret); | ||||
|         } | ||||
| 
 | ||||
|         return parameters; | ||||
|     } | ||||
|  | @ -155,7 +172,22 @@ public class UniWebViewAuthenticationFlowCustomize : UniWebViewAuthenticationCom | |||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public UniWebViewAuthenticationStandardToken GenerateTokenFromExchangeResponse(string exchangeResponse) { | ||||
|     public virtual Dictionary<string, string> GetRefreshTokenRequestParameters(string refreshToken) { | ||||
|         var parameters = new Dictionary<string, string> { | ||||
|             { "client_id", clientId }, | ||||
|             { "refresh_token", refreshToken }, | ||||
|             { "grant_type", config.refreshTokenGrantType } | ||||
|         }; | ||||
|         if (optional != null && !String.IsNullOrEmpty(optional.clientSecret)) { | ||||
|             parameters.Add("client_secret", optional.clientSecret); | ||||
|         } | ||||
|         return parameters; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public virtual UniWebViewAuthenticationStandardToken GenerateTokenFromExchangeResponse(string exchangeResponse) { | ||||
|         return UniWebViewAuthenticationTokenFactory<UniWebViewAuthenticationStandardToken>.Parse(exchangeResponse); | ||||
|     } | ||||
| 
 | ||||
|  | @ -170,6 +202,18 @@ public class UniWebViewAuthenticationFlowCustomize : UniWebViewAuthenticationCom | |||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<long, string> OnAuthenticationErrored { get; set; } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<UniWebViewAuthenticationStandardToken> OnRefreshTokenFinished { get; set; } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<long, string> OnRefreshTokenErrored { get; set; } | ||||
| } | ||||
| 
 | ||||
| [Serializable] | ||||
|  | @ -178,10 +222,14 @@ public class UniWebViewAuthenticationFlowCustomizeConfig { | |||
|     public string tokenEndpoint = ""; | ||||
|     public string responseType = "code"; | ||||
|     public string grantType = "authorization_code"; | ||||
| 
 | ||||
|     public string refreshTokenGrantType = "refresh_token"; | ||||
| } | ||||
| 
 | ||||
| [Serializable] | ||||
| public class UniWebViewAuthenticationFlowCustomizeOptional { | ||||
|     public UniWebViewAuthenticationPKCE PKCESupport = UniWebViewAuthenticationPKCE.None;  | ||||
|     public bool enableState = false; | ||||
|     public string clientSecret = ""; | ||||
|     public string additionalAuthenticationUriQuery = ""; | ||||
| } | ||||
|  | @ -53,9 +53,9 @@ public class UniWebViewAuthenticationFlowDiscord : UniWebViewAuthenticationCommo | |||
|     /// Optional to control this flow's behaviour. | ||||
|     /// </summary> | ||||
|     public UniWebViewAuthenticationFlowDiscordOptional optional; | ||||
|      | ||||
|     private string responseType = "code"; | ||||
|     private string grantType = "authorization_code"; | ||||
| 
 | ||||
|     private const string responseType = "code"; | ||||
|     private const string grantType = "authorization_code"; | ||||
| 
 | ||||
|     private readonly UniWebViewAuthenticationConfiguration config =  | ||||
|         new UniWebViewAuthenticationConfiguration( | ||||
|  | @ -71,7 +71,17 @@ public class UniWebViewAuthenticationFlowDiscord : UniWebViewAuthenticationCommo | |||
|         var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationDiscordToken>(this); | ||||
|         flow.StartAuth(); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Starts the refresh flow with the standard OAuth 2.0. | ||||
|     /// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`. | ||||
|     /// </summary> | ||||
|     /// <param name="refreshToken">The refresh token received with a previous access token response.</param> | ||||
|     public override void StartRefreshTokenFlow(string refreshToken) { | ||||
|         var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationDiscordToken>(this); | ||||
|         flow.RefreshToken(refreshToken); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|  | @ -113,12 +123,16 @@ public class UniWebViewAuthenticationFlowDiscord : UniWebViewAuthenticationCommo | |||
| 
 | ||||
|         return authorizeArgs; | ||||
|     } | ||||
|      | ||||
|     public string GetAdditionalAuthenticationUriQuery() { | ||||
|         return optional.additionalAuthenticationUriQuery; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) { | ||||
|         if (!authResponse.StartsWith(redirectUri)) { | ||||
|         if (!authResponse.StartsWith(redirectUri, StringComparison.InvariantCultureIgnoreCase)) { | ||||
|             throw AuthenticationResponseException.UnexpectedAuthCallbackUrl; | ||||
|         } | ||||
|          | ||||
|  | @ -144,6 +158,18 @@ public class UniWebViewAuthenticationFlowDiscord : UniWebViewAuthenticationCommo | |||
|         return parameters; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public Dictionary<string, string> GetRefreshTokenRequestParameters(string refreshToken) { | ||||
|         return new Dictionary<string, string> { | ||||
|             { "client_id", clientId },  | ||||
|             { "client_secret", clientSecret },  | ||||
|             { "refresh_token", refreshToken }, | ||||
|             { "grant_type", "refresh_token" } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|  | @ -151,10 +177,29 @@ public class UniWebViewAuthenticationFlowDiscord : UniWebViewAuthenticationCommo | |||
|         return UniWebViewAuthenticationTokenFactory<UniWebViewAuthenticationDiscordToken>.Parse(exchangeResponse); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<UniWebViewAuthenticationDiscordToken> OnAuthenticationFinished { get; set; } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<long, string> OnAuthenticationErrored { get; set; } | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<UniWebViewAuthenticationDiscordToken> OnRefreshTokenFinished { get; set; } | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<long, string> OnRefreshTokenErrored { get; set; } | ||||
| } | ||||
| 
 | ||||
| /// <summary> | ||||
|  | @ -172,6 +217,16 @@ public class UniWebViewAuthenticationFlowDiscordOptional { | |||
|     /// authentication callback. Default is `true`. | ||||
|     /// </summary> | ||||
|     public bool enableState = true; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The additional query arguments that are used to construct the query string of the authentication request. | ||||
|     ///  | ||||
|     /// This is useful when you want to add some custom parameters to the authentication request. This string will be  | ||||
|     /// appended to the query string that constructed from `GetAuthenticationUriArguments`.  | ||||
|     ///  | ||||
|     /// For example, if you set `prompt=consent&ui_locales=en`, it will be contained in the final authentication query. | ||||
|     /// </summary> | ||||
|     public string additionalAuthenticationUriQuery = ""; | ||||
| } | ||||
| 
 | ||||
| /// <summary> | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ | |||
| 
 | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Collections.Specialized; | ||||
| using UnityEngine; | ||||
| using UnityEngine.Events; | ||||
| 
 | ||||
|  | @ -49,9 +50,10 @@ public class UniWebViewAuthenticationFlowFacebook: UniWebViewAuthenticationCommo | |||
|     public UniWebViewAuthenticationFlowFacebookOptional optional; | ||||
|      | ||||
|     // The redirect URL should be exactly this one. Web view should inspect the loading of this URL to handle the result. | ||||
|     private string redirectUri = "https://www.facebook.com/connect/login_success.html"; | ||||
|     private const string redirectUri = "https://www.facebook.com/connect/login_success.html"; | ||||
| 
 | ||||
|     // Only `token` response type is supported to use Facebook Login as the manual flow. | ||||
|     private string responseType = "token"; | ||||
|     private const string responseType = "token"; | ||||
|      | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<UniWebViewAuthenticationFacebookToken> OnAuthenticationFinished { get; set; } | ||||
|  | @ -72,6 +74,12 @@ public class UniWebViewAuthenticationFlowFacebook: UniWebViewAuthenticationCommo | |||
|     /// </summary> | ||||
|     public override void StartAuthenticationFlow() { | ||||
|         var webView = gameObject.AddComponent<UniWebView>(); | ||||
|          | ||||
|         // Facebook login deprecates the Web View login on Android. As a workaround, prevents to be a desktop browser to continue the manual flow. | ||||
|         #if UNITY_ANDROID && !UNITY_EDITOR | ||||
|         webView.SetUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 13_0_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15"); | ||||
|         #endif | ||||
|          | ||||
|         webView.OnPageFinished += (view, status, url) => { | ||||
|             if (status != 200) { | ||||
|                 if (OnAuthenticationErrored != null) { | ||||
|  | @ -114,24 +122,35 @@ public class UniWebViewAuthenticationFlowFacebook: UniWebViewAuthenticationCommo | |||
|                 } | ||||
|             } | ||||
|         }; | ||||
|         webView.OnPageErrorReceived += (view, code, message) => { | ||||
|         webView.OnLoadingErrorReceived += (view, code, message, payload) => { | ||||
|             if (OnAuthenticationErrored != null) { | ||||
|                 OnAuthenticationErrored.Invoke(code, message); | ||||
|             } | ||||
|         }; | ||||
|         webView.Frame = new Rect(0, 0, Screen.width, Screen.height); | ||||
|         webView.Load(GetAuthUrl()); | ||||
|         webView.SetShowToolbar(true, adjustInset: true); | ||||
|         webView.EmbeddedToolbar.Show(); | ||||
|         webView.Show(false, UniWebViewTransitionEdge.Bottom, 0.25f); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Starts the refresh flow with the standard OAuth 2.0. | ||||
|     /// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`. | ||||
|     /// </summary> | ||||
|     /// <param name="refreshToken">The refresh token received with a previous access token response.</param> | ||||
|     public override void StartRefreshTokenFlow(string refreshToken) { | ||||
|         Debug.LogError("Facebook does not provide a refresh token flow when building with the manual flow."); | ||||
|         throw new NotImplementedException(); | ||||
|     } | ||||
| 
 | ||||
|     private string GetAuthUrl() { | ||||
|         var builder = new UriBuilder(config.authorizationEndpoint); | ||||
|         var query = System.Web.HttpUtility.ParseQueryString(""); | ||||
|         var query = new Dictionary<string, string>(); | ||||
|         foreach (var kv in GetAuthenticationUriArguments()) { | ||||
|             query.Add(kv.Key, kv.Value); | ||||
|         } | ||||
|         builder.Query = query.ToString(); | ||||
| 
 | ||||
|         builder.Query = UniWebViewAuthenticationUtils.CreateQueryString(query, optional.additionalAuthenticationUriQuery); | ||||
|         return builder.ToString(); | ||||
|     } | ||||
| 
 | ||||
|  | @ -163,6 +182,16 @@ public class UniWebViewAuthenticationFlowFacebookOptional { | |||
|     /// The scope string of all your required scopes. | ||||
|     /// </summary> | ||||
|     public string scope = ""; | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// The additional query arguments that are used to construct the query string of the authentication request. | ||||
|     ///  | ||||
|     /// This is useful when you want to add some custom parameters to the authentication request. This string will be  | ||||
|     /// appended to the query string that constructed from `GetAuthenticationUriArguments`.  | ||||
|     ///  | ||||
|     /// For example, if you set `prompt=consent&ui_locales=en`, it will be contained in the final authentication query. | ||||
|     /// </summary> | ||||
|     public string additionalAuthenticationUriQuery = ""; | ||||
| } | ||||
| 
 | ||||
| /// The token object from Facebook.  | ||||
|  | @ -187,7 +216,7 @@ public class UniWebViewAuthenticationFacebookToken { | |||
|      | ||||
|     public UniWebViewAuthenticationFacebookToken(string response, Dictionary<string, string> values) { | ||||
|         RawValue = response; | ||||
|         AccessToken = values.ContainsKey("access_token") ? values["access_token"] as string : null ; | ||||
|         AccessToken = values.ContainsKey("access_token") ? values["access_token"] : null ; | ||||
|         if (AccessToken == null) { | ||||
|             throw AuthenticationResponseException.InvalidResponse(response); | ||||
|         } | ||||
|  |  | |||
|  | @ -56,10 +56,29 @@ public class UniWebViewAuthenticationFlowGitHub: UniWebViewAuthenticationCommonF | |||
|             "https://github.com/login/oauth/access_token" | ||||
|         ); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<UniWebViewAuthenticationGitHubToken> OnAuthenticationFinished { get; set; } | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<long, string> OnAuthenticationErrored { get; set; } | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<UniWebViewAuthenticationGitHubToken> OnRefreshTokenFinished { get; set;  } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<long, string> OnRefreshTokenErrored { get; set; } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Starts the authentication flow with the standard OAuth 2.0. | ||||
|  | @ -70,6 +89,16 @@ public class UniWebViewAuthenticationFlowGitHub: UniWebViewAuthenticationCommonF | |||
|         flow.StartAuth(); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Starts the refresh flow with the standard OAuth 2.0. | ||||
|     /// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`. | ||||
|     /// </summary> | ||||
|     /// <param name="refreshToken">The refresh token received with a previous access token response.</param> | ||||
|     public override void StartRefreshTokenFlow(string refreshToken) { | ||||
|         var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationGitHubToken>(this); | ||||
|         flow.RefreshToken(refreshToken); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|  | @ -92,11 +121,18 @@ public class UniWebViewAuthenticationFlowGitHub: UniWebViewAuthenticationCommonF | |||
|             if (!optional.allowSignup) { // The default value is true. | ||||
|                 authorizeArgs.Add("allow_signup", "false"); | ||||
|             } | ||||
|             if (!String.IsNullOrEmpty(optional.prompt)) { | ||||
|                 authorizeArgs.Add("prompt", optional.prompt); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return authorizeArgs; | ||||
|     } | ||||
| 
 | ||||
|     public string GetAdditionalAuthenticationUriQuery() { | ||||
|         return optional.additionalAuthenticationUriQuery; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|  | @ -115,7 +151,7 @@ public class UniWebViewAuthenticationFlowGitHub: UniWebViewAuthenticationCommonF | |||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) { | ||||
|         if (!authResponse.StartsWith(callbackUrl)) { | ||||
|         if (!authResponse.StartsWith(callbackUrl, StringComparison.InvariantCultureIgnoreCase)) { | ||||
|             throw AuthenticationResponseException.UnexpectedAuthCallbackUrl; | ||||
|         } | ||||
|         var uri = new Uri(authResponse); | ||||
|  | @ -138,6 +174,18 @@ public class UniWebViewAuthenticationFlowGitHub: UniWebViewAuthenticationCommonF | |||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public Dictionary<string, string> GetRefreshTokenRequestParameters(string refreshToken) { | ||||
|         return new Dictionary<string, string> { | ||||
|             { "client_id", clientId },  | ||||
|             { "client_secret", clientSecret },  | ||||
|             { "refresh_token", refreshToken }, | ||||
|             { "grant_type", "refresh_token" } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|  | @ -169,9 +217,27 @@ public class UniWebViewAuthenticationFlowGitHubOptional { | |||
|     /// </summary> | ||||
|     public bool enableState = false; | ||||
|     /// <summary> | ||||
|     /// Whether or not unauthenticated users will be offered an option to sign up for GitHub during the OAuth flow. | ||||
|     /// Whether unauthenticated users will be offered an option to sign up for GitHub during the OAuth flow. | ||||
|     /// </summary> | ||||
|     public bool allowSignup = true; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The prompt that will be set to the authentication request query. For example, the possible values can be  | ||||
|     /// `login`, `consent`, `select_account` and so on. | ||||
|     ///  | ||||
|     /// See https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest | ||||
|     /// </summary> | ||||
|     public string prompt = ""; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The additional query arguments that are used to construct the query string of the authentication request. | ||||
|     ///  | ||||
|     /// This is useful when you want to add some custom parameters to the authentication request. This string will be  | ||||
|     /// appended to the query string that constructed from `GetAuthenticationUriArguments`.  | ||||
|     ///  | ||||
|     /// For example, if you set `prompt=consent&ui_locales=en`, it will be contained in the final authentication query. | ||||
|     /// </summary> | ||||
|     public string additionalAuthenticationUriQuery = ""; | ||||
| } | ||||
| 
 | ||||
| /// <summary> | ||||
|  |  | |||
|  | @ -57,8 +57,8 @@ public class UniWebViewAuthenticationFlowGoogle : UniWebViewAuthenticationCommon | |||
|     /// </summary> | ||||
|     public UniWebViewAuthenticationFlowGoogleOptional optional; | ||||
|      | ||||
|     private string responseType = "code"; | ||||
|     private string grantType = "authorization_code"; | ||||
|     private const string responseType = "code"; | ||||
|     private const string grantType = "authorization_code"; | ||||
| 
 | ||||
|     private readonly UniWebViewAuthenticationConfiguration config =  | ||||
|         new UniWebViewAuthenticationConfiguration( | ||||
|  | @ -74,7 +74,17 @@ public class UniWebViewAuthenticationFlowGoogle : UniWebViewAuthenticationCommon | |||
|         var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationGoogleToken>(this); | ||||
|         flow.StartAuth(); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Starts the refresh flow with the standard OAuth 2.0. | ||||
|     /// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`. | ||||
|     /// </summary> | ||||
|     /// <param name="refreshToken">The refresh token received with a previous access token response.</param> | ||||
|     public override void StartRefreshTokenFlow(string refreshToken) { | ||||
|         var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationGoogleToken>(this); | ||||
|         flow.RefreshToken(refreshToken); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|  | @ -116,16 +126,23 @@ public class UniWebViewAuthenticationFlowGoogle : UniWebViewAuthenticationCommon | |||
|             if (!String.IsNullOrEmpty(optional.loginHint)) { | ||||
|                 authorizeArgs.Add("login_hint", optional.loginHint); | ||||
|             } | ||||
|             if (!String.IsNullOrEmpty(optional.prompt)) { | ||||
|                 authorizeArgs.Add("prompt", optional.prompt); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return authorizeArgs; | ||||
|     } | ||||
|      | ||||
|     public string GetAdditionalAuthenticationUriQuery() { | ||||
|         return optional.additionalAuthenticationUriQuery; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) { | ||||
|         if (!authResponse.StartsWith(redirectUri)) { | ||||
|         if (!authResponse.StartsWith(redirectUri, StringComparison.InvariantCultureIgnoreCase)) { | ||||
|             throw AuthenticationResponseException.UnexpectedAuthCallbackUrl; | ||||
|         } | ||||
|          | ||||
|  | @ -149,7 +166,18 @@ public class UniWebViewAuthenticationFlowGoogle : UniWebViewAuthenticationCommon | |||
| 
 | ||||
|         return parameters; | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public Dictionary<string, string> GetRefreshTokenRequestParameters(string refreshToken) { | ||||
|         return new Dictionary<string, string> { | ||||
|             { "client_id", clientId }, | ||||
|             { "refresh_token", refreshToken }, | ||||
|             { "grant_type", "refresh_token" } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|  | @ -157,10 +185,29 @@ public class UniWebViewAuthenticationFlowGoogle : UniWebViewAuthenticationCommon | |||
|         return UniWebViewAuthenticationTokenFactory<UniWebViewAuthenticationGoogleToken>.Parse(exchangeResponse); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<UniWebViewAuthenticationGoogleToken> OnAuthenticationFinished { get; set; } | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<long, string> OnAuthenticationErrored { get; set; } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<UniWebViewAuthenticationGoogleToken> OnRefreshTokenFinished { get; set; } | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<long, string> OnRefreshTokenErrored { get; set; } | ||||
| } | ||||
| 
 | ||||
| /// <summary> | ||||
|  | @ -182,6 +229,24 @@ public class UniWebViewAuthenticationFlowGoogleOptional { | |||
|     /// the Google Authentication Server.  | ||||
|     /// </summary> | ||||
|     public string loginHint = ""; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The prompt that will be set to the authentication request query. For example, the possible values can be  | ||||
|     /// `login`, `consent`, `select_account` and so on. | ||||
|     ///  | ||||
|     /// See https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest | ||||
|     /// </summary> | ||||
|     public string prompt = ""; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The additional query arguments that are used to construct the query string of the authentication request. | ||||
|     ///  | ||||
|     /// This is useful when you want to add some custom parameters to the authentication request. This string will be  | ||||
|     /// appended to the query string that constructed from `GetAuthenticationUriArguments`.  | ||||
|     ///  | ||||
|     /// For example, if you set `prompt=consent&ui_locales=en`, it will be contained in the final authentication query. | ||||
|     /// </summary> | ||||
|     public string additionalAuthenticationUriQuery = ""; | ||||
| } | ||||
| 
 | ||||
| /// <summary> | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ | |||
| 
 | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Collections.Specialized; | ||||
| using UnityEngine; | ||||
| using UnityEngine.Events; | ||||
| 
 | ||||
|  | @ -57,8 +58,8 @@ public class UniWebViewAuthenticationFlowLine : UniWebViewAuthenticationCommonFl | |||
|     /// </summary> | ||||
|     public UniWebViewAuthenticationFlowLineOptional optional; | ||||
|      | ||||
|     private string responseType = "code"; | ||||
|     private string grantType = "authorization_code"; | ||||
|     private const string responseType = "code"; | ||||
|     private const string grantType = "authorization_code"; | ||||
| 
 | ||||
|     private string RedirectUri { | ||||
|         get { | ||||
|  | @ -88,7 +89,17 @@ public class UniWebViewAuthenticationFlowLine : UniWebViewAuthenticationCommonFl | |||
|         var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationLineToken>(this); | ||||
|         flow.StartAuth(); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Starts the refresh flow with the standard OAuth 2.0. | ||||
|     /// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`. | ||||
|     /// </summary> | ||||
|     /// <param name="refreshToken">The refresh token received with a previous access token response.</param> | ||||
|     public override void StartRefreshTokenFlow(string refreshToken) { | ||||
|         var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationLineToken>(this); | ||||
|         flow.RefreshToken(refreshToken); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|  | @ -114,13 +125,18 @@ public class UniWebViewAuthenticationFlowLine : UniWebViewAuthenticationCommonFl | |||
|          | ||||
|         return authorizeArgs; | ||||
|     } | ||||
|      | ||||
|     public string GetAdditionalAuthenticationUriQuery() { | ||||
|         return optional.additionalAuthenticationUriQuery; | ||||
|     } | ||||
| 
 | ||||
|     private string GenerateReturnUri() { | ||||
|         var query = System.Web.HttpUtility.ParseQueryString(""); | ||||
|         query.Add("response_type", responseType); | ||||
|         query.Add("client_id", clientId); | ||||
|         query.Add("redirect_uri", RedirectUri); | ||||
|          | ||||
|         var query = new Dictionary<string, string> { | ||||
|             { "response_type", responseType }, | ||||
|             { "client_id", clientId }, | ||||
|             { "redirect_uri", RedirectUri } | ||||
|         }; | ||||
| 
 | ||||
|         // State is a must in LINE Login. | ||||
|         var state = GenerateAndStoreState(); | ||||
|         query.Add("state", state); | ||||
|  | @ -139,7 +155,8 @@ public class UniWebViewAuthenticationFlowLine : UniWebViewAuthenticationCommonFl | |||
|                 query.Add("code_challenge_method", method); | ||||
|             } | ||||
|         } | ||||
|         return "/oauth2/v2.1/authorize/consent?" + query; | ||||
|         var additionalQuery = GetAdditionalAuthenticationUriQuery(); | ||||
|         return "/oauth2/v2.1/authorize/consent?" + UniWebViewAuthenticationUtils.CreateQueryString(query, additionalQuery); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|  | @ -147,7 +164,7 @@ public class UniWebViewAuthenticationFlowLine : UniWebViewAuthenticationCommonFl | |||
|     /// </summary> | ||||
|     public Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) { | ||||
|         var normalizedRedirectUri = UniWebViewAuthenticationUtils.ConvertIntentUri(RedirectUri); | ||||
|         if (!authResponse.StartsWith(normalizedRedirectUri)) { | ||||
|         if (!authResponse.StartsWith(normalizedRedirectUri, StringComparison.InvariantCultureIgnoreCase)) { | ||||
|             throw AuthenticationResponseException.UnexpectedAuthCallbackUrl; | ||||
|         } | ||||
|          | ||||
|  | @ -170,6 +187,17 @@ public class UniWebViewAuthenticationFlowLine : UniWebViewAuthenticationCommonFl | |||
|         return parameters; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public Dictionary<string, string> GetRefreshTokenRequestParameters(string refreshToken) { | ||||
|         return new Dictionary<string, string> { | ||||
|             { "client_id", clientId },  | ||||
|             { "refresh_token", refreshToken }, | ||||
|             { "grant_type", "refresh_token" } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|  | @ -181,6 +209,10 @@ public class UniWebViewAuthenticationFlowLine : UniWebViewAuthenticationCommonFl | |||
|     public UnityEvent<UniWebViewAuthenticationLineToken> OnAuthenticationFinished { get; set; } | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<long, string> OnAuthenticationErrored { get; set; } | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<UniWebViewAuthenticationLineToken> OnRefreshTokenFinished { get; set;  } | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<long, string> OnRefreshTokenErrored { get; set; } | ||||
| } | ||||
| 
 | ||||
| /// <summary> | ||||
|  | @ -192,6 +224,16 @@ public class UniWebViewAuthenticationFlowLineOptional { | |||
|     /// Whether to enable PKCE when performing authentication. Default is `S256`. | ||||
|     /// </summary> | ||||
|     public UniWebViewAuthenticationPKCE PKCESupport = UniWebViewAuthenticationPKCE.S256; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The additional query arguments that are used to construct the query string of the authentication request. | ||||
|     ///  | ||||
|     /// This is useful when you want to add some custom parameters to the authentication request. This string will be  | ||||
|     /// appended to the query string that constructed from `GetAuthenticationUriArguments`.  | ||||
|     ///  | ||||
|     /// For example, if you set `prompt=consent&ui_locales=en`, it will be contained in the final authentication query. | ||||
|     /// </summary> | ||||
|     public string additionalAuthenticationUriQuery = ""; | ||||
| } | ||||
| 
 | ||||
| /// <summary> | ||||
|  |  | |||
|  | @ -46,8 +46,8 @@ public class UniWebViewAuthenticationFlowTwitter : UniWebViewAuthenticationCommo | |||
|     /// </summary> | ||||
|     public UniWebViewAuthenticationFlowTwitterOptional optional; | ||||
|      | ||||
|     private string responseType = "code"; | ||||
|     private string grantType = "authorization_code"; | ||||
|     private const string responseType = "code"; | ||||
|     private const string grantType = "authorization_code"; | ||||
|      | ||||
|     private readonly UniWebViewAuthenticationConfiguration config =  | ||||
|         new UniWebViewAuthenticationConfiguration( | ||||
|  | @ -63,7 +63,17 @@ public class UniWebViewAuthenticationFlowTwitter : UniWebViewAuthenticationCommo | |||
|         var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationTwitterToken>(this); | ||||
|         flow.StartAuth(); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Starts the refresh flow with the standard OAuth 2.0. | ||||
|     /// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`. | ||||
|     /// </summary> | ||||
|     /// <param name="refreshToken">The refresh token received with a previous access token response.</param> | ||||
|     public override void StartRefreshTokenFlow(string refreshToken) { | ||||
|         var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationTwitterToken>(this); | ||||
|         flow.RefreshToken(refreshToken); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|  | @ -105,12 +115,16 @@ public class UniWebViewAuthenticationFlowTwitter : UniWebViewAuthenticationCommo | |||
| 
 | ||||
|         return authorizeArgs; | ||||
|     } | ||||
|      | ||||
|     public string GetAdditionalAuthenticationUriQuery() { | ||||
|         return optional.additionalAuthenticationUriQuery; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) { | ||||
|         if (!authResponse.StartsWith(redirectUri)) { | ||||
|         if (!authResponse.StartsWith(redirectUri, StringComparison.InvariantCultureIgnoreCase)) { | ||||
|             throw AuthenticationResponseException.UnexpectedAuthCallbackUrl; | ||||
|         } | ||||
|          | ||||
|  | @ -134,6 +148,17 @@ public class UniWebViewAuthenticationFlowTwitter : UniWebViewAuthenticationCommo | |||
| 
 | ||||
|         return parameters; | ||||
|     } | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     public Dictionary<string, string> GetRefreshTokenRequestParameters(string refreshToken) { | ||||
|         return new Dictionary<string, string> { | ||||
|             { "client_id", clientId },  | ||||
|             { "refresh_token", refreshToken }, | ||||
|             { "grant_type", "refresh_token" } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|  | @ -142,10 +167,29 @@ public class UniWebViewAuthenticationFlowTwitter : UniWebViewAuthenticationCommo | |||
|         return UniWebViewAuthenticationTokenFactory<UniWebViewAuthenticationTwitterToken>.Parse(exchangeResponse); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<UniWebViewAuthenticationTwitterToken> OnAuthenticationFinished { get; set; } | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<long, string> OnAuthenticationErrored { get; set; } | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<UniWebViewAuthenticationTwitterToken> OnRefreshTokenFinished { get; set; } | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Implements required method in `IUniWebViewAuthenticationFlow`. | ||||
|     /// </summary> | ||||
|     [field: SerializeField] | ||||
|     public UnityEvent<long, string> OnRefreshTokenErrored { get; set; } | ||||
| } | ||||
| 
 | ||||
| /// <summary> | ||||
|  | @ -163,6 +207,16 @@ public class UniWebViewAuthenticationFlowTwitterOptional { | |||
|     /// authentication callback. This has to be `true`, otherwise, Twitter will reject the authentication request. | ||||
|     /// </summary> | ||||
|     public bool enableState = true; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The additional query arguments that are used to construct the query string of the authentication request. | ||||
|     ///  | ||||
|     /// This is useful when you want to add some custom parameters to the authentication request. This string will be  | ||||
|     /// appended to the query string that constructed from `GetAuthenticationUriArguments`.  | ||||
|     ///  | ||||
|     /// For example, if you set `prompt=consent&ui_locales=en`, it will be contained in the final authentication query. | ||||
|     /// </summary> | ||||
|     public string additionalAuthenticationUriQuery = ""; | ||||
| } | ||||
| 
 | ||||
| /// <summary> | ||||
|  |  | |||
|  | @ -71,7 +71,7 @@ public class UniWebViewAuthenticationSession: UnityEngine.Object { | |||
|   /// </summary> | ||||
|   public event AuthErrorReceivedDelegate OnAuthenticationErrorReceived; | ||||
| 
 | ||||
|   private string id = Guid.NewGuid().ToString(); | ||||
|   private readonly string id = Guid.NewGuid().ToString(); | ||||
|   private UniWebViewNativeListener listener; | ||||
|    | ||||
|   /// <summary> | ||||
|  | @ -134,11 +134,7 @@ public class UniWebViewAuthenticationSession: UnityEngine.Object { | |||
|   /// Returns `true` if the safe browsing mode is supported and the page will be opened in safe browsing  | ||||
|   /// mode. Otherwise, `false`. | ||||
|   /// </returns> | ||||
|   public static bool IsAuthenticationSupported {  | ||||
|     get { | ||||
|       return UniWebViewInterface.IsAuthenticationIsSupported();  | ||||
|     } | ||||
|   } | ||||
|   public static bool IsAuthenticationSupported => UniWebViewInterface.IsAuthenticationIsSupported(); | ||||
| 
 | ||||
|   /// <summary> | ||||
|   /// Creates a new authentication session with a given authentication page URL and a callback scheme. | ||||
|  | @ -167,6 +163,17 @@ public class UniWebViewAuthenticationSession: UnityEngine.Object { | |||
|     UniWebViewInterface.AuthenticationStart(listener.Name); | ||||
|   } | ||||
| 
 | ||||
|   /// <summary> | ||||
|   /// Cancels the authentication session. | ||||
|   ///  | ||||
|   /// This method is only available on iOS. | ||||
|   /// </summary> | ||||
|   internal void Cancel() { | ||||
| #if UNITY_IOS && !UNITY_EDITOR | ||||
|     UniWebViewInterface.AuthenticationCancel(listener.Name); | ||||
| #endif | ||||
|   } | ||||
| 
 | ||||
|   /// <summary> | ||||
|   /// Sets to use the private mode for the authentication. If running under private mode, the previous stored | ||||
|   /// authentication information will not be used. | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ using System; | |||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Security.Cryptography; | ||||
| using System.Text; | ||||
| using UnityEngine.Networking; | ||||
| 
 | ||||
| /// <summary> | ||||
|  | @ -83,7 +84,7 @@ public class UniWebViewAuthenticationUtils { | |||
|     /// <param name="input">The Base64URL encoded string.</param> | ||||
|     /// <returns>A string with Base64 encoded for the input.</returns> | ||||
|     public static string ConvertToBase64String(string input) { | ||||
|         string result = input.Replace('_', '/').Replace('-', '+'); | ||||
|         var result = input.Replace('_', '/').Replace('-', '+'); | ||||
|         switch (input.Length % 4) { | ||||
|             case 2: | ||||
|                 result += "=="; | ||||
|  | @ -103,14 +104,13 @@ public class UniWebViewAuthenticationUtils { | |||
|     /// <returns>A generated code verifier for PKCE usage.</returns> | ||||
|     public static string GenerateCodeVerifier(int length = 64) { | ||||
|         var randomNumber = new byte[32]; | ||||
|         string value = ""; | ||||
|         string value; | ||||
| 
 | ||||
|         using (var rng = RandomNumberGenerator.Create()) { | ||||
|             rng.GetBytes(randomNumber); | ||||
|             String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; | ||||
|             Random random = new Random(System.BitConverter.ToInt32(randomNumber, 0)); | ||||
|             value = new String(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray()); | ||||
|         } | ||||
|         using var rng = RandomNumberGenerator.Create(); | ||||
|         rng.GetBytes(randomNumber); | ||||
|         const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; | ||||
|         var random = new Random(System.BitConverter.ToInt32(randomNumber, 0)); | ||||
|         value = new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray()); | ||||
| 
 | ||||
|         return value; | ||||
|     } | ||||
|  | @ -157,7 +157,6 @@ public class UniWebViewAuthenticationUtils { | |||
| 
 | ||||
|         var host = uri.Host; | ||||
|         string scheme = null; | ||||
|         var fragmentPairs = new Dictionary<string, string>(); | ||||
|         var fragments = uri.Fragment; | ||||
|         fragments.Split(';').ToList().ForEach(fragment => { | ||||
|             var kv = fragment.Split('='); | ||||
|  | @ -172,6 +171,28 @@ public class UniWebViewAuthenticationUtils { | |||
| 
 | ||||
|         return input; | ||||
|     } | ||||
| 
 | ||||
|     public static string CreateQueryString(Dictionary<string, string> collection, string additionalQuery = "") { | ||||
|         int count = collection.Count; | ||||
|         if (count == 0) { | ||||
|             return additionalQuery ?? "";             | ||||
|         } | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         string [] keys = collection.Keys.ToArray(); | ||||
|         for (int i = 0; i < count; i++) { | ||||
|             sb.AppendFormat ("{0}={1}&", keys[i], UnityWebRequest.EscapeURL(collection[keys[i]])); | ||||
|         } | ||||
| 
 | ||||
|         if (sb.Length > 0) { | ||||
|             if (string.IsNullOrEmpty(additionalQuery)) { | ||||
|                 sb.Length--; | ||||
|             } else { | ||||
|                 sb.AppendFormat("{0}", additionalQuery); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return sb.ToString(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| public enum UniWebViewAuthenticationPKCE | ||||
|  |  | |||
|  | @ -0,0 +1,45 @@ | |||
| // | ||||
| //  UniWebViewCacheMode.cs | ||||
| //  Created by Wang Wei(@onevcat) on 2024-01-17. | ||||
| // | ||||
| //  This file is a part of UniWebView Project (https://uniwebview.com) | ||||
| //  By purchasing the asset, you are allowed to use this code in as many as projects  | ||||
| //  you want, only if you publish the final products under the name of the same account | ||||
| //  used for the purchase.  | ||||
| // | ||||
| //  This asset and all corresponding files (such as source code) are provided on an  | ||||
| //  “as is” basis, without warranty of any kind, express of implied, including but not | ||||
| //  limited to the warranties of merchantability, fitness for a particular purpose, and  | ||||
| //  noninfringement. In no event shall the authors or copyright holders be liable for any  | ||||
| //  claim, damages or other liability, whether in action of contract, tort or otherwise,  | ||||
| //  arising from, out of or in connection with the software or the use of other dealing in the software. | ||||
| 
 | ||||
| /// <summary> | ||||
| /// Defines the cache mode for UniWebView. | ||||
| /// </summary> | ||||
| public enum UniWebViewCacheMode | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Default mode. The web view will check the validity of the cache copy when there is one. If the copy is invalid, | ||||
|     /// the web view will load from the network. This is the default setting. | ||||
|     /// </summary> | ||||
|     Default = 0, | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// No cache is used. All pages are loaded directly from the network. This is useful for applications that do not | ||||
|     /// want to have a cache. | ||||
|     /// </summary> | ||||
|     NoCache = 1, | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Prioritize the cache. If there is a copy of the page in the cache, the web view will use it even if the copy | ||||
|     /// has expired. The web view will only load from the network when the page does not exist in the cache. | ||||
|     /// </summary> | ||||
|     CacheElseLoad = 2, | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Only use the cache. In this mode, the web view will not load pages from the network, only use the content in | ||||
|     /// the cache. If the requested URL is not in the cache, an error is returned. | ||||
|     /// </summary> | ||||
|     CacheOnly = 3, | ||||
| } | ||||
|  | @ -0,0 +1,3 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: af9091341ad1451a88060e20c2dc953f | ||||
| timeCreated: 1705494877 | ||||
|  | @ -0,0 +1,3 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 983375010eb44a88b27df35ee1eedc4f | ||||
| timeCreated: 1682064507 | ||||
|  | @ -0,0 +1,33 @@ | |||
| // | ||||
| //  UniWebViewChannelMethodHandleRequest.cs | ||||
| //  Created by Wang Wei(@onevcat) on 2023-04-21. | ||||
| // | ||||
| //  This file is a part of UniWebView Project (https://uniwebview.com) | ||||
| //  By purchasing the asset, you are allowed to use this code in as many as projects  | ||||
| //  you want, only if you publish the final products under the name of the same account | ||||
| //  used for the purchase.  | ||||
| // | ||||
| //  This asset and all corresponding files (such as source code) are provided on an  | ||||
| //  “as is” basis, without warranty of any kind, express of implied, including but not | ||||
| //  limited to the warranties of merchantability, fitness for a particular purpose, and  | ||||
| //  noninfringement. In no event shall the authors or copyright holders be liable for any  | ||||
| //  claim, damages or other liability, whether in action of contract, tort or otherwise,  | ||||
| //  arising from, out of or in connection with the software or the use of other dealing in the software. | ||||
| // | ||||
| 
 | ||||
| using System; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// Represents the request of a loading used in request handler.   | ||||
| /// </summary> | ||||
| [Serializable] | ||||
| public class UniWebViewChannelMethodHandleRequest { | ||||
|     [SerializeField] | ||||
|     private string url; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The URL of the request. | ||||
|     /// </summary> | ||||
|     public string Url => url; | ||||
| } | ||||
|  | @ -0,0 +1,3 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: e1fb4e291b944fa8b8a114f076f06da9 | ||||
| timeCreated: 1682064771 | ||||
|  | @ -0,0 +1,144 @@ | |||
| // | ||||
| //  UniWebViewChannelMethodManager.cs | ||||
| //  Created by Wang Wei(@onevcat) on 2023-04-21. | ||||
| // | ||||
| //  This file is a part of UniWebView Project (https://uniwebview.com) | ||||
| //  By purchasing the asset, you are allowed to use this code in as many as projects  | ||||
| //  you want, only if you publish the final products under the name of the same account | ||||
| //  used for the purchase.  | ||||
| // | ||||
| //  This asset and all corresponding files (such as source code) are provided on an  | ||||
| //  “as is” basis, without warranty of any kind, express of implied, including but not | ||||
| //  limited to the warranties of merchantability, fitness for a particular purpose, and  | ||||
| //  noninfringement. In no event shall the authors or copyright holders be liable for any  | ||||
| //  claim, damages or other liability, whether in action of contract, tort or otherwise,  | ||||
| //  arising from, out of or in connection with the software or the use of other dealing in the software. | ||||
| // | ||||
| 
 | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| enum UniWebViewChannelMethod { | ||||
|     ShouldUniWebViewHandleRequest, | ||||
|     RequestMediaCapturePermission | ||||
| } | ||||
| 
 | ||||
| class UniWebViewChannelMethodManager { | ||||
|      | ||||
|     private static UniWebViewChannelMethodManager instance; | ||||
|     private Dictionary<string, Dictionary<string, Func<object, object>>> channels =  | ||||
|         new Dictionary<string, Dictionary<string, Func<object, object>>>(); | ||||
| 
 | ||||
|     internal static UniWebViewChannelMethodManager Instance { | ||||
|         get { | ||||
|             if (instance == null) { | ||||
|                 instance = new UniWebViewChannelMethodManager(); | ||||
|             } | ||||
|             return instance; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     internal void RegisterChannelMethod( | ||||
|         string webViewName,  | ||||
|         UniWebViewChannelMethod method,  | ||||
|         Func<object, object> handler) | ||||
|     { | ||||
|         if (!HasRegisteredChannel(webViewName)) { | ||||
|             channels[webViewName] = new Dictionary<string, Func<object, object>>(); | ||||
|         } | ||||
|         var methodName = method.ToString(); | ||||
|         channels[webViewName][methodName] = handler; | ||||
|         UniWebViewLogger.Instance.Info("Channel method is registered for web view: " + webViewName + ", method: " + | ||||
|                                        methodName); | ||||
|     } | ||||
|      | ||||
|     internal void UnregisterChannel(string webViewName) { | ||||
|         if (!HasRegisteredChannel(webViewName)) { | ||||
|             return; | ||||
|         } | ||||
|         channels.Remove(webViewName); | ||||
|         UniWebViewLogger.Instance.Debug("All channel methods are unregistered for web view: " + webViewName); | ||||
|     } | ||||
|      | ||||
|     internal void UnregisterChannelMethod(string webViewName, UniWebViewChannelMethod method) { | ||||
|         var methodName = method.ToString(); | ||||
|         if (!HasRegisteredChannel(webViewName)) { | ||||
|             return; | ||||
|         } | ||||
|         channels[webViewName].Remove(methodName); | ||||
|         UniWebViewLogger.Instance.Debug("Channel method is unregistered for web view: " + webViewName + ", method: " + | ||||
|                                        methodName); | ||||
|     } | ||||
| 
 | ||||
|     bool HasRegisteredChannel(string webViewName) { | ||||
|         return channels.Keys.Contains(webViewName); | ||||
|     } | ||||
| 
 | ||||
|     bool HasRegisteredMethod(string webViewName, string methodName) { | ||||
|         if (!HasRegisteredChannel(webViewName)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return channels[webViewName].Keys.Contains(methodName); | ||||
|     } | ||||
| 
 | ||||
|     internal string InvokeMethod(string webViewName, string methodName, string parameters) { | ||||
|         if (!HasRegisteredMethod(webViewName, methodName)) { | ||||
|             UniWebViewLogger.Instance.Info("There is no handler for the channel method. Ignoring."); | ||||
|             return null; | ||||
|         } | ||||
|         var func = channels[webViewName][methodName]; | ||||
| 
 | ||||
|         if (!Enum.TryParse<UniWebViewChannelMethod>(methodName, out var method)) { | ||||
|             UniWebViewLogger.Instance.Info("Unknown method name: " + methodName + ". Please check, ignoring."); | ||||
|             return null; | ||||
|         } | ||||
|          | ||||
|         UniWebViewLogger.Instance.Verbose("Channel method invoking received for web view: " + webViewName + ", method: " + | ||||
|                                           methodName + ", parameters: " + parameters); | ||||
|         string result; | ||||
|         switch (method) { | ||||
|             case UniWebViewChannelMethod.ShouldUniWebViewHandleRequest: { | ||||
|                 // (UniWebViewChannelMethodHandleRequest) -> bool | ||||
|                 var input = JsonUtility.FromJson<UniWebViewChannelMethodHandleRequest>(parameters); | ||||
|                 bool Func(UniWebViewChannelMethodHandleRequest i) => (bool)func(i); | ||||
|                 result = ResultJsonWith(Func(input)); | ||||
|                 break; | ||||
|             } | ||||
|             case UniWebViewChannelMethod.RequestMediaCapturePermission: { | ||||
|                 // (UniWebViewChannelMethodMediaCapturePermission) -> UniWebViewMediaCapturePermissionDecision | ||||
|                 var input = JsonUtility.FromJson<UniWebViewChannelMethodMediaCapturePermission>(parameters); | ||||
|                 UniWebViewMediaCapturePermissionDecision Func(UniWebViewChannelMethodMediaCapturePermission i) => (UniWebViewMediaCapturePermissionDecision)func(i); | ||||
|                 result = ResultJsonWith(Func(input)); | ||||
|                 break; | ||||
|             } | ||||
|             default: | ||||
|                 result = null; | ||||
|                 break; | ||||
|         } | ||||
|         UniWebViewLogger.Instance.Debug("Channel method handler responded. Result: " + result); | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     string ResultJsonWith(bool value) { | ||||
|         return value ? "{\"result\":true}" : "{\"result\":false}"; | ||||
|     } | ||||
|      | ||||
|     string ResultJsonWith(UniWebViewMediaCapturePermissionDecision decision) { | ||||
|         switch (decision) { | ||||
|             case UniWebViewMediaCapturePermissionDecision.Prompt: | ||||
|                 return "{\"result\":\"prompt\"}"; | ||||
|             case UniWebViewMediaCapturePermissionDecision.Grant: | ||||
|                 return "{\"result\":\"grant\"}"; | ||||
|             case UniWebViewMediaCapturePermissionDecision.Deny: | ||||
|                 return "{\"result\":\"deny\"}"; | ||||
|             default: | ||||
|                 UniWebViewLogger.Instance.Critical("Unknown decision: " + decision); | ||||
|                 break; | ||||
|         } | ||||
|         Debug.LogAssertion("Unrecognized UniWebViewMediaCapturePermissionDecision. Fallback to prompt."); | ||||
|         return "{\"result\":\"prompt\"}"; | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,3 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: f9415d08641d44ec97fdccd2799861e1 | ||||
| timeCreated: 1682063884 | ||||
|  | @ -0,0 +1,69 @@ | |||
| // | ||||
| //  UniWebViewChannelMethodMediaCapturePermission.cs | ||||
| //  Created by Wang Wei(@onevcat) on 2024-02-20. | ||||
| // | ||||
| //  This file is a part of UniWebView Project (https://uniwebview.com) | ||||
| //  By purchasing the asset, you are allowed to use this code in as many as projects  | ||||
| //  you want, only if you publish the final products under the name of the same account | ||||
| //  used for the purchase.  | ||||
| // | ||||
| //  This asset and all corresponding files (such as source code) are provided on an  | ||||
| //  “as is” basis, without warranty of any kind, express of implied, including but not | ||||
| //  limited to the warranties of merchantability, fitness for a particular purpose, and  | ||||
| //  noninfringement. In no event shall the authors or copyright holders be liable for any  | ||||
| //  claim, damages or other liability, whether in action of contract, tort or otherwise,  | ||||
| //  arising from, out of or in connection with the software or the use of other dealing in the software. | ||||
| // | ||||
| 
 | ||||
| using System; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// Represents the request of media capture permission. | ||||
| /// | ||||
| /// This class represents the request of media capture permission. When the web page requests the permission to access | ||||
| /// the device's hardware, such as the camera or microphone, UniWebView will ask you to handle the request with a value | ||||
| /// of this class. You can use the values in this class to determine the decision for the permission request. | ||||
| /// </summary> | ||||
| [Serializable] | ||||
| public class UniWebViewChannelMethodMediaCapturePermission { | ||||
|      | ||||
|     [SerializeField] | ||||
|     private string protocol; | ||||
| 
 | ||||
|     [SerializeField]  | ||||
|     private string host; | ||||
|      | ||||
|     [SerializeField] | ||||
|     private int port; | ||||
|      | ||||
|     [SerializeField]  | ||||
|     private string[] resources; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The protocol used by the permission request. Such as "https" or "http". | ||||
|     /// </summary> | ||||
|     public string Protocol => protocol; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The host of the origin of the permission request. | ||||
|     /// | ||||
|     /// It is usually the domain of the web page. | ||||
|     /// </summary> | ||||
|     public string Host => host; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The port of the origin of the permission request. | ||||
|     /// | ||||
|     /// If not existing in the request URL, it is -1. | ||||
|     /// </summary> | ||||
|     public int Port => port; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The string representation of the resources of the origin of the permission request. | ||||
|     /// | ||||
|     /// An array contains requested resources, the most common values are "VIDEO", "AUDIO". On Android, some pages can | ||||
|     /// also requests for "PROTECTED_MEDIA_ID", "MIDI_SYSEX", etc.  | ||||
|     /// </summary> | ||||
|     public string[] Resources => resources; | ||||
| } | ||||
|  | @ -0,0 +1,3 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 31347bc762ad46fda0634683da0201ac | ||||
| timeCreated: 1708439899 | ||||
|  | @ -0,0 +1,42 @@ | |||
| // | ||||
| //  UniWebViewMediaCapturePermissionDecision.cs | ||||
| //  Created by Wang Wei(@onevcat) on 2024-02-20. | ||||
| // | ||||
| //  This file is a part of UniWebView Project (https://uniwebview.com) | ||||
| //  By purchasing the asset, you are allowed to use this code in as many as projects  | ||||
| //  you want, only if you publish the final products under the name of the same account | ||||
| //  used for the purchase.  | ||||
| // | ||||
| //  This asset and all corresponding files (such as source code) are provided on an  | ||||
| //  “as is” basis, without warranty of any kind, express of implied, including but not | ||||
| //  limited to the warranties of merchantability, fitness for a particular purpose, and  | ||||
| //  noninfringement. In no event shall the authors or copyright holders be liable for any  | ||||
| //  claim, damages or other liability, whether in action of contract, tort or otherwise,  | ||||
| //  arising from, out of or in connection with the software or the use of other dealing in the software. | ||||
| // | ||||
| 
 | ||||
| /// <summary> | ||||
| /// Represents the decision of media capture permission request. | ||||
| /// | ||||
| /// Use value in this enum to guide how UniWebView should handle the media capture permission request. | ||||
| /// </summary> | ||||
| public enum UniWebViewMediaCapturePermissionDecision { | ||||
|     /// <summary> | ||||
|     /// Display a prompt to ask user for the permission. | ||||
|     /// | ||||
|     /// The prompt alert shows the origin of the request and the resources requested. It asks user to grant or deny the | ||||
|     /// permission. | ||||
|     /// </summary> | ||||
|     Prompt, | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Grant the permission request without asking user. | ||||
|     /// </summary> | ||||
|     Grant, | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Deny the permission request. The web page will receive an error and it knows the request resources are not | ||||
|     /// allowed to use. | ||||
|     /// </summary> | ||||
|     Deny | ||||
| } | ||||
|  | @ -0,0 +1,3 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 325ac2081331446986f826bce9a5d62e | ||||
| timeCreated: 1708440272 | ||||
|  | @ -100,8 +100,9 @@ public class UniWebViewEmbeddedToolbar { | |||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Hides the navigation buttons on the toolbar. When called, the back button and forward button will not be shown. | ||||
|     /// | ||||
|     /// Hides the navigation buttons on the toolbar.  | ||||
|     ///  | ||||
|     /// When called, the back button and forward button will not be shown. | ||||
|     /// By default, the navigation buttons are shown. | ||||
|     /// </summary> | ||||
|     public void HideNavigationButtons() { | ||||
|  | @ -109,8 +110,9 @@ public class UniWebViewEmbeddedToolbar { | |||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Shows the navigation buttons on the toolbar. When called, the back button and forward button will be shown. | ||||
|     /// Shows the navigation buttons on the toolbar.  | ||||
|     ///  | ||||
|     /// When called, the back button and forward button will be shown. | ||||
|     /// By default, the navigation buttons are shown. | ||||
|     /// </summary> | ||||
|     public void ShowNavigationButtons() { | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 824ddd9d1592945268d857265662a174 | ||||
| timeCreated: 1495373327 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|  |  | |||
|  | @ -58,7 +58,7 @@ public class UniWebViewLogger { | |||
|     /// Default is `Critical`, which means the logger only prints errors and exceptions. | ||||
|     /// </summary> | ||||
|     public Level LogLevel { | ||||
|         get { return level; } | ||||
|         get => level; | ||||
|         set { | ||||
|             Log(Level.Off, "Setting UniWebView logger level to: " + value); | ||||
|             level = value; | ||||
|  | @ -107,10 +107,11 @@ public class UniWebViewLogger { | |||
|     /// <param name="message">The message to log.</param> | ||||
|     public void Critical(string message) { Log(Level.Critical, message); } | ||||
| 
 | ||||
|     private void Log(Level level, string message) { | ||||
|         if (level >= this.LogLevel) { | ||||
|     // ReSharper disable Unity.PerformanceAnalysis | ||||
|     private void Log(Level targetLevel, string message) { | ||||
|         if (targetLevel >= this.LogLevel) { | ||||
|             var logMessage = "<UniWebView> " + message; | ||||
|             if (level == Level.Critical) { | ||||
|             if (targetLevel == Level.Critical) { | ||||
|                 UnityEngine.Debug.LogError(logMessage); | ||||
|             } else { | ||||
|                 UnityEngine.Debug.Log(logMessage); | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 06ca7a8564d9842ba99c77d43e9ce4f5 | ||||
| timeCreated: 1491898971 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|  |  | |||
|  | @ -14,15 +14,9 @@ | |||
| //  claim, damages or other liability, whether in action of contract, tort or otherwise,  | ||||
| //  arising from, out of or in connection with the software or the use of other dealing in the software. | ||||
| // | ||||
| using System.Collections.Generic; | ||||
| using UnityEngine; | ||||
| using UnityEngine.Networking; | ||||
| 
 | ||||
| #if UNITY_2017_3_OR_NEWER | ||||
| using System.Collections.Generic; | ||||
| using Net = UnityEngine.Networking.UnityWebRequest; | ||||
| #else | ||||
| using Net = UnityEngine.WWW; | ||||
| #endif | ||||
| 
 | ||||
| /// <summary> | ||||
| /// A structure represents a message from webview. | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 0dbdb1ed01b9747a1ad6eb7bcc7b4014 | ||||
| timeCreated: 1491898971 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|  |  | |||
|  | @ -15,9 +15,9 @@ | |||
| //  arising from, out of or in connection with the software or the use of other dealing in the software. | ||||
| // | ||||
| using UnityEngine; | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using System; | ||||
| using UniWebViewExternal; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// A listener script for message sent from native side of UniWebView. | ||||
|  | @ -66,11 +66,7 @@ public class UniWebViewNativeListener: MonoBehaviour { | |||
|     /// Name of current listener. This is a UUID string by which native side could use to find  | ||||
|     /// the message destination. | ||||
|     /// </summary> | ||||
|     public string Name { | ||||
|         get { | ||||
|             return gameObject.name; | ||||
|         } | ||||
|     } | ||||
|     public string Name => gameObject.name; | ||||
| 
 | ||||
|     public void PageStarted(string url) { | ||||
|         UniWebViewLogger.Instance.Info("Page Started Event. Url: " + url); | ||||
|  | @ -185,6 +181,11 @@ public class UniWebViewNativeListener: MonoBehaviour { | |||
|         var payload = JsonUtility.FromJson<UniWebViewNativeResultPayload>(result); | ||||
|         session.InternalAuthenticationErrorReceived(payload); | ||||
|     } | ||||
| 
 | ||||
|     public void SnapshotRenderingStarted(string identifier) { | ||||
|         UniWebViewLogger.Instance.Info("Snapshot Rendering Started Event. Identifier: " + identifier); | ||||
|         webView.InternalOnSnapshotRenderingStarted(identifier); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// <summary> | ||||
|  | @ -193,6 +194,12 @@ public class UniWebViewNativeListener: MonoBehaviour { | |||
| /// </summary> | ||||
| [System.Serializable] | ||||
| public class UniWebViewNativeResultPayload { | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// The key in `Extra` dictionary which contains the failing URL, if available. | ||||
|     /// </summary> | ||||
|     public const string ExtraFailingURLKey = "failingURL"; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The identifier bound to this payload. It would be used internally to identify the callback. | ||||
|     /// </summary> | ||||
|  | @ -205,6 +212,28 @@ public class UniWebViewNativeResultPayload { | |||
|     /// <summary> | ||||
|     /// Return value or data from native. You should look at  | ||||
|     /// corresponding APIs to know what exactly contained in this. | ||||
|     /// | ||||
|     /// Usually it is a string value represents the reason or a small piece of data related to the result.  | ||||
|     /// </summary> | ||||
|     public string data; | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// The extra data from native side. It is a JSON string and could be parsed to a dictionary. | ||||
|     /// | ||||
|     /// Usually you do not access to this value directly. Instead, use `Extra` property to get the parsed dictionary. | ||||
|     /// </summary> | ||||
|     public string extra; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// The extra data from native side, in dictionary format. If there is no extra data provided, this will be null. | ||||
|     /// Otherwise, it contains values passed from native side. | ||||
|     /// </summary> | ||||
|     public Dictionary<string, object> Extra { | ||||
|         get { | ||||
|             if (String.IsNullOrEmpty(extra)) { | ||||
|                 return null; | ||||
|             } | ||||
|             return Json.Deserialize(extra) as Dictionary<string, object>; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 5538b25d1713f4de994dacdc6eaacb95 | ||||
| timeCreated: 1491961733 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|  |  | |||
|  | @ -0,0 +1,27 @@ | |||
| using UnityEngine; | ||||
| 
 | ||||
| public class UniWebViewStaticListener { | ||||
|     public static void DebugLog(string value) { | ||||
|         var payload = JsonUtility.FromJson<UniWebViewNativeResultPayload>(value); | ||||
|         switch (payload.resultCode) { | ||||
|             case "0": | ||||
|                 Debug.Log(payload.data); | ||||
|                 break; | ||||
|             case "1": | ||||
|                 Debug.Log(payload.data); | ||||
|                 break; | ||||
|             case "2": | ||||
|                 Debug.LogWarning(payload.data); | ||||
|                 break; | ||||
|             case "3": | ||||
|                 Debug.LogError(payload.data); | ||||
|                 break; | ||||
|             case "4": | ||||
|                 Debug.LogError(payload.data); | ||||
|                 break; | ||||
|             default: | ||||
|                 Debug.Log(payload.data); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,3 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 17a82aa0398d47948eaf456b1c3f4716 | ||||
| timeCreated: 1696325753 | ||||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 9e4c706c92e5e4dc381385bf4edbd3dd | ||||
| timeCreated: 1496199547 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 1256e7eea8f184fae9b700f8f78e014c | ||||
| timeCreated: 1493888384 | ||||
| licenseType: Free | ||||
| licenseType: Store | ||||
| MonoImporter: | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|  |  | |||
|  | @ -1,7 +1,11 @@ | |||
| { | ||||
|     "name": "UniWebView-CSharp", | ||||
|     "rootNamespace": "", | ||||
|     "references": ["GUID:343deaaf83e0cee4ca978e7df0b80d21","GUID:2bafac87e7f4b9b418d9448d219b01ab"], | ||||
|     "references": [ | ||||
|         "GUID:343deaaf83e0cee4ca978e7df0b80d21", | ||||
|         "GUID:2bafac87e7f4b9b418d9448d219b01ab", | ||||
|         "GUID:75469ad4d38634e559750d17036d5f7c" | ||||
|     ], | ||||
|     "includePlatforms": [], | ||||
|     "excludePlatforms": [], | ||||
|     "allowUnsafeCode": false, | ||||
|  |  | |||
|  | @ -1 +0,0 @@ | |||
| -r:System.Web.dll | ||||
|  | @ -1,7 +0,0 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 81b32579db9e845999209664ba8e2ec3 | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
		Loading…
	
		Reference in New Issue