@@ -0,0 +1,132 @@ | |||||
fileFormatVersion: 2 | |||||
guid: b7972444e74a3874296bed0afbef3b37 | |||||
TextureImporter: | |||||
internalIDToNameTable: [] | |||||
externalObjects: {} | |||||
serializedVersion: 11 | |||||
mipmaps: | |||||
mipMapMode: 0 | |||||
enableMipMap: 0 | |||||
sRGBTexture: 1 | |||||
linearTexture: 0 | |||||
fadeOut: 0 | |||||
borderMipMap: 0 | |||||
mipMapsPreserveCoverage: 0 | |||||
alphaTestReferenceValue: 0.5 | |||||
mipMapFadeDistanceStart: 1 | |||||
mipMapFadeDistanceEnd: 3 | |||||
bumpmap: | |||||
convertToNormalMap: 0 | |||||
externalNormalMap: 0 | |||||
heightScale: 0.25 | |||||
normalMapFilter: 0 | |||||
isReadable: 0 | |||||
streamingMipmaps: 0 | |||||
streamingMipmapsPriority: 0 | |||||
vTOnly: 0 | |||||
grayScaleToAlpha: 0 | |||||
generateCubemap: 6 | |||||
cubemapConvolution: 0 | |||||
seamlessCubemap: 0 | |||||
textureFormat: 1 | |||||
maxTextureSize: 2048 | |||||
textureSettings: | |||||
serializedVersion: 2 | |||||
filterMode: 1 | |||||
aniso: 1 | |||||
mipBias: 0 | |||||
wrapU: 1 | |||||
wrapV: 1 | |||||
wrapW: 0 | |||||
nPOTScale: 0 | |||||
lightmap: 0 | |||||
compressionQuality: 50 | |||||
spriteMode: 1 | |||||
spriteExtrude: 1 | |||||
spriteMeshType: 1 | |||||
alignment: 0 | |||||
spritePivot: {x: 0.5, y: 0.5} | |||||
spritePixelsToUnits: 100 | |||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0} | |||||
spriteGenerateFallbackPhysicsShape: 1 | |||||
alphaUsage: 1 | |||||
alphaIsTransparency: 1 | |||||
spriteTessellationDetail: -1 | |||||
textureType: 8 | |||||
textureShape: 1 | |||||
singleChannelComponent: 0 | |||||
flipbookRows: 1 | |||||
flipbookColumns: 1 | |||||
maxTextureSizeSet: 0 | |||||
compressionQualitySet: 0 | |||||
textureFormatSet: 0 | |||||
ignorePngGamma: 0 | |||||
applyGammaDecoding: 0 | |||||
platformSettings: | |||||
- serializedVersion: 3 | |||||
buildTarget: DefaultTexturePlatform | |||||
maxTextureSize: 2048 | |||||
resizeAlgorithm: 0 | |||||
textureFormat: -1 | |||||
textureCompression: 1 | |||||
compressionQuality: 50 | |||||
crunchedCompression: 0 | |||||
allowsAlphaSplitting: 0 | |||||
overridden: 0 | |||||
androidETC2FallbackOverride: 0 | |||||
forceMaximumCompressionQuality_BC6H_BC7: 0 | |||||
- serializedVersion: 3 | |||||
buildTarget: Standalone | |||||
maxTextureSize: 2048 | |||||
resizeAlgorithm: 0 | |||||
textureFormat: -1 | |||||
textureCompression: 1 | |||||
compressionQuality: 50 | |||||
crunchedCompression: 0 | |||||
allowsAlphaSplitting: 0 | |||||
overridden: 0 | |||||
androidETC2FallbackOverride: 0 | |||||
forceMaximumCompressionQuality_BC6H_BC7: 0 | |||||
- serializedVersion: 3 | |||||
buildTarget: iPhone | |||||
maxTextureSize: 2048 | |||||
resizeAlgorithm: 0 | |||||
textureFormat: -1 | |||||
textureCompression: 1 | |||||
compressionQuality: 50 | |||||
crunchedCompression: 0 | |||||
allowsAlphaSplitting: 0 | |||||
overridden: 0 | |||||
androidETC2FallbackOverride: 0 | |||||
forceMaximumCompressionQuality_BC6H_BC7: 0 | |||||
- serializedVersion: 3 | |||||
buildTarget: Android | |||||
maxTextureSize: 2048 | |||||
resizeAlgorithm: 0 | |||||
textureFormat: -1 | |||||
textureCompression: 1 | |||||
compressionQuality: 50 | |||||
crunchedCompression: 0 | |||||
allowsAlphaSplitting: 0 | |||||
overridden: 0 | |||||
androidETC2FallbackOverride: 0 | |||||
forceMaximumCompressionQuality_BC6H_BC7: 0 | |||||
spriteSheet: | |||||
serializedVersion: 2 | |||||
sprites: [] | |||||
outline: [] | |||||
physicsShape: [] | |||||
bones: [] | |||||
spriteID: 5e97eb03825dee720800000000000000 | |||||
internalID: 0 | |||||
vertices: [] | |||||
indices: | |||||
edges: [] | |||||
weights: [] | |||||
secondaryTextures: [] | |||||
spritePackingTag: | |||||
pSDRemoveMatte: 0 | |||||
pSDShowRemoveMatteOption: 0 | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 28c55975e69947e48a9407bb833a7ac9 | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: a81820dc80e59154c961c01c6d42781e | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,26 @@ | |||||
%YAML 1.1 | |||||
%TAG !u! tag:unity3d.com,2011: | |||||
--- !u!114 &11400000 | |||||
MonoBehaviour: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 0} | |||||
m_Enabled: 1 | |||||
m_EditorHideFlags: 0 | |||||
m_Script: {fileID: 11500000, guid: a403a09e241a0480a957591ea60fb785, type: 3} | |||||
m_Name: settings | |||||
m_EditorClassIdentifier: | |||||
usesCleartextTraffic: 0 | |||||
writeExternalStorage: 0 | |||||
accessFineLocation: 0 | |||||
addsKotlin: 1 | |||||
kotlinVersion: | |||||
addsAndroidBrowser: 1 | |||||
androidBrowserVersion: | |||||
addsAndroidXCore: 0 | |||||
androidXCoreVersion: | |||||
enableJetifier: 1 | |||||
authCallbackUrls: [] | |||||
supportLINELogin: 0 |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: f4e3e511116a133428f348c027b47bfa | |||||
NativeFormatImporter: | |||||
externalObjects: {} | |||||
mainObjectFileID: 11400000 | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,21 @@ | |||||
using System.Collections; | |||||
using System.Collections.Generic; | |||||
using UnityEngine; | |||||
public class Init : MonoBehaviour | |||||
{ | |||||
[SerializeField] | |||||
private UniWebView m_UniWebView; | |||||
// Start is called before the first frame update | |||||
void Start() | |||||
{ | |||||
m_UniWebView.SetBackButtonEnabled(false);// 回退钮 物理按键 | |||||
} | |||||
// Update is called once per frame | |||||
void Update() | |||||
{ | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 4f155601221f7e24ab1909afd0e4121a | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: d3dad2b92748d439e851e10d4f4ecaf0 | |||||
folderAsset: yes | |||||
timeCreated: 1489325633 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 3916a9bfa4fd449e2827f2f20897a7d7 | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,17 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<!-- GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN--> | |||||
<manifest | |||||
xmlns:android="http://schemas.android.com/apk/res/android" | |||||
package="com.unity3d.player" | |||||
xmlns:tools="http://schemas.android.com/tools"> | |||||
<application android:usesCleartextTraffic="true"> | |||||
<activity android:name="com.unity3d.player.UnityPlayerActivity" | |||||
android:theme="@style/UnityThemeSelector"> | |||||
<intent-filter> | |||||
<action android:name="android.intent.action.MAIN" /> | |||||
<category android:name="android.intent.category.LAUNCHER" /> | |||||
</intent-filter> | |||||
<meta-data android:name="unityplayer.UnityActivity" android:value="true" /> | |||||
</activity> | |||||
</application> | |||||
</manifest> |
@@ -0,0 +1,7 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 669f8dedf59caa842abee26fe99bfcd5 | |||||
TextScriptImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,32 @@ | |||||
fileFormatVersion: 2 | |||||
guid: a70633e155d144f5da10d40c35d9c832 | |||||
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: |
@@ -0,0 +1,34 @@ | |||||
fileFormatVersion: 2 | |||||
guid: db3f23043d1754d4b9c96ddb5ab457c9 | |||||
folderAsset: yes | |||||
PluginImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
iconMap: {} | |||||
executionOrder: {} | |||||
defineConstraints: [] | |||||
isPreloaded: 0 | |||||
isOverridable: 0 | |||||
isExplicitlyReferenced: 0 | |||||
validateReferences: 1 | |||||
platformData: | |||||
- first: | |||||
Any: | |||||
second: | |||||
enabled: 0 | |||||
settings: {} | |||||
- first: | |||||
Editor: Editor | |||||
second: | |||||
enabled: 1 | |||||
settings: | |||||
DefaultValueInitialized: true | |||||
- first: | |||||
Standalone: OSXUniversal | |||||
second: | |||||
enabled: 1 | |||||
settings: | |||||
CPU: AnyCPU | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 19150ffd969374afd9b181cd8c71f662 | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,48 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||||
<plist version="1.0"> | |||||
<dict> | |||||
<key>BuildMachineOSBuild</key> | |||||
<string>23F79</string> | |||||
<key>CFBundleDevelopmentRegion</key> | |||||
<string>en</string> | |||||
<key>CFBundleExecutable</key> | |||||
<string>UniWebView</string> | |||||
<key>CFBundleIdentifier</key> | |||||
<string>com.onevcat.UniWebViewMac</string> | |||||
<key>CFBundleInfoDictionaryVersion</key> | |||||
<string>6.0</string> | |||||
<key>CFBundleName</key> | |||||
<string>UniWebView</string> | |||||
<key>CFBundlePackageType</key> | |||||
<string>BNDL</string> | |||||
<key>CFBundleShortVersionString</key> | |||||
<string>1.0</string> | |||||
<key>CFBundleSupportedPlatforms</key> | |||||
<array> | |||||
<string>MacOSX</string> | |||||
</array> | |||||
<key>CFBundleVersion</key> | |||||
<string>1</string> | |||||
<key>DTCompiler</key> | |||||
<string>com.apple.compilers.llvm.clang.1_0</string> | |||||
<key>DTPlatformBuild</key> | |||||
<string></string> | |||||
<key>DTPlatformName</key> | |||||
<string>macosx</string> | |||||
<key>DTPlatformVersion</key> | |||||
<string>14.5</string> | |||||
<key>DTSDKBuild</key> | |||||
<string>23F73</string> | |||||
<key>DTSDKName</key> | |||||
<string>macosx14.5</string> | |||||
<key>DTXcode</key> | |||||
<string>1540</string> | |||||
<key>DTXcodeBuild</key> | |||||
<string>15F31d</string> | |||||
<key>LSMinimumSystemVersion</key> | |||||
<string>10.11</string> | |||||
<key>NSHumanReadableCopyright</key> | |||||
<string>Copyright © 2017年 OneV's Den. All rights reserved.</string> | |||||
</dict> | |||||
</plist> |
@@ -0,0 +1,7 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 6ed4d1ad77ba246f0aa50cb883ca2973 | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 495312126946747a3b197461eaff9687 | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,7 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 0168f4971f50d488e959c40d05b94c59 | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 80fcb824c01d44613803213f4b1ed096 | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,115 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||||
<plist version="1.0"> | |||||
<dict> | |||||
<key>files</key> | |||||
<dict/> | |||||
<key>files2</key> | |||||
<dict/> | |||||
<key>rules</key> | |||||
<dict> | |||||
<key>^Resources/</key> | |||||
<true/> | |||||
<key>^Resources/.*\.lproj/</key> | |||||
<dict> | |||||
<key>optional</key> | |||||
<true/> | |||||
<key>weight</key> | |||||
<real>1000</real> | |||||
</dict> | |||||
<key>^Resources/.*\.lproj/locversion.plist$</key> | |||||
<dict> | |||||
<key>omit</key> | |||||
<true/> | |||||
<key>weight</key> | |||||
<real>1100</real> | |||||
</dict> | |||||
<key>^Resources/Base\.lproj/</key> | |||||
<dict> | |||||
<key>weight</key> | |||||
<real>1010</real> | |||||
</dict> | |||||
<key>^version.plist$</key> | |||||
<true/> | |||||
</dict> | |||||
<key>rules2</key> | |||||
<dict> | |||||
<key>.*\.dSYM($|/)</key> | |||||
<dict> | |||||
<key>weight</key> | |||||
<real>11</real> | |||||
</dict> | |||||
<key>^(.*/)?\.DS_Store$</key> | |||||
<dict> | |||||
<key>omit</key> | |||||
<true/> | |||||
<key>weight</key> | |||||
<real>2000</real> | |||||
</dict> | |||||
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key> | |||||
<dict> | |||||
<key>nested</key> | |||||
<true/> | |||||
<key>weight</key> | |||||
<real>10</real> | |||||
</dict> | |||||
<key>^.*</key> | |||||
<true/> | |||||
<key>^Info\.plist$</key> | |||||
<dict> | |||||
<key>omit</key> | |||||
<true/> | |||||
<key>weight</key> | |||||
<real>20</real> | |||||
</dict> | |||||
<key>^PkgInfo$</key> | |||||
<dict> | |||||
<key>omit</key> | |||||
<true/> | |||||
<key>weight</key> | |||||
<real>20</real> | |||||
</dict> | |||||
<key>^Resources/</key> | |||||
<dict> | |||||
<key>weight</key> | |||||
<real>20</real> | |||||
</dict> | |||||
<key>^Resources/.*\.lproj/</key> | |||||
<dict> | |||||
<key>optional</key> | |||||
<true/> | |||||
<key>weight</key> | |||||
<real>1000</real> | |||||
</dict> | |||||
<key>^Resources/.*\.lproj/locversion.plist$</key> | |||||
<dict> | |||||
<key>omit</key> | |||||
<true/> | |||||
<key>weight</key> | |||||
<real>1100</real> | |||||
</dict> | |||||
<key>^Resources/Base\.lproj/</key> | |||||
<dict> | |||||
<key>weight</key> | |||||
<real>1010</real> | |||||
</dict> | |||||
<key>^[^/]+$</key> | |||||
<dict> | |||||
<key>nested</key> | |||||
<true/> | |||||
<key>weight</key> | |||||
<real>10</real> | |||||
</dict> | |||||
<key>^embedded\.provisionprofile$</key> | |||||
<dict> | |||||
<key>weight</key> | |||||
<real>20</real> | |||||
</dict> | |||||
<key>^version\.plist$</key> | |||||
<dict> | |||||
<key>weight</key> | |||||
<real>20</real> | |||||
</dict> | |||||
</dict> | |||||
</dict> | |||||
</plist> |
@@ -0,0 +1,7 @@ | |||||
fileFormatVersion: 2 | |||||
guid: f0a1c562a4f234a5c80db8830befc067 | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 10cf9bec133da4d3b836f29204deca1b | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,33 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 060e1768692e941c2aa0f7c9f9f10e32 | |||||
PluginImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
iconMap: {} | |||||
executionOrder: {} | |||||
defineConstraints: [] | |||||
isPreloaded: 0 | |||||
isOverridable: 0 | |||||
isExplicitlyReferenced: 0 | |||||
validateReferences: 1 | |||||
platformData: | |||||
- first: | |||||
Any: | |||||
second: | |||||
enabled: 0 | |||||
settings: {} | |||||
- first: | |||||
Editor: Editor | |||||
second: | |||||
enabled: 0 | |||||
settings: | |||||
DefaultValueInitialized: true | |||||
- first: | |||||
iPhone: iOS | |||||
second: | |||||
enabled: 1 | |||||
settings: | |||||
AddToEmbeddedBinaries: false | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 5d77f90ac1519fd43918145191e2c5eb | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,267 @@ | |||||
%YAML 1.1 | |||||
%TAG !u! tag:unity3d.com,2011: | |||||
--- !u!29 &1 | |||||
OcclusionCullingSettings: | |||||
m_ObjectHideFlags: 0 | |||||
serializedVersion: 2 | |||||
m_OcclusionBakeSettings: | |||||
smallestOccluder: 5 | |||||
smallestHole: 0.25 | |||||
backfaceThreshold: 100 | |||||
m_SceneGUID: 00000000000000000000000000000000 | |||||
m_OcclusionCullingData: {fileID: 0} | |||||
--- !u!104 &2 | |||||
RenderSettings: | |||||
m_ObjectHideFlags: 0 | |||||
serializedVersion: 9 | |||||
m_Fog: 0 | |||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} | |||||
m_FogMode: 3 | |||||
m_FogDensity: 0.01 | |||||
m_LinearFogStart: 0 | |||||
m_LinearFogEnd: 300 | |||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} | |||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} | |||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} | |||||
m_AmbientIntensity: 1 | |||||
m_AmbientMode: 0 | |||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} | |||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} | |||||
m_HaloStrength: 0.5 | |||||
m_FlareStrength: 1 | |||||
m_FlareFadeSpeed: 3 | |||||
m_HaloTexture: {fileID: 0} | |||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} | |||||
m_DefaultReflectionMode: 0 | |||||
m_DefaultReflectionResolution: 128 | |||||
m_ReflectionBounces: 1 | |||||
m_ReflectionIntensity: 1 | |||||
m_CustomReflection: {fileID: 0} | |||||
m_Sun: {fileID: 705507994} | |||||
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} | |||||
m_UseRadianceAmbientProbe: 0 | |||||
--- !u!157 &3 | |||||
LightmapSettings: | |||||
m_ObjectHideFlags: 0 | |||||
serializedVersion: 12 | |||||
m_GIWorkflowMode: 1 | |||||
m_GISettings: | |||||
serializedVersion: 2 | |||||
m_BounceScale: 1 | |||||
m_IndirectOutputScale: 1 | |||||
m_AlbedoBoost: 1 | |||||
m_EnvironmentLightingMode: 0 | |||||
m_EnableBakedLightmaps: 1 | |||||
m_EnableRealtimeLightmaps: 0 | |||||
m_LightmapEditorSettings: | |||||
serializedVersion: 12 | |||||
m_Resolution: 2 | |||||
m_BakeResolution: 40 | |||||
m_AtlasSize: 1024 | |||||
m_AO: 0 | |||||
m_AOMaxDistance: 1 | |||||
m_CompAOExponent: 1 | |||||
m_CompAOExponentDirect: 0 | |||||
m_ExtractAmbientOcclusion: 0 | |||||
m_Padding: 2 | |||||
m_LightmapParameters: {fileID: 0} | |||||
m_LightmapsBakeMode: 1 | |||||
m_TextureCompression: 1 | |||||
m_FinalGather: 0 | |||||
m_FinalGatherFiltering: 1 | |||||
m_FinalGatherRayCount: 256 | |||||
m_ReflectionCompression: 2 | |||||
m_MixedBakeMode: 2 | |||||
m_BakeBackend: 1 | |||||
m_PVRSampling: 1 | |||||
m_PVRDirectSampleCount: 32 | |||||
m_PVRSampleCount: 500 | |||||
m_PVRBounces: 2 | |||||
m_PVREnvironmentSampleCount: 500 | |||||
m_PVREnvironmentReferencePointCount: 2048 | |||||
m_PVRFilteringMode: 2 | |||||
m_PVRDenoiserTypeDirect: 0 | |||||
m_PVRDenoiserTypeIndirect: 0 | |||||
m_PVRDenoiserTypeAO: 0 | |||||
m_PVRFilterTypeDirect: 0 | |||||
m_PVRFilterTypeIndirect: 0 | |||||
m_PVRFilterTypeAO: 0 | |||||
m_PVREnvironmentMIS: 0 | |||||
m_PVRCulling: 1 | |||||
m_PVRFilteringGaussRadiusDirect: 1 | |||||
m_PVRFilteringGaussRadiusIndirect: 5 | |||||
m_PVRFilteringGaussRadiusAO: 2 | |||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5 | |||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2 | |||||
m_PVRFilteringAtrousPositionSigmaAO: 1 | |||||
m_ExportTrainingData: 0 | |||||
m_TrainingDataDestination: TrainingData | |||||
m_LightProbeSampleCountMultiplier: 4 | |||||
m_LightingDataAsset: {fileID: 0} | |||||
m_LightingSettings: {fileID: 0} | |||||
--- !u!196 &4 | |||||
NavMeshSettings: | |||||
serializedVersion: 2 | |||||
m_ObjectHideFlags: 0 | |||||
m_BuildSettings: | |||||
serializedVersion: 2 | |||||
agentTypeID: 0 | |||||
agentRadius: 0.5 | |||||
agentHeight: 2 | |||||
agentSlope: 45 | |||||
agentClimb: 0.4 | |||||
ledgeDropHeight: 0 | |||||
maxJumpAcrossDistance: 0 | |||||
minRegionArea: 2 | |||||
manualCellSize: 0 | |||||
cellSize: 0.16666667 | |||||
manualTileSize: 0 | |||||
tileSize: 256 | |||||
accuratePlacement: 0 | |||||
debug: | |||||
m_Flags: 0 | |||||
m_NavMeshData: {fileID: 0} | |||||
--- !u!1 &705507993 | |||||
GameObject: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInternal: {fileID: 0} | |||||
serializedVersion: 6 | |||||
m_Component: | |||||
- component: {fileID: 705507995} | |||||
- component: {fileID: 705507994} | |||||
m_Layer: 0 | |||||
m_Name: Directional Light | |||||
m_TagString: Untagged | |||||
m_Icon: {fileID: 0} | |||||
m_NavMeshLayer: 0 | |||||
m_StaticEditorFlags: 0 | |||||
m_IsActive: 1 | |||||
--- !u!108 &705507994 | |||||
Light: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInternal: {fileID: 0} | |||||
m_GameObject: {fileID: 705507993} | |||||
m_Enabled: 1 | |||||
serializedVersion: 8 | |||||
m_Type: 1 | |||||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} | |||||
m_Intensity: 1 | |||||
m_Range: 10 | |||||
m_SpotAngle: 30 | |||||
m_CookieSize: 10 | |||||
m_Shadows: | |||||
m_Type: 2 | |||||
m_Resolution: -1 | |||||
m_CustomResolution: -1 | |||||
m_Strength: 1 | |||||
m_Bias: 0.05 | |||||
m_NormalBias: 0.4 | |||||
m_NearPlane: 0.2 | |||||
m_Cookie: {fileID: 0} | |||||
m_DrawHalo: 0 | |||||
m_Flare: {fileID: 0} | |||||
m_RenderMode: 0 | |||||
m_CullingMask: | |||||
serializedVersion: 2 | |||||
m_Bits: 4294967295 | |||||
m_Lightmapping: 1 | |||||
m_LightShadowCasterMode: 0 | |||||
m_AreaSize: {x: 1, y: 1} | |||||
m_BounceIntensity: 1 | |||||
m_ColorTemperature: 6570 | |||||
m_UseColorTemperature: 0 | |||||
m_ShadowRadius: 0 | |||||
m_ShadowAngle: 0 | |||||
--- !u!4 &705507995 | |||||
Transform: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInternal: {fileID: 0} | |||||
m_GameObject: {fileID: 705507993} | |||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} | |||||
m_LocalPosition: {x: 0, y: 3, z: 0} | |||||
m_LocalScale: {x: 1, y: 1, z: 1} | |||||
m_Children: [] | |||||
m_Father: {fileID: 0} | |||||
m_RootOrder: 1 | |||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} | |||||
--- !u!1 &963194225 | |||||
GameObject: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInternal: {fileID: 0} | |||||
serializedVersion: 6 | |||||
m_Component: | |||||
- component: {fileID: 963194228} | |||||
- component: {fileID: 963194227} | |||||
- component: {fileID: 963194226} | |||||
m_Layer: 0 | |||||
m_Name: Main Camera | |||||
m_TagString: MainCamera | |||||
m_Icon: {fileID: 0} | |||||
m_NavMeshLayer: 0 | |||||
m_StaticEditorFlags: 0 | |||||
m_IsActive: 1 | |||||
--- !u!81 &963194226 | |||||
AudioListener: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInternal: {fileID: 0} | |||||
m_GameObject: {fileID: 963194225} | |||||
m_Enabled: 1 | |||||
--- !u!20 &963194227 | |||||
Camera: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInternal: {fileID: 0} | |||||
m_GameObject: {fileID: 963194225} | |||||
m_Enabled: 1 | |||||
serializedVersion: 2 | |||||
m_ClearFlags: 1 | |||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} | |||||
m_projectionMatrixMode: 1 | |||||
m_SensorSize: {x: 36, y: 24} | |||||
m_LensShift: {x: 0, y: 0} | |||||
m_GateFitMode: 2 | |||||
m_FocalLength: 50 | |||||
m_NormalizedViewPortRect: | |||||
serializedVersion: 2 | |||||
x: 0 | |||||
y: 0 | |||||
width: 1 | |||||
height: 1 | |||||
near clip plane: 0.3 | |||||
far clip plane: 1000 | |||||
field of view: 60 | |||||
orthographic: 0 | |||||
orthographic size: 5 | |||||
m_Depth: -1 | |||||
m_CullingMask: | |||||
serializedVersion: 2 | |||||
m_Bits: 4294967295 | |||||
m_RenderingPath: -1 | |||||
m_TargetTexture: {fileID: 0} | |||||
m_TargetDisplay: 0 | |||||
m_TargetEye: 3 | |||||
m_HDR: 1 | |||||
m_AllowMSAA: 1 | |||||
m_AllowDynamicResolution: 0 | |||||
m_ForceIntoRT: 0 | |||||
m_OcclusionCulling: 1 | |||||
m_StereoConvergence: 10 | |||||
m_StereoSeparation: 0.022 | |||||
--- !u!4 &963194228 | |||||
Transform: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInternal: {fileID: 0} | |||||
m_GameObject: {fileID: 963194225} | |||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | |||||
m_LocalPosition: {x: 0, y: 1, z: -10} | |||||
m_LocalScale: {x: 1, y: 1, z: 1} | |||||
m_Children: [] | |||||
m_Father: {fileID: 0} | |||||
m_RootOrder: 0 | |||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} |
@@ -0,0 +1,7 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 9fc0d4010bbf28b4594072e72b8655ab | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,373 @@ | |||||
%YAML 1.1 | |||||
%TAG !u! tag:unity3d.com,2011: | |||||
--- !u!29 &1 | |||||
OcclusionCullingSettings: | |||||
m_ObjectHideFlags: 0 | |||||
serializedVersion: 2 | |||||
m_OcclusionBakeSettings: | |||||
smallestOccluder: 5 | |||||
smallestHole: 0.25 | |||||
backfaceThreshold: 100 | |||||
m_SceneGUID: 00000000000000000000000000000000 | |||||
m_OcclusionCullingData: {fileID: 0} | |||||
--- !u!104 &2 | |||||
RenderSettings: | |||||
m_ObjectHideFlags: 0 | |||||
serializedVersion: 9 | |||||
m_Fog: 0 | |||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} | |||||
m_FogMode: 3 | |||||
m_FogDensity: 0.01 | |||||
m_LinearFogStart: 0 | |||||
m_LinearFogEnd: 300 | |||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} | |||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} | |||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} | |||||
m_AmbientIntensity: 1 | |||||
m_AmbientMode: 3 | |||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} | |||||
m_SkyboxMaterial: {fileID: 0} | |||||
m_HaloStrength: 0.5 | |||||
m_FlareStrength: 1 | |||||
m_FlareFadeSpeed: 3 | |||||
m_HaloTexture: {fileID: 0} | |||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} | |||||
m_DefaultReflectionMode: 0 | |||||
m_DefaultReflectionResolution: 128 | |||||
m_ReflectionBounces: 1 | |||||
m_ReflectionIntensity: 1 | |||||
m_CustomReflection: {fileID: 0} | |||||
m_Sun: {fileID: 0} | |||||
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} | |||||
m_UseRadianceAmbientProbe: 0 | |||||
--- !u!157 &3 | |||||
LightmapSettings: | |||||
m_ObjectHideFlags: 0 | |||||
serializedVersion: 12 | |||||
m_GIWorkflowMode: 1 | |||||
m_GISettings: | |||||
serializedVersion: 2 | |||||
m_BounceScale: 1 | |||||
m_IndirectOutputScale: 1 | |||||
m_AlbedoBoost: 1 | |||||
m_EnvironmentLightingMode: 0 | |||||
m_EnableBakedLightmaps: 0 | |||||
m_EnableRealtimeLightmaps: 0 | |||||
m_LightmapEditorSettings: | |||||
serializedVersion: 12 | |||||
m_Resolution: 2 | |||||
m_BakeResolution: 40 | |||||
m_AtlasSize: 1024 | |||||
m_AO: 0 | |||||
m_AOMaxDistance: 1 | |||||
m_CompAOExponent: 1 | |||||
m_CompAOExponentDirect: 0 | |||||
m_ExtractAmbientOcclusion: 0 | |||||
m_Padding: 2 | |||||
m_LightmapParameters: {fileID: 0} | |||||
m_LightmapsBakeMode: 1 | |||||
m_TextureCompression: 1 | |||||
m_FinalGather: 0 | |||||
m_FinalGatherFiltering: 1 | |||||
m_FinalGatherRayCount: 256 | |||||
m_ReflectionCompression: 2 | |||||
m_MixedBakeMode: 2 | |||||
m_BakeBackend: 0 | |||||
m_PVRSampling: 1 | |||||
m_PVRDirectSampleCount: 32 | |||||
m_PVRSampleCount: 500 | |||||
m_PVRBounces: 2 | |||||
m_PVREnvironmentSampleCount: 500 | |||||
m_PVREnvironmentReferencePointCount: 2048 | |||||
m_PVRFilteringMode: 2 | |||||
m_PVRDenoiserTypeDirect: 0 | |||||
m_PVRDenoiserTypeIndirect: 0 | |||||
m_PVRDenoiserTypeAO: 0 | |||||
m_PVRFilterTypeDirect: 0 | |||||
m_PVRFilterTypeIndirect: 0 | |||||
m_PVRFilterTypeAO: 0 | |||||
m_PVREnvironmentMIS: 0 | |||||
m_PVRCulling: 1 | |||||
m_PVRFilteringGaussRadiusDirect: 1 | |||||
m_PVRFilteringGaussRadiusIndirect: 5 | |||||
m_PVRFilteringGaussRadiusAO: 2 | |||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5 | |||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2 | |||||
m_PVRFilteringAtrousPositionSigmaAO: 1 | |||||
m_ExportTrainingData: 0 | |||||
m_TrainingDataDestination: TrainingData | |||||
m_LightProbeSampleCountMultiplier: 4 | |||||
m_LightingDataAsset: {fileID: 0} | |||||
m_LightingSettings: {fileID: 315771890} | |||||
--- !u!196 &4 | |||||
NavMeshSettings: | |||||
serializedVersion: 2 | |||||
m_ObjectHideFlags: 0 | |||||
m_BuildSettings: | |||||
serializedVersion: 2 | |||||
agentTypeID: 0 | |||||
agentRadius: 0.5 | |||||
agentHeight: 2 | |||||
agentSlope: 45 | |||||
agentClimb: 0.4 | |||||
ledgeDropHeight: 0 | |||||
maxJumpAcrossDistance: 0 | |||||
minRegionArea: 2 | |||||
manualCellSize: 0 | |||||
cellSize: 0.16666667 | |||||
manualTileSize: 0 | |||||
tileSize: 256 | |||||
accuratePlacement: 0 | |||||
maxJobWorkers: 0 | |||||
preserveTilesOutsideBounds: 0 | |||||
debug: | |||||
m_Flags: 0 | |||||
m_NavMeshData: {fileID: 0} | |||||
--- !u!850595691 &315771890 | |||||
LightingSettings: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_Name: Settings.lighting | |||||
serializedVersion: 3 | |||||
m_GIWorkflowMode: 1 | |||||
m_EnableBakedLightmaps: 0 | |||||
m_EnableRealtimeLightmaps: 0 | |||||
m_RealtimeEnvironmentLighting: 1 | |||||
m_BounceScale: 1 | |||||
m_AlbedoBoost: 1 | |||||
m_IndirectOutputScale: 1 | |||||
m_UsingShadowmask: 1 | |||||
m_BakeBackend: 0 | |||||
m_LightmapMaxSize: 1024 | |||||
m_BakeResolution: 40 | |||||
m_Padding: 2 | |||||
m_TextureCompression: 1 | |||||
m_AO: 0 | |||||
m_AOMaxDistance: 1 | |||||
m_CompAOExponent: 1 | |||||
m_CompAOExponentDirect: 0 | |||||
m_ExtractAO: 0 | |||||
m_MixedBakeMode: 2 | |||||
m_LightmapsBakeMode: 1 | |||||
m_FilterMode: 1 | |||||
m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} | |||||
m_ExportTrainingData: 0 | |||||
m_TrainingDataDestination: TrainingData | |||||
m_RealtimeResolution: 2 | |||||
m_ForceWhiteAlbedo: 0 | |||||
m_ForceUpdates: 0 | |||||
m_FinalGather: 0 | |||||
m_FinalGatherRayCount: 256 | |||||
m_FinalGatherFiltering: 1 | |||||
m_PVRCulling: 1 | |||||
m_PVRSampling: 1 | |||||
m_PVRDirectSampleCount: 32 | |||||
m_PVRSampleCount: 500 | |||||
m_PVREnvironmentSampleCount: 500 | |||||
m_PVREnvironmentReferencePointCount: 2048 | |||||
m_LightProbeSampleCountMultiplier: 4 | |||||
m_PVRBounces: 2 | |||||
m_PVRMinBounces: 2 | |||||
m_PVREnvironmentMIS: 0 | |||||
m_PVRFilteringMode: 2 | |||||
m_PVRDenoiserTypeDirect: 0 | |||||
m_PVRDenoiserTypeIndirect: 0 | |||||
m_PVRDenoiserTypeAO: 0 | |||||
m_PVRFilterTypeDirect: 0 | |||||
m_PVRFilterTypeIndirect: 0 | |||||
m_PVRFilterTypeAO: 0 | |||||
m_PVRFilteringGaussRadiusDirect: 1 | |||||
m_PVRFilteringGaussRadiusIndirect: 5 | |||||
m_PVRFilteringGaussRadiusAO: 2 | |||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5 | |||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2 | |||||
m_PVRFilteringAtrousPositionSigmaAO: 1 | |||||
--- !u!1 &564699633 | |||||
GameObject: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
serializedVersion: 6 | |||||
m_Component: | |||||
- component: {fileID: 564699635} | |||||
- component: {fileID: 564699634} | |||||
m_Layer: 0 | |||||
m_Name: Init | |||||
m_TagString: Untagged | |||||
m_Icon: {fileID: 0} | |||||
m_NavMeshLayer: 0 | |||||
m_StaticEditorFlags: 0 | |||||
m_IsActive: 1 | |||||
--- !u!114 &564699634 | |||||
MonoBehaviour: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 564699633} | |||||
m_Enabled: 1 | |||||
m_EditorHideFlags: 0 | |||||
m_Script: {fileID: 11500000, guid: 4f155601221f7e24ab1909afd0e4121a, type: 3} | |||||
m_Name: | |||||
m_EditorClassIdentifier: | |||||
m_UniWebView: {fileID: 776194971} | |||||
--- !u!4 &564699635 | |||||
Transform: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 564699633} | |||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | |||||
m_LocalPosition: {x: 0, y: 0, z: 0} | |||||
m_LocalScale: {x: 1, y: 1, z: 1} | |||||
m_Children: [] | |||||
m_Father: {fileID: 0} | |||||
m_RootOrder: 2 | |||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} | |||||
--- !u!114 &776194971 stripped | |||||
MonoBehaviour: | |||||
m_CorrespondingSourceObject: {fileID: 114939446366399424, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
m_PrefabInstance: {fileID: 783622082} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 0} | |||||
m_Enabled: 1 | |||||
m_EditorHideFlags: 0 | |||||
m_Script: {fileID: 11500000, guid: 598e18fb001004a81960f552978ecf4e, type: 3} | |||||
m_Name: | |||||
m_EditorClassIdentifier: | |||||
--- !u!1001 &783622082 | |||||
PrefabInstance: | |||||
m_ObjectHideFlags: 0 | |||||
serializedVersion: 2 | |||||
m_Modification: | |||||
m_TransformParent: {fileID: 0} | |||||
m_Modifications: | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_RootOrder | |||||
value: 1 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalPosition.x | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalPosition.y | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalPosition.z | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalRotation.w | |||||
value: 1 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalRotation.x | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalRotation.y | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalRotation.z | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 114939446366399424, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: urlOnStart | |||||
value: http://120.79.156.64/games/16game/ | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 114939446366399424, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: useEmbeddedToolbar | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
m_RemovedComponents: [] | |||||
m_SourcePrefab: {fileID: 100100000, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
--- !u!1 &1313296981 | |||||
GameObject: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
serializedVersion: 6 | |||||
m_Component: | |||||
- component: {fileID: 1313296986} | |||||
- component: {fileID: 1313296985} | |||||
- component: {fileID: 1313296982} | |||||
m_Layer: 0 | |||||
m_Name: Main Camera | |||||
m_TagString: MainCamera | |||||
m_Icon: {fileID: 0} | |||||
m_NavMeshLayer: 0 | |||||
m_StaticEditorFlags: 0 | |||||
m_IsActive: 1 | |||||
--- !u!81 &1313296982 | |||||
AudioListener: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 1313296981} | |||||
m_Enabled: 1 | |||||
--- !u!20 &1313296985 | |||||
Camera: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 1313296981} | |||||
m_Enabled: 1 | |||||
serializedVersion: 2 | |||||
m_ClearFlags: 1 | |||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} | |||||
m_projectionMatrixMode: 1 | |||||
m_GateFitMode: 2 | |||||
m_FOVAxisMode: 0 | |||||
m_SensorSize: {x: 36, y: 24} | |||||
m_LensShift: {x: 0, y: 0} | |||||
m_FocalLength: 50 | |||||
m_NormalizedViewPortRect: | |||||
serializedVersion: 2 | |||||
x: 0 | |||||
y: 0 | |||||
width: 1 | |||||
height: 1 | |||||
near clip plane: 0.3 | |||||
far clip plane: 1000 | |||||
field of view: 60 | |||||
orthographic: 1 | |||||
orthographic size: 5 | |||||
m_Depth: -1 | |||||
m_CullingMask: | |||||
serializedVersion: 2 | |||||
m_Bits: 4294967295 | |||||
m_RenderingPath: -1 | |||||
m_TargetTexture: {fileID: 0} | |||||
m_TargetDisplay: 0 | |||||
m_TargetEye: 3 | |||||
m_HDR: 1 | |||||
m_AllowMSAA: 1 | |||||
m_AllowDynamicResolution: 0 | |||||
m_ForceIntoRT: 0 | |||||
m_OcclusionCulling: 1 | |||||
m_StereoConvergence: 10 | |||||
m_StereoSeparation: 0.022 | |||||
--- !u!4 &1313296986 | |||||
Transform: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 1313296981} | |||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | |||||
m_LocalPosition: {x: 0, y: 0, z: -10} | |||||
m_LocalScale: {x: 1, y: 1, z: 1} | |||||
m_Children: [] | |||||
m_Father: {fileID: 0} | |||||
m_RootOrder: 0 | |||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} |
@@ -0,0 +1,7 @@ | |||||
fileFormatVersion: 2 | |||||
guid: c02c34e4bd6e14b4784558ce2e6af62a | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 92454175c62504ab5bf7128917b56362 | |||||
folderAsset: yes | |||||
timeCreated: 1490878496 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: f7ee8c3eb642344b08f4ceeb53d70cd1 | |||||
timeCreated: 1497057465 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 565d674726c3d499cade2709dd955ad0 | |||||
folderAsset: yes | |||||
timeCreated: 1536753564 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,318 @@ | |||||
%YAML 1.1 | |||||
%TAG !u! tag:unity3d.com,2011: | |||||
--- !u!29 &1 | |||||
OcclusionCullingSettings: | |||||
m_ObjectHideFlags: 0 | |||||
serializedVersion: 2 | |||||
m_OcclusionBakeSettings: | |||||
smallestOccluder: 5 | |||||
smallestHole: 0.25 | |||||
backfaceThreshold: 100 | |||||
m_SceneGUID: 00000000000000000000000000000000 | |||||
m_OcclusionCullingData: {fileID: 0} | |||||
--- !u!104 &2 | |||||
RenderSettings: | |||||
m_ObjectHideFlags: 0 | |||||
serializedVersion: 9 | |||||
m_Fog: 0 | |||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} | |||||
m_FogMode: 3 | |||||
m_FogDensity: 0.01 | |||||
m_LinearFogStart: 0 | |||||
m_LinearFogEnd: 300 | |||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} | |||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} | |||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} | |||||
m_AmbientIntensity: 1 | |||||
m_AmbientMode: 3 | |||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} | |||||
m_SkyboxMaterial: {fileID: 0} | |||||
m_HaloStrength: 0.5 | |||||
m_FlareStrength: 1 | |||||
m_FlareFadeSpeed: 3 | |||||
m_HaloTexture: {fileID: 0} | |||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} | |||||
m_DefaultReflectionMode: 0 | |||||
m_DefaultReflectionResolution: 128 | |||||
m_ReflectionBounces: 1 | |||||
m_ReflectionIntensity: 1 | |||||
m_CustomReflection: {fileID: 0} | |||||
m_Sun: {fileID: 0} | |||||
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} | |||||
m_UseRadianceAmbientProbe: 0 | |||||
--- !u!157 &3 | |||||
LightmapSettings: | |||||
m_ObjectHideFlags: 0 | |||||
serializedVersion: 12 | |||||
m_GIWorkflowMode: 1 | |||||
m_GISettings: | |||||
serializedVersion: 2 | |||||
m_BounceScale: 1 | |||||
m_IndirectOutputScale: 1 | |||||
m_AlbedoBoost: 1 | |||||
m_EnvironmentLightingMode: 0 | |||||
m_EnableBakedLightmaps: 0 | |||||
m_EnableRealtimeLightmaps: 0 | |||||
m_LightmapEditorSettings: | |||||
serializedVersion: 12 | |||||
m_Resolution: 2 | |||||
m_BakeResolution: 40 | |||||
m_AtlasSize: 1024 | |||||
m_AO: 0 | |||||
m_AOMaxDistance: 1 | |||||
m_CompAOExponent: 1 | |||||
m_CompAOExponentDirect: 0 | |||||
m_ExtractAmbientOcclusion: 0 | |||||
m_Padding: 2 | |||||
m_LightmapParameters: {fileID: 0} | |||||
m_LightmapsBakeMode: 1 | |||||
m_TextureCompression: 1 | |||||
m_FinalGather: 0 | |||||
m_FinalGatherFiltering: 1 | |||||
m_FinalGatherRayCount: 256 | |||||
m_ReflectionCompression: 2 | |||||
m_MixedBakeMode: 2 | |||||
m_BakeBackend: 0 | |||||
m_PVRSampling: 1 | |||||
m_PVRDirectSampleCount: 32 | |||||
m_PVRSampleCount: 500 | |||||
m_PVRBounces: 2 | |||||
m_PVREnvironmentSampleCount: 500 | |||||
m_PVREnvironmentReferencePointCount: 2048 | |||||
m_PVRFilteringMode: 2 | |||||
m_PVRDenoiserTypeDirect: 0 | |||||
m_PVRDenoiserTypeIndirect: 0 | |||||
m_PVRDenoiserTypeAO: 0 | |||||
m_PVRFilterTypeDirect: 0 | |||||
m_PVRFilterTypeIndirect: 0 | |||||
m_PVRFilterTypeAO: 0 | |||||
m_PVREnvironmentMIS: 0 | |||||
m_PVRCulling: 1 | |||||
m_PVRFilteringGaussRadiusDirect: 1 | |||||
m_PVRFilteringGaussRadiusIndirect: 5 | |||||
m_PVRFilteringGaussRadiusAO: 2 | |||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5 | |||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2 | |||||
m_PVRFilteringAtrousPositionSigmaAO: 1 | |||||
m_ExportTrainingData: 0 | |||||
m_TrainingDataDestination: TrainingData | |||||
m_LightProbeSampleCountMultiplier: 4 | |||||
m_LightingDataAsset: {fileID: 0} | |||||
m_LightingSettings: {fileID: 315771890} | |||||
--- !u!196 &4 | |||||
NavMeshSettings: | |||||
serializedVersion: 2 | |||||
m_ObjectHideFlags: 0 | |||||
m_BuildSettings: | |||||
serializedVersion: 2 | |||||
agentTypeID: 0 | |||||
agentRadius: 0.5 | |||||
agentHeight: 2 | |||||
agentSlope: 45 | |||||
agentClimb: 0.4 | |||||
ledgeDropHeight: 0 | |||||
maxJumpAcrossDistance: 0 | |||||
minRegionArea: 2 | |||||
manualCellSize: 0 | |||||
cellSize: 0.16666667 | |||||
manualTileSize: 0 | |||||
tileSize: 256 | |||||
accuratePlacement: 0 | |||||
maxJobWorkers: 0 | |||||
preserveTilesOutsideBounds: 0 | |||||
debug: | |||||
m_Flags: 0 | |||||
m_NavMeshData: {fileID: 0} | |||||
--- !u!850595691 &315771890 | |||||
LightingSettings: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_Name: Settings.lighting | |||||
serializedVersion: 3 | |||||
m_GIWorkflowMode: 1 | |||||
m_EnableBakedLightmaps: 0 | |||||
m_EnableRealtimeLightmaps: 0 | |||||
m_RealtimeEnvironmentLighting: 1 | |||||
m_BounceScale: 1 | |||||
m_AlbedoBoost: 1 | |||||
m_IndirectOutputScale: 1 | |||||
m_UsingShadowmask: 1 | |||||
m_BakeBackend: 0 | |||||
m_LightmapMaxSize: 1024 | |||||
m_BakeResolution: 40 | |||||
m_Padding: 2 | |||||
m_TextureCompression: 1 | |||||
m_AO: 0 | |||||
m_AOMaxDistance: 1 | |||||
m_CompAOExponent: 1 | |||||
m_CompAOExponentDirect: 0 | |||||
m_ExtractAO: 0 | |||||
m_MixedBakeMode: 2 | |||||
m_LightmapsBakeMode: 1 | |||||
m_FilterMode: 1 | |||||
m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} | |||||
m_ExportTrainingData: 0 | |||||
m_TrainingDataDestination: TrainingData | |||||
m_RealtimeResolution: 2 | |||||
m_ForceWhiteAlbedo: 0 | |||||
m_ForceUpdates: 0 | |||||
m_FinalGather: 0 | |||||
m_FinalGatherRayCount: 256 | |||||
m_FinalGatherFiltering: 1 | |||||
m_PVRCulling: 1 | |||||
m_PVRSampling: 1 | |||||
m_PVRDirectSampleCount: 32 | |||||
m_PVRSampleCount: 500 | |||||
m_PVREnvironmentSampleCount: 500 | |||||
m_PVREnvironmentReferencePointCount: 2048 | |||||
m_LightProbeSampleCountMultiplier: 4 | |||||
m_PVRBounces: 2 | |||||
m_PVRMinBounces: 2 | |||||
m_PVREnvironmentMIS: 0 | |||||
m_PVRFilteringMode: 2 | |||||
m_PVRDenoiserTypeDirect: 0 | |||||
m_PVRDenoiserTypeIndirect: 0 | |||||
m_PVRDenoiserTypeAO: 0 | |||||
m_PVRFilterTypeDirect: 0 | |||||
m_PVRFilterTypeIndirect: 0 | |||||
m_PVRFilterTypeAO: 0 | |||||
m_PVRFilteringGaussRadiusDirect: 1 | |||||
m_PVRFilteringGaussRadiusIndirect: 5 | |||||
m_PVRFilteringGaussRadiusAO: 2 | |||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5 | |||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2 | |||||
m_PVRFilteringAtrousPositionSigmaAO: 1 | |||||
--- !u!1001 &783622082 | |||||
PrefabInstance: | |||||
m_ObjectHideFlags: 0 | |||||
serializedVersion: 2 | |||||
m_Modification: | |||||
m_TransformParent: {fileID: 0} | |||||
m_Modifications: | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_RootOrder | |||||
value: 1 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalPosition.x | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalPosition.y | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalPosition.z | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalRotation.w | |||||
value: 1 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalRotation.x | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalRotation.y | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 4960404783511462, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: m_LocalRotation.z | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 114939446366399424, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: urlOnStart | |||||
value: http://120.79.156.64/games/16game/ | |||||
objectReference: {fileID: 0} | |||||
- target: {fileID: 114939446366399424, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
propertyPath: useEmbeddedToolbar | |||||
value: 0 | |||||
objectReference: {fileID: 0} | |||||
m_RemovedComponents: [] | |||||
m_SourcePrefab: {fileID: 100100000, guid: 7e3f16a6f6303419cbd9837f6c746de4, type: 3} | |||||
--- !u!1 &1313296981 | |||||
GameObject: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
serializedVersion: 6 | |||||
m_Component: | |||||
- component: {fileID: 1313296986} | |||||
- component: {fileID: 1313296985} | |||||
- component: {fileID: 1313296982} | |||||
m_Layer: 0 | |||||
m_Name: Main Camera | |||||
m_TagString: MainCamera | |||||
m_Icon: {fileID: 0} | |||||
m_NavMeshLayer: 0 | |||||
m_StaticEditorFlags: 0 | |||||
m_IsActive: 1 | |||||
--- !u!81 &1313296982 | |||||
AudioListener: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 1313296981} | |||||
m_Enabled: 1 | |||||
--- !u!20 &1313296985 | |||||
Camera: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 1313296981} | |||||
m_Enabled: 1 | |||||
serializedVersion: 2 | |||||
m_ClearFlags: 1 | |||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} | |||||
m_projectionMatrixMode: 1 | |||||
m_GateFitMode: 2 | |||||
m_FOVAxisMode: 0 | |||||
m_SensorSize: {x: 36, y: 24} | |||||
m_LensShift: {x: 0, y: 0} | |||||
m_FocalLength: 50 | |||||
m_NormalizedViewPortRect: | |||||
serializedVersion: 2 | |||||
x: 0 | |||||
y: 0 | |||||
width: 1 | |||||
height: 1 | |||||
near clip plane: 0.3 | |||||
far clip plane: 1000 | |||||
field of view: 60 | |||||
orthographic: 1 | |||||
orthographic size: 5 | |||||
m_Depth: -1 | |||||
m_CullingMask: | |||||
serializedVersion: 2 | |||||
m_Bits: 4294967295 | |||||
m_RenderingPath: -1 | |||||
m_TargetTexture: {fileID: 0} | |||||
m_TargetDisplay: 0 | |||||
m_TargetEye: 3 | |||||
m_HDR: 1 | |||||
m_AllowMSAA: 1 | |||||
m_AllowDynamicResolution: 0 | |||||
m_ForceIntoRT: 0 | |||||
m_OcclusionCulling: 1 | |||||
m_StereoConvergence: 10 | |||||
m_StereoSeparation: 0.022 | |||||
--- !u!4 &1313296986 | |||||
Transform: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 1313296981} | |||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | |||||
m_LocalPosition: {x: 0, y: 0, z: -10} | |||||
m_LocalScale: {x: 1, y: 1, z: 1} | |||||
m_Children: [] | |||||
m_Father: {fileID: 0} | |||||
m_RootOrder: 0 | |||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 938c077f40a814d4584ce2ad17947cf3 | |||||
timeCreated: 1536753573 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 5f0047c1c8c8348de9d66b0496b353e1 | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,237 @@ | |||||
using System.Xml; | |||||
using System.Text; | |||||
using System; | |||||
using UnityEngine; | |||||
internal class UniWebViewAndroidXmlDocument : XmlDocument { | |||||
private readonly string path; | |||||
protected readonly XmlNamespaceManager nameSpaceManager; | |||||
protected const string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android"; | |||||
protected UniWebViewAndroidXmlDocument(string path) { | |||||
this.path = path; | |||||
using (var reader = new XmlTextReader(path)) { | |||||
reader.Read(); | |||||
Load(reader); | |||||
} | |||||
nameSpaceManager = new XmlNamespaceManager(NameTable); | |||||
nameSpaceManager.AddNamespace("android", AndroidXmlNamespace); | |||||
} | |||||
public void Save() { | |||||
SaveAs(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; | |||||
public UniWebViewAndroidManifest(string path) : base(path) { | |||||
manifestElement = SelectSingleNode("/manifest") as XmlElement; | |||||
applicationElement = SelectSingleNode("/manifest/application") as XmlElement; | |||||
} | |||||
private XmlAttribute CreateAndroidAttribute(string key, string value) { | |||||
XmlAttribute attr = CreateAttribute("android", key, AndroidXmlNamespace); | |||||
attr.Value = value; | |||||
return attr; | |||||
} | |||||
internal XmlNode GetActivityWithLaunchIntent() { | |||||
return | |||||
SelectSingleNode( | |||||
"/manifest/application/activity[intent-filter/action/@android:name='android.intent.action.MAIN' and " | |||||
+ "intent-filter/category/@android:name='android.intent.category.LAUNCHER']", | |||||
nameSpaceManager); | |||||
} | |||||
internal bool SetUsesCleartextTraffic() { | |||||
var changed = false; | |||||
if (applicationElement.GetAttribute("usesCleartextTraffic", AndroidXmlNamespace) != "true") { | |||||
applicationElement.SetAttribute("usesCleartextTraffic", AndroidXmlNamespace, "true"); | |||||
changed = true; | |||||
} | |||||
return changed; | |||||
} | |||||
internal bool SetHardwareAccelerated() { | |||||
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; | |||||
} | |||||
return changed; | |||||
} | |||||
internal bool AddCameraPermission() { | |||||
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); | |||||
changed = true; | |||||
} | |||||
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); | |||||
changed = true; | |||||
} | |||||
return changed; | |||||
} | |||||
internal bool AddMicrophonePermission() { | |||||
bool changed = false; | |||||
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); | |||||
changed = true; | |||||
} | |||||
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); | |||||
changed = true; | |||||
} | |||||
return changed; | |||||
} | |||||
internal bool AddReadExternalStoragePermission() { | |||||
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); | |||||
changed = true; | |||||
} | |||||
return changed; | |||||
} | |||||
internal bool AddWriteExternalStoragePermission() { | |||||
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); | |||||
changed = true; | |||||
} | |||||
return changed; | |||||
} | |||||
internal bool AddAccessFineLocationPermission() { | |||||
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); | |||||
changed = true; | |||||
} | |||||
return changed; | |||||
} | |||||
internal bool AddAuthCallbacksIntentFilter(string[] authCallbackUrls) { | |||||
var changed = false; | |||||
XmlElement authActivityNode; | |||||
if (authCallbackUrls.Length > 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, "singleTask"); | |||||
created.SetAttribute("configChanges", AndroidXmlNamespace, "orientation|screenSize|keyboardHidden"); | |||||
authActivityNode = created; | |||||
} else { | |||||
authActivityNode = list[0] as XmlElement; | |||||
} | |||||
} else { | |||||
return false; | |||||
} | |||||
foreach (var url in authCallbackUrls) { | |||||
var intentFilter = CreateIntentFilter(url); | |||||
if (intentFilter != null) { | |||||
authActivityNode?.AppendChild(intentFilter); | |||||
changed = true; | |||||
} | |||||
} | |||||
if (authActivityNode != null) { | |||||
applicationElement.AppendChild(authActivityNode); | |||||
} | |||||
return changed; | |||||
} | |||||
private XmlElement CreateIntentFilter(string url) { | |||||
var uri = new Uri(url); | |||||
var scheme = uri.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"); | |||||
var action = CreateElement("action"); | |||||
action.SetAttribute("name", AndroidXmlNamespace, "android.intent.action.VIEW"); | |||||
filter.AppendChild(action); | |||||
var defaultCategory = CreateElement("category"); | |||||
defaultCategory.SetAttribute("name", AndroidXmlNamespace, "android.intent.category.DEFAULT"); | |||||
filter.AppendChild(defaultCategory); | |||||
var browseCategory = CreateElement("category"); | |||||
browseCategory.SetAttribute("name", AndroidXmlNamespace, "android.intent.category.BROWSABLE"); | |||||
filter.AppendChild(browseCategory); | |||||
var data = CreateElement("data"); | |||||
data.SetAttribute("scheme", AndroidXmlNamespace, scheme); | |||||
if (!String.IsNullOrEmpty(uri.Host)) { | |||||
data.SetAttribute("host", AndroidXmlNamespace, uri.Host); | |||||
} | |||||
if (uri.Port != -1) { | |||||
data.SetAttribute("port", AndroidXmlNamespace, uri.Port.ToString()); | |||||
} | |||||
if (!string.IsNullOrEmpty(uri.PathAndQuery) && uri.PathAndQuery != "/") { | |||||
data.SetAttribute("path", AndroidXmlNamespace, uri.PathAndQuery); | |||||
} | |||||
filter.AppendChild(data); | |||||
return filter; | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: d09054fcc76964295a49868566075973 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,346 @@ | |||||
using System.Collections.Generic; | |||||
using UnityEngine; | |||||
using System.IO; | |||||
using System.Text; | |||||
using System; | |||||
public class UniWebViewGradleConfig | |||||
{ | |||||
private readonly string m_filePath; | |||||
public UniWebViewGradleNode Root { get; } | |||||
public UniWebViewGradleConfig(string filePath) | |||||
{ | |||||
var file = File.ReadAllText(filePath); | |||||
TextReader reader = new StringReader(file); | |||||
m_filePath = filePath; | |||||
Root = new UniWebViewGradleNode("root"); | |||||
var curNode = Root; | |||||
var str = new StringBuilder(); | |||||
var inDoubleQuote = false; | |||||
var inSingleQuote = false; | |||||
var inDollarVariable = false; | |||||
while (reader.Peek() > 0) | |||||
{ | |||||
char c = (char)reader.Read(); | |||||
switch (c) | |||||
{ | |||||
// case '/': | |||||
// if (reader.Peek() == '/') | |||||
// { | |||||
// reader.Read(); | |||||
// string comment = reader.ReadLine(); | |||||
// Debug.Log("Comment line: " + comment); | |||||
// curNode.AppendChildNode(new UniWebViewGradleCommentNode(comment, curNode)); | |||||
// } | |||||
// else | |||||
// { | |||||
// str.Append('/'); | |||||
// } | |||||
// break; | |||||
case '\n': | |||||
case '\r': | |||||
{ | |||||
var strf = FormatStr(str); | |||||
if (!string.IsNullOrEmpty(strf)) | |||||
{ | |||||
curNode.AppendChildNode(new UniWebViewGradleContentNode(strf, curNode)); | |||||
} | |||||
} | |||||
inDollarVariable = false; | |||||
str = new StringBuilder(); | |||||
break; | |||||
case '\t': | |||||
{ | |||||
var strf = FormatStr(str); | |||||
if (!string.IsNullOrEmpty(strf)) | |||||
{ | |||||
str.Append(" "); | |||||
} | |||||
break; | |||||
} | |||||
case '{': | |||||
{ | |||||
if (inDoubleQuote || inSingleQuote) { | |||||
str.Append(c); | |||||
break; | |||||
} | |||||
var n = FormatStr(str); | |||||
if (!string.IsNullOrEmpty(n)) | |||||
{ | |||||
UniWebViewGradleNode node = new UniWebViewGradleNode(n, curNode); | |||||
curNode.AppendChildNode(node); | |||||
curNode = node; | |||||
} | |||||
} | |||||
str = new StringBuilder(); | |||||
break; | |||||
case '}': | |||||
{ | |||||
if (inDoubleQuote || inSingleQuote) { | |||||
str.Append(c); | |||||
break; | |||||
} | |||||
var strf = FormatStr(str); | |||||
if (!string.IsNullOrEmpty(strf)) | |||||
{ | |||||
curNode.AppendChildNode(new UniWebViewGradleContentNode(strf, curNode)); | |||||
} | |||||
curNode = curNode.Parent; | |||||
} | |||||
str = new StringBuilder(); | |||||
break; | |||||
case '\"': | |||||
if (inDollarVariable) { | |||||
str.Append(c); | |||||
break; | |||||
} | |||||
inDoubleQuote = !inDoubleQuote; | |||||
str.Append(c); | |||||
break; | |||||
case '\'': | |||||
if (inDollarVariable) { | |||||
str.Append(c); | |||||
break; | |||||
} | |||||
inSingleQuote = !inSingleQuote; | |||||
str.Append(c); | |||||
break; | |||||
case '$': | |||||
{ | |||||
if (inDoubleQuote || inSingleQuote) { | |||||
str.Append(c); | |||||
break; | |||||
} | |||||
inDollarVariable = true; | |||||
str.Append(c); | |||||
break; | |||||
} | |||||
default: | |||||
str.Append(c); | |||||
break; | |||||
} | |||||
} | |||||
// 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) { | |||||
path = m_filePath; | |||||
} | |||||
File.WriteAllText(path, Print()); | |||||
} | |||||
private static string FormatStr(StringBuilder sb) | |||||
{ | |||||
var str = sb.ToString(); | |||||
str = str.Trim(); | |||||
return str; | |||||
} | |||||
public string Print() | |||||
{ | |||||
StringBuilder sb = new StringBuilder(); | |||||
PrintNode(sb, Root, -1); | |||||
// Remove the first empty line. | |||||
sb.Remove(0, 1); | |||||
return sb.ToString(); | |||||
} | |||||
private string GetLevelIndent(int level) | |||||
{ | |||||
if (level <= 0) return ""; | |||||
StringBuilder sb = new StringBuilder(""); | |||||
for (int i = 0; i < level; i++) | |||||
{ | |||||
sb.Append('\t'); | |||||
} | |||||
return sb.ToString(); | |||||
} | |||||
private void PrintNode(StringBuilder stringBuilder, UniWebViewGradleNode node, int level) | |||||
{ | |||||
if (node.Parent != null) { | |||||
if (node is UniWebViewGradleCommentNode) | |||||
{ | |||||
stringBuilder.Append("\n" + GetLevelIndent(level) + @"//" + node.Name); | |||||
} | |||||
else | |||||
{ | |||||
stringBuilder.Append("\n" + GetLevelIndent(level) + node.Name); | |||||
} | |||||
} | |||||
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 string m_name; | |||||
public UniWebViewGradleNode Parent { get; private set; } | |||||
public string Name => m_name; | |||||
public List<UniWebViewGradleNode> Children { get; private set; } = new List<UniWebViewGradleNode>(); | |||||
public UniWebViewGradleNode(string name, UniWebViewGradleNode parent = null) | |||||
{ | |||||
Parent = parent; | |||||
m_name = name; | |||||
} | |||||
public void Each(Action<UniWebViewGradleNode> f) | |||||
{ | |||||
f(this); | |||||
foreach (var n in Children) | |||||
{ | |||||
n.Each(f); | |||||
} | |||||
} | |||||
public void AppendChildNode(UniWebViewGradleNode node) | |||||
{ | |||||
if (Children == null) Children = new List<UniWebViewGradleNode>(); | |||||
Children.Add(node); | |||||
node.Parent = this; | |||||
} | |||||
public UniWebViewGradleNode TryGetNode(string path) | |||||
{ | |||||
var subpath = path.Split('/'); | |||||
var cnode = this; | |||||
foreach (var p in subpath) | |||||
{ | |||||
if (string.IsNullOrEmpty(p)) continue; | |||||
var tnode = cnode.FindChildNodeByName(p); | |||||
if (tnode == null) | |||||
{ | |||||
Debug.Log("Can't find Node:" + p); | |||||
return null; | |||||
} | |||||
cnode = tnode; | |||||
} | |||||
return cnode; | |||||
} | |||||
public UniWebViewGradleNode FindChildNodeByName(string name) | |||||
{ | |||||
foreach (var n in Children) | |||||
{ | |||||
if (n is UniWebViewGradleCommentNode || n is UniWebViewGradleContentNode) | |||||
continue; | |||||
if (n.Name == name) | |||||
return n; | |||||
} | |||||
return null; | |||||
} | |||||
public bool ReplaceContentStartsWith(string pattern, string value) | |||||
{ | |||||
foreach (var n in Children) | |||||
{ | |||||
if (!(n is UniWebViewGradleContentNode)) continue; | |||||
if (n.m_name.StartsWith(pattern)) | |||||
{ | |||||
n.m_name = value; | |||||
return true; | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
public UniWebViewGradleContentNode ReplaceContentOrAddStartsWith(string pattern, string value) | |||||
{ | |||||
foreach (var n in Children) | |||||
{ | |||||
if (!(n is UniWebViewGradleContentNode)) continue; | |||||
if (n.m_name.StartsWith(pattern)) | |||||
{ | |||||
n.m_name = value; | |||||
return (UniWebViewGradleContentNode)n; | |||||
} | |||||
} | |||||
return AppendContentNode(value); | |||||
} | |||||
public UniWebViewGradleContentNode AppendContentNode(string content) | |||||
{ | |||||
foreach (var n in Children) | |||||
{ | |||||
if (!(n is UniWebViewGradleContentNode)) continue; | |||||
if (n.m_name == content) | |||||
{ | |||||
Debug.Log("UniWebViewGradleContentNode with " + content + " already exists!"); | |||||
return null; | |||||
} | |||||
} | |||||
UniWebViewGradleContentNode cnode = new UniWebViewGradleContentNode(content, this); | |||||
AppendChildNode(cnode); | |||||
return cnode; | |||||
} | |||||
public bool RemoveContentNode(string contentPattern) | |||||
{ | |||||
for(int i=0;i<Children.Count;i++) | |||||
{ | |||||
if (!(Children[i] is UniWebViewGradleContentNode)) continue; | |||||
if(Children[i].m_name.Contains(contentPattern)) | |||||
{ | |||||
Children.RemoveAt(i); | |||||
return true; | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
} | |||||
public sealed class UniWebViewGradleContentNode : UniWebViewGradleNode | |||||
{ | |||||
public UniWebViewGradleContentNode(String content, UniWebViewGradleNode parent) : base(content, parent) | |||||
{ | |||||
} | |||||
public void SetContent(string content) | |||||
{ | |||||
m_name = content; | |||||
} | |||||
} | |||||
public sealed class UniWebViewGradleCommentNode : UniWebViewGradleNode | |||||
{ | |||||
public UniWebViewGradleCommentNode(String content, UniWebViewGradleNode parent) : base(content, parent) | |||||
{ | |||||
} | |||||
public string GetComment() | |||||
{ | |||||
return m_name; | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 4a73a3268e19f44f7be818c5db9af457 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,47 @@ | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.IO; | |||||
public class UniWebViewGradlePropertyPatcher { | |||||
private readonly string filePath; | |||||
private readonly UniWebViewEditorSettings settings; | |||||
// 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); | |||||
} | |||||
public string UpdatedString() | |||||
{ | |||||
var lines = File.ReadAllLines(filePath); | |||||
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 && settings.enableJetifier) { | |||||
builder.AppendLine("android.enableJetifier=true"); | |||||
} | |||||
// 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(); | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 62bf94600ac0f42c09fc261d7ab19e12 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,15 @@ | |||||
{ | |||||
"name": "UniWebView-CSharp.Editor", | |||||
"references": ["GUID:343deaaf83e0cee4ca978e7df0b80d21","GUID:2bafac87e7f4b9b418d9448d219b01ab"], | |||||
"includePlatforms": [ | |||||
"Editor" | |||||
], | |||||
"excludePlatforms": [], | |||||
"allowUnsafeCode": false, | |||||
"overrideReferences": false, | |||||
"precompiledReferences": [], | |||||
"autoReferenced": true, | |||||
"defineConstraints": [], | |||||
"versionDefines": [], | |||||
"noEngineReferences": false | |||||
} |
@@ -0,0 +1,7 @@ | |||||
fileFormatVersion: 2 | |||||
guid: b6d0e5b49a073436cbcd56804553ee20 | |||||
AssemblyDefinitionImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,219 @@ | |||||
using UnityEngine; | |||||
using UnityEditor; | |||||
using System.Collections.Generic; | |||||
using System; | |||||
using System.IO; | |||||
public class UniWebViewEditorSettings: ScriptableObject | |||||
{ | |||||
private const string AssetPath = "Assets/Editor/UniWebView/settings.asset"; | |||||
[SerializeField] | |||||
internal bool usesCleartextTraffic = false; | |||||
[SerializeField] | |||||
internal bool writeExternalStorage = false; | |||||
[SerializeField] | |||||
internal bool accessFineLocation = false; | |||||
[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; | |||||
[SerializeField] | |||||
internal string[] authCallbackUrls = { }; | |||||
[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); | |||||
if (settings == null) { | |||||
settings = ScriptableObject.CreateInstance<UniWebViewEditorSettings>(); | |||||
Directory.CreateDirectory("Assets/Editor/UniWebView/"); | |||||
AssetDatabase.CreateAsset(settings, AssetPath); | |||||
AssetDatabase.SaveAssets(); | |||||
} | |||||
return settings; | |||||
} | |||||
internal static SerializedObject GetSerializedSettings() { | |||||
return new SerializedObject(GetOrCreateSettings()); | |||||
} | |||||
} | |||||
// 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; | |||||
#if UNITY_2018_3_OR_NEWER | |||||
private class Provider : SettingsProvider { | |||||
public Provider(string path, SettingsScope scope = SettingsScope.User): base(path, scope) {} | |||||
public override void OnGUI(string searchContext) { | |||||
DrawPref(); | |||||
} | |||||
} | |||||
[SettingsProvider] | |||||
static SettingsProvider UniWebViewPref() { | |||||
return new Provider("Preferences/UniWebView"); | |||||
} | |||||
#else | |||||
[PreferenceItem("UniWebView")] | |||||
#endif | |||||
static void DrawPref() { | |||||
EditorGUIUtility.labelWidth = 320; | |||||
EditorGUIUtility.fieldWidth = 20; | |||||
if (settings == null) { | |||||
settings = UniWebViewEditorSettings.GetSerializedSettings(); | |||||
} | |||||
settings.Update(); | |||||
EditorGUI.BeginChangeCheck(); | |||||
// Manifest | |||||
EditorGUILayout.Space(); | |||||
EditorGUILayout.BeginVertical(); | |||||
EditorGUILayout.LabelField("Android Manifest", EditorStyles.boldLabel); | |||||
EditorGUI.indentLevel++; | |||||
EditorGUILayout.PropertyField(settings.FindProperty("usesCleartextTraffic")); | |||||
DrawDetailLabel("If you need to load plain HTTP content."); | |||||
EditorGUILayout.PropertyField(settings.FindProperty("writeExternalStorage")); | |||||
DrawDetailLabel("If you need to download an image from web page."); | |||||
EditorGUILayout.PropertyField(settings.FindProperty("accessFineLocation")); | |||||
DrawDetailLabel("If you need to enable location support in web view."); | |||||
EditorGUI.indentLevel--; | |||||
EditorGUILayout.EndVertical(); | |||||
// Gradle | |||||
EditorGUILayout.Space(); | |||||
EditorGUILayout.BeginVertical(); | |||||
EditorGUILayout.LabelField("Gradle Build", EditorStyles.boldLabel); | |||||
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--; | |||||
EditorGUILayout.EndVertical(); | |||||
// Auth callbacks | |||||
EditorGUILayout.Space(); | |||||
EditorGUILayout.BeginVertical(); | |||||
EditorGUILayout.LabelField("Auth Callbacks", EditorStyles.boldLabel); | |||||
EditorGUI.indentLevel++; | |||||
EditorGUILayout.PropertyField(settings.FindProperty("authCallbackUrls"), true); | |||||
DrawDetailLabel("Adds all available auth callback URLs here to use UniWebView's auth support."); | |||||
EditorGUILayout.Space(); | |||||
EditorGUILayout.PropertyField(settings.FindProperty("supportLINELogin")); | |||||
DrawDetailLabel("LINE Login is using a custom fixed scheme. If you want to support LINE Login, turn on this."); | |||||
EditorGUI.indentLevel--; | |||||
EditorGUILayout.EndVertical(); | |||||
EditorGUILayout.Space(); | |||||
EditorGUILayout.BeginHorizontal(); | |||||
EditorGUI.indentLevel++; | |||||
EditorGUILayout.HelpBox("Read the help page to know more about all UniWebView preferences detail.", MessageType.Info); | |||||
var style = new GUIStyle(GUI.skin.label); | |||||
style.normal.textColor = Color.blue; | |||||
if (GUILayout.Button("Help Page", style)) { | |||||
Application.OpenURL("https://docs.uniwebview.com/guide/installation.html#optional-steps"); | |||||
} | |||||
EditorGUILayout.Space(); | |||||
EditorGUI.indentLevel--; | |||||
EditorGUILayout.EndHorizontal(); | |||||
if (EditorGUI.EndChangeCheck()) { | |||||
settings.ApplyModifiedProperties(); | |||||
AssetDatabase.SaveAssets(); | |||||
} | |||||
EditorGUIUtility.labelWidth = 0; | |||||
} | |||||
static void DrawDetailLabel(string text) { | |||||
EditorGUI.indentLevel++; | |||||
EditorGUILayout.LabelField(text, EditorStyles.miniLabel); | |||||
EditorGUI.indentLevel--; | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: a403a09e241a0480a957591ea60fb785 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,337 @@ | |||||
// #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; | |||||
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; | |||||
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..."); | |||||
PatchAndroidManifest(path); | |||||
PatchBuildGradle(path); | |||||
PatchGradleProperty(path); | |||||
} | |||||
private void PatchAndroidManifest(string root) { | |||||
var manifestFilePath = GetManifestFilePath(root); | |||||
var manifest = new UniWebViewAndroidManifest(manifestFilePath); | |||||
var changed = false; | |||||
Debug.Log("<UniWebView> Set hardware accelerated to enable smooth web view experience and HTML5 support like video and canvas."); | |||||
changed = manifest.SetHardwareAccelerated() || changed; | |||||
var settings = UniWebViewEditorSettings.GetOrCreateSettings(); | |||||
if (settings.usesCleartextTraffic) { | |||||
changed = manifest.SetUsesCleartextTraffic() || changed; | |||||
} | |||||
if (settings.writeExternalStorage) { | |||||
changed = manifest.AddWriteExternalStoragePermission() || changed; | |||||
} | |||||
if (settings.accessFineLocation) { | |||||
changed = manifest.AddAccessFineLocationPermission() || changed; | |||||
} | |||||
if (settings.authCallbackUrls.Length > 0) { | |||||
changed = manifest.AddAuthCallbacksIntentFilter(settings.authCallbackUrls) || changed; | |||||
} | |||||
if (settings.supportLINELogin) { | |||||
changed = manifest.AddAuthCallbacksIntentFilter(new string[] { "lineauth://auth" }) || changed; | |||||
} | |||||
if (changed) { | |||||
manifest.Save(); | |||||
} | |||||
} | |||||
private void PatchBuildGradle(string root) { | |||||
var gradleFilePath = GetGradleFilePath(root); | |||||
var config = new UniWebViewGradleConfig(gradleFilePath); | |||||
var settings = UniWebViewEditorSettings.GetOrCreateSettings(); | |||||
var kotlinPrefix = "implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:"; | |||||
var kotlinVersion = String.IsNullOrWhiteSpace(settings.kotlinVersion) | |||||
? UniWebViewEditorSettings.defaultKotlinVersion : settings.kotlinVersion; | |||||
var browserPrefix = "implementation 'androidx.browser:browser:"; | |||||
var browserVersion = String.IsNullOrWhiteSpace(settings.androidBrowserVersion) | |||||
? UniWebViewEditorSettings.defaultAndroidBrowserVersion : settings.androidBrowserVersion; | |||||
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.ReplaceContentOrAddStartsWith(kotlinPrefix, kotlinPrefix + kotlinVersion + "'"); | |||||
Debug.Log("<UniWebView> Updated Kotlin dependency in build.gradle."); | |||||
} | |||||
// Add browser package | |||||
if (settings.addsAndroidBrowser) { | |||||
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); | |||||
if (settings.addsKotlin) { | |||||
newNode.AppendContentNode(kotlinPrefix + kotlinVersion + "'"); | |||||
} | |||||
if (settings.addsAndroidBrowser) { | |||||
newNode.AppendContentNode(browserPrefix + browserVersion + "'"); | |||||
} | |||||
if (settings.addsAndroidXCore) { | |||||
newNode.AppendContentNode(androidXCorePrefix + androidXCoreVersion + "'"); | |||||
} | |||||
newNode.AppendContentNode("implementation(name: 'UniWebView', ext:'aar')"); | |||||
config.Root.AppendChildNode(newNode); | |||||
} | |||||
config.Save(); | |||||
} | |||||
private void PatchGradleProperty(string root) { | |||||
var gradlePropertyFilePath = GetGradlePropertyFilePath(root); | |||||
var patcher = | |||||
new UniWebViewGradlePropertyPatcher(gradlePropertyFilePath, UniWebViewEditorSettings.GetOrCreateSettings()); | |||||
patcher.Patch(); | |||||
} | |||||
private string CombinePaths(string[] paths) { | |||||
var path = ""; | |||||
foreach (var item in paths) { | |||||
path = Path.Combine(path, item); | |||||
} | |||||
return path; | |||||
} | |||||
private string GetManifestFilePath(string root) { | |||||
string[] comps = {root, "src", "main", "AndroidManifest.xml"}; | |||||
return CombinePaths(comps); | |||||
} | |||||
private string GetGradleFilePath(string root) { | |||||
string[] comps = {root, "build.gradle"}; | |||||
return CombinePaths(comps); | |||||
} | |||||
private string GetGradlePropertyFilePath(string root) { | |||||
#if UNITY_2019_3_OR_NEWER | |||||
string[] compos = {root, "..", "gradle.properties"}; | |||||
#else | |||||
string[] compos = {root, "gradle.properties"}; | |||||
#endif | |||||
return CombinePaths(compos); | |||||
} | |||||
} | |||||
#endif |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 59d4a8d85c95843719d8b9df823c3da3 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: dc0f52a2d219347b1a6390b753d6ac97 | |||||
folderAsset: yes | |||||
timeCreated: 1491898971 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,568 @@ | |||||
#if UNITY_ANDROID && !UNITY_EDITOR | |||||
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; | |||||
static 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()); | |||||
} | |||||
public static void SetLogLevel(int level) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setLogLevel", level); | |||||
} | |||||
public static bool IsWebViewSupported() { | |||||
CheckPlatform(); | |||||
return plugin.CallStatic<bool>("isWebViewSupported"); | |||||
} | |||||
public static void Init(string name, int x, int y, int width, int height) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("init", name, x, y, width, height); | |||||
} | |||||
public static void Destroy(string name) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("destroy", name); | |||||
} | |||||
public static void Load(string name, string url, bool skipEncoding, string readAccessURL) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("load", name, url); | |||||
} | |||||
public static void LoadHTMLString(string name, string html, string baseUrl, bool skipEncoding) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("loadHTMLString", name, html, baseUrl); | |||||
} | |||||
public static void Reload(string name) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("reload", name); | |||||
} | |||||
public static void Stop(string name) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("stop", name); | |||||
} | |||||
public static string GetUrl(string name) { | |||||
CheckPlatform(); | |||||
return plugin.CallStatic<string>("getUrl", name); | |||||
} | |||||
public static void SetFrame(string name, int x, int y, int width, int height) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setFrame", name, x, y, width, height); | |||||
} | |||||
public static void SetPosition(string name, int x, int y) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setPosition", name, x, y); | |||||
} | |||||
public static void SetSize(string name, int width, int height) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setSize", name, width, height); | |||||
} | |||||
public static bool Show(string name, bool fade, int edge, float duration, bool useAsync, string identifier) { | |||||
CheckPlatform(); | |||||
if (useAsync) { | |||||
plugin.CallStatic("showAsync", name, fade, edge, duration, identifier); | |||||
return true; | |||||
} else { | |||||
return plugin.CallStatic<bool>("show", name, fade, edge, duration, identifier); | |||||
} | |||||
} | |||||
public static bool Hide(string name, bool fade, int edge, float duration, bool useAsync, string identifier) { | |||||
CheckPlatform(); | |||||
if (useAsync) { | |||||
plugin.CallStatic("hideAsync", name, fade, edge, duration, identifier); | |||||
return true; | |||||
} else { | |||||
return plugin.CallStatic<bool>("hide", name, fade, edge, duration, identifier); | |||||
} | |||||
} | |||||
public static bool AnimateTo(string name, int x, int y, int width, int height, float duration, float delay, string identifier) { | |||||
CheckPlatform(); | |||||
return plugin.CallStatic<bool>("animateTo", name, x, y, width, height, duration, delay, identifier); | |||||
} | |||||
public static void AddJavaScript(string name, string jsString, string identifier) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("addJavaScript", name, jsString, identifier); | |||||
} | |||||
public static void EvaluateJavaScript(string name, string jsString, string identifier) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("evaluateJavaScript", name, jsString, identifier); | |||||
} | |||||
public static void AddUrlScheme(string name, string scheme) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("addUrlScheme", name, scheme); | |||||
} | |||||
public static void RemoveUrlScheme(string name, string scheme) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("removeUrlScheme", name, scheme); | |||||
} | |||||
public static void AddSslExceptionDomain(string name, string domain) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("addSslExceptionDomain", name, domain); | |||||
} | |||||
public static void RemoveSslExceptionDomain(string name, string domain) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("removeSslExceptionDomain", name, domain); | |||||
} | |||||
public static void AddPermissionTrustDomain(string name, string domain) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("addPermissionTrustDomain", name, domain); | |||||
} | |||||
public static void RemovePermissionTrustDomain(string name, string domain) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("removePermissionTrustDomain", name, domain); | |||||
} | |||||
public static void SetHeaderField(string name, string key, string value) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setHeaderField", name, key, value); | |||||
} | |||||
public static void SetUserAgent(string name, string userAgent) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setUserAgent", name, userAgent); | |||||
} | |||||
public static string GetUserAgent(string name) { | |||||
CheckPlatform(); | |||||
return plugin.CallStatic<string>("getUserAgent", name); | |||||
} | |||||
public static void SetAllowAutoPlay(bool flag) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setAllowAutoPlay", flag); | |||||
} | |||||
public static void SetAllowJavaScriptOpenWindow(bool flag) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setAllowJavaScriptOpenWindow", flag); | |||||
} | |||||
public static void SetAllowFileAccess(string name, bool flag) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setAllowFileAccess", name, flag); | |||||
} | |||||
public static void SetAcceptThirdPartyCookies(string name, bool flag) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setAcceptThirdPartyCookies", name, flag); | |||||
} | |||||
public static void SetAllowFileAccessFromFileURLs(string name, bool flag) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setAllowFileAccessFromFileURLs", name, flag); | |||||
} | |||||
public static void SetAllowUniversalAccessFromFileURLs(bool flag) { | |||||
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(); | |||||
plugin.CallStatic("setEnableKeyboardAvoidance", flag); | |||||
} | |||||
public static void SetJavaScriptEnabled(bool enabled) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setJavaScriptEnabled", enabled); | |||||
} | |||||
public static void CleanCache(string name) { | |||||
CheckPlatform(); | |||||
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"); | |||||
} | |||||
public static void SetCookie(string url, string cookie, bool skipEncoding) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setCookie", url, cookie); | |||||
} | |||||
public static string GetCookie(string url, string key, bool skipEncoding) { | |||||
CheckPlatform(); | |||||
return plugin.CallStatic<string>("getCookie", url, key); | |||||
} | |||||
public static void RemoveCookies(string url, bool skipEncoding) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("removeCookies", url); | |||||
} | |||||
public static void RemoveCookie(string url, string key, bool skipEncoding) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("removeCookie", url, key); | |||||
} | |||||
public static void ClearHttpAuthUsernamePassword(string host, string realm) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("clearHttpAuthUsernamePassword", host, realm); | |||||
} | |||||
public static void SetBackgroundColor(string name, float r, float g, float b, float a) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setBackgroundColor", name, r, g, b, a); | |||||
} | |||||
public static void SetWebViewAlpha(string name, float alpha) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setWebViewAlpha", name, alpha); | |||||
} | |||||
public static float GetWebViewAlpha(string name) { | |||||
CheckPlatform(); | |||||
return plugin.CallStatic<float>("getWebViewAlpha", name); | |||||
} | |||||
public static void SetShowSpinnerWhileLoading(string name, bool show) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setShowSpinnerWhileLoading", name, show); | |||||
} | |||||
public static void SetSpinnerText(string name, string text) { | |||||
CheckPlatform(); | |||||
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); | |||||
} | |||||
public static bool CanGoForward(string name) { | |||||
CheckPlatform(); | |||||
return plugin.CallStatic<bool>("canGoForward", name); | |||||
} | |||||
public static void GoBack(string name) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("goBack", name); | |||||
} | |||||
public static void GoForward(string name) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("goForward", name); | |||||
} | |||||
public static void SetOpenLinksInExternalBrowser(string name, bool flag) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setOpenLinksInExternalBrowser", name, flag); | |||||
} | |||||
public static void SetHorizontalScrollBarEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setHorizontalScrollBarEnabled", name, enabled); | |||||
} | |||||
public static void SetVerticalScrollBarEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setVerticalScrollBarEnabled", name, enabled); | |||||
} | |||||
public static void SetBouncesEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setBouncesEnabled", name, enabled); | |||||
} | |||||
public static void SetZoomEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setZoomEnabled", name, enabled); | |||||
} | |||||
public static void SetUseWideViewPort(string name, bool use) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setUseWideViewPort", name, use); | |||||
} | |||||
public static void SetLoadWithOverviewMode(string name, bool overview) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setLoadWithOverviewMode", name, overview); | |||||
} | |||||
public static void SetImmersiveModeEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setImmersiveModeEnabled", name, enabled); | |||||
} | |||||
public static void SetUserInteractionEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setUserInteractionEnabled", name, enabled); | |||||
} | |||||
public static void SetTransparencyClickingThroughEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setTransparencyClickingThroughEnabled", name, enabled); | |||||
} | |||||
public static void SetWebContentsDebuggingEnabled(bool enabled) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setWebContentsDebuggingEnabled", enabled); | |||||
} | |||||
public static void SetAllowHTTPAuthPopUpWindow(string name, bool flag) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setAllowHTTPAuthPopUpWindow", name, flag); | |||||
} | |||||
public static void Print(string name) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("print", name); | |||||
} | |||||
public static void CaptureSnapshot(string name, string filename) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("captureSnapshot", name, filename); | |||||
} | |||||
public static void ScrollTo(string name, int x, int y, bool animated) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("scrollTo", name, x, y, animated); | |||||
} | |||||
public static void SetCalloutEnabled(string name, bool flag) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setCalloutEnabled", name, flag); | |||||
} | |||||
public static void SetSupportMultipleWindows(string name, bool enabled, bool allowJavaScriptOpening) { | |||||
CheckPlatform(); | |||||
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); | |||||
} | |||||
public static void SetTextZoom(string name, int textZoom) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setTextZoom", name, textZoom); | |||||
} | |||||
public static float NativeScreenWidth() { | |||||
CheckPlatform(); | |||||
return plugin.CallStatic<float>("screenWidth"); | |||||
} | |||||
public static float NativeScreenHeight() { | |||||
CheckPlatform(); | |||||
return plugin.CallStatic<float>("screenHeight"); | |||||
} | |||||
public static void SetDownloadEventForContextMenuEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
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() { | |||||
CheckPlatform(); | |||||
return plugin.CallStatic<bool>("isSafeBrowsingSupported"); | |||||
} | |||||
public static void SafeBrowsingInit(string name, string url) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("safeBrowsingInit", name, url); | |||||
} | |||||
public static void SafeBrowsingSetToolbarColor(string name, float r, float g, float b) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("safeBrowsingSetToolbarColor", name, r, g, b); | |||||
} | |||||
public static void SafeBrowsingShow(string name) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("safeBrowsingShow", name); | |||||
} | |||||
// Authentication | |||||
public static bool IsAuthenticationIsSupported() { | |||||
CheckPlatform(); | |||||
return plugin.CallStatic<bool>("isAuthenticationIsSupported"); | |||||
} | |||||
public static void AuthenticationInit(string name, string url, string scheme) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("authenticationInit", name, url, scheme); | |||||
} | |||||
public static void AuthenticationStart(string name) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("authenticationStart", name); | |||||
} | |||||
public static void AuthenticationSetPrivateMode(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("authenticationSetPrivateMode", name, enabled); | |||||
} | |||||
public static void SetShowEmbeddedToolbar(string name, bool show) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setShowEmbeddedToolbar", name, show); | |||||
} | |||||
public static void SetEmbeddedToolbarOnTop(string name, bool top) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setEmbeddedToolbarOnTop", name, top); | |||||
} | |||||
public static void SetEmbeddedToolbarDoneButtonText(string name, string text) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setEmbeddedToolbarDoneButtonText", name, text); | |||||
} | |||||
public static void SetEmbeddedToolbarGoBackButtonText(string name, string text) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setEmbeddedToolbarGoBackButtonText", name, text); | |||||
} | |||||
public static void SetEmbeddedToolbarGoForwardButtonText(string name, string text) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setEmbeddedToolbarGoForwardButtonText", name, text); | |||||
} | |||||
public static void SetEmbeddedToolbarTitleText(string name, string text) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setEmbeddedToolbarTitleText", name, text); | |||||
} | |||||
public static void SetEmbeddedToolbarBackgroundColor(string name, Color color) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setEmbeddedToolbarBackgroundColor", name, color.r, color.g, color.b, color.a); | |||||
} | |||||
public static void SetEmbeddedToolbarButtonTextColor(string name, Color color) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setEmbeddedToolbarButtonTextColor", name, color.r, color.g, color.b, color.a); | |||||
} | |||||
public static void SetEmbeddedToolbarTitleTextColor(string name, Color color) { | |||||
CheckPlatform(); | |||||
plugin.CallStatic("setEmbeddedToolbarTitleTextColor", name, color.r, color.g, color.b, color.a); | |||||
} | |||||
public static void SetEmeddedToolbarNavigationButtonsShow(string name, bool show) { | |||||
CheckPlatform(); | |||||
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() { | |||||
if (!correctPlatform) { | |||||
throw new System.InvalidOperationException("Method can only be performed on Android."); | |||||
} | |||||
} | |||||
} | |||||
#endif |
@@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 5a1d3cecc27d64565835e14b493c935b | |||||
timeCreated: 1490880130 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,40 @@ | |||||
// #if UNITY_ANDROID && !UNITY_EDITOR | |||||
using System; | |||||
using System.Reflection; | |||||
using UnityEngine; | |||||
public class UniWebViewAndroidStaticListener: MonoBehaviour { | |||||
void Awake() { | |||||
DontDestroyOnLoad(gameObject); | |||||
} | |||||
void OnJavaMessage(string message) { | |||||
// {listener_name}@{method_name}@parameters | |||||
var parts = message.Split("@"[0]); | |||||
if (parts.Length < 3) { | |||||
Debug.Log("Not enough parts for receiving a message."); | |||||
return; | |||||
} | |||||
var listener = UniWebViewNativeListener.GetListener(parts[0]); | |||||
if (listener == null) { | |||||
return; | |||||
} | |||||
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; | |||||
var left = new string[leftLength]; | |||||
for (int i = 0; i < leftLength; i++) { | |||||
left[i] = parts[i + 2]; | |||||
} | |||||
methodInfo.Invoke(listener, new object[] { string.Join("@", left) }); | |||||
} | |||||
} | |||||
// #endif |
@@ -0,0 +1,13 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 2704cf8e127d541f1888d96429308645 | |||||
timeCreated: 1514387178 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,898 @@ | |||||
// | |||||
// UniWebViewInterface.cs | |||||
// Created by Wang Wei(@onevcat) on 2017-04-11. | |||||
// | |||||
// 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. | |||||
// | |||||
#if (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS) && !UNITY_EDITOR_WIN && !UNITY_EDITOR_LINUX | |||||
using UnityEngine; | |||||
using System; | |||||
using System.Runtime.InteropServices; | |||||
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); | |||||
private const string DllLib = | |||||
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX | |||||
"UniWebView"; | |||||
#else | |||||
"__Internal"; | |||||
#endif | |||||
private static bool correctPlatform = | |||||
#if UNITY_EDITOR_OSX | |||||
Application.platform == RuntimePlatform.OSXEditor || | |||||
Application.platform == RuntimePlatform.IPhonePlayer || // Support for Device Simulator package | |||||
Application.platform == RuntimePlatform.Android; // Support for Device Simulator package | |||||
#elif UNITY_STANDALONE_OSX | |||||
Application.platform == RuntimePlatform.OSXPlayer; | |||||
#else | |||||
Application.platform == RuntimePlatform.IPhonePlayer; | |||||
#endif | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_connectMessageSender( | |||||
[MarshalAs(UnmanagedType.FunctionPtr)] UnitySendMessageDelegate sendMessageDelegate | |||||
); | |||||
static void ConnectMessageSender() { | |||||
UniWebViewLogger.Instance.Info("Connecting to native side message sender."); | |||||
CheckPlatform(); | |||||
uv_connectMessageSender(SendMessage); | |||||
} | |||||
[MonoPInvokeCallback(typeof(UnitySendMessageDelegate))] | |||||
private static void SendMessage(IntPtr namePtr, IntPtr methodPtr, IntPtr parameterPtr) { | |||||
string name = Marshal.PtrToStringAuto(namePtr); | |||||
string method = Marshal.PtrToStringAuto(methodPtr); | |||||
string parameters = Marshal.PtrToStringAuto(parameterPtr); | |||||
UniWebViewLogger.Instance.Verbose( | |||||
"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); | |||||
if (methodInfo != null) { | |||||
methodInfo.Invoke(listener, new object[] { parameters }); | |||||
} | |||||
} | |||||
} | |||||
delegate string ChannelMethodDelegate(IntPtr namePtr, IntPtr methodPtr, IntPtr parameterPtr); | |||||
[DllImport(DllLib)] | |||||
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(); | |||||
uv_registerChannel(ChannelFunc); | |||||
} | |||||
[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); | |||||
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_setLogLevel(int level); | |||||
public static void SetLogLevel(int level) { | |||||
CheckPlatform(); | |||||
uv_setLogLevel(level); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_init(string name, int x, int y, int width, int height); | |||||
public static void Init(string name, int x, int y, int width, int height) { | |||||
CheckPlatform(); | |||||
if (String.IsNullOrEmpty(name)) { | |||||
return; | |||||
} | |||||
uv_init(name, x, y, width, height); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_destroy(string name); | |||||
public static void Destroy(string name) { | |||||
CheckPlatform(); | |||||
uv_destroy(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_load(string name, string url, bool skipEncoding, string readAccessURL); | |||||
public static void Load(string name, string url, bool skipEncoding, string readAccessURL) { | |||||
CheckPlatform(); | |||||
uv_load(name, url, skipEncoding, readAccessURL); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_loadHTMLString(string name, string html, string baseUrl, bool skipEncoding); | |||||
public static void LoadHTMLString(string name, string html, string baseUrl, bool skipEncoding) { | |||||
CheckPlatform(); | |||||
uv_loadHTMLString(name, html, baseUrl, skipEncoding); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_reload(string name); | |||||
public static void Reload(string name) { | |||||
CheckPlatform(); | |||||
uv_reload(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_stop(string name); | |||||
public static void Stop(string name) { | |||||
CheckPlatform(); | |||||
uv_stop(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern string uv_getUrl(string name); | |||||
public static string GetUrl(string name) { | |||||
CheckPlatform(); | |||||
return uv_getUrl(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setFrame(string name, int x, int y, int width, int height); | |||||
public static void SetFrame(string name, int x, int y, int width, int height) { | |||||
CheckPlatform(); | |||||
uv_setFrame(name, x, y, width, height); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setPosition(string name, int x, int y); | |||||
public static void SetPosition(string name, int x, int y) { | |||||
CheckPlatform(); | |||||
uv_setPosition(name, x, y); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setSize(string name, int width, int height); | |||||
public static void SetSize(string name, int width, int height) { | |||||
CheckPlatform(); | |||||
uv_setSize(name, width, height); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern bool uv_show(string name, bool fade, int edge, float duration, string identifier); | |||||
public static bool Show(string name, bool fade, int edge, float duration, bool useAsync, string identifier) { | |||||
CheckPlatform(); | |||||
return uv_show(name, fade, edge, duration, identifier); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern bool uv_hide(string name, bool fade, int edge, float duration, string identifier); | |||||
public static bool Hide(string name, bool fade, int edge, float duration, bool useAsync, string identifier) { | |||||
CheckPlatform(); | |||||
return uv_hide(name, fade, edge, duration, identifier); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern bool uv_animateTo( | |||||
string name, int x, int y, int width, int height, float duration, float delay, string identifier | |||||
); | |||||
public static bool AnimateTo( | |||||
string name, int x, int y, int width, int height, float duration, float delay, string identifier) | |||||
{ | |||||
CheckPlatform(); | |||||
return uv_animateTo(name, x, y, width, height, duration, delay, identifier); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_addJavaScript(string name, string jsString, string identifier); | |||||
public static void AddJavaScript(string name, string jsString, string identifier) { | |||||
CheckPlatform(); | |||||
uv_addJavaScript(name, jsString, identifier); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_evaluateJavaScript(string name, string jsString, string identifier); | |||||
public static void EvaluateJavaScript(string name, string jsString, string identifier) { | |||||
CheckPlatform(); | |||||
uv_evaluateJavaScript(name, jsString, identifier); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_addUrlScheme(string name, string scheme); | |||||
public static void AddUrlScheme(string name, string scheme) { | |||||
CheckPlatform(); | |||||
uv_addUrlScheme(name, scheme); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_removeUrlScheme(string name, string scheme); | |||||
public static void RemoveUrlScheme(string name, string scheme) { | |||||
CheckPlatform(); | |||||
uv_removeUrlScheme(name, scheme); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_addSslExceptionDomain(string name, string domain); | |||||
public static void AddSslExceptionDomain(string name, string domain) { | |||||
CheckPlatform(); | |||||
uv_addSslExceptionDomain(name, domain); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_removeSslExceptionDomain(string name, string domain); | |||||
public static void RemoveSslExceptionDomain(string name, string domain) { | |||||
CheckPlatform(); | |||||
uv_removeSslExceptionDomain(name, domain); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setHeaderField(string name, string key, string value); | |||||
public static void SetHeaderField(string name, string key, string value) { | |||||
CheckPlatform(); | |||||
uv_setHeaderField(name, key, value); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setUserAgent(string name, string userAgent); | |||||
public static void SetUserAgent(string name, string userAgent) { | |||||
CheckPlatform(); | |||||
uv_setUserAgent(name, userAgent); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern string uv_getUserAgent(string name); | |||||
public static string GetUserAgent(string name) { | |||||
CheckPlatform(); | |||||
return uv_getUserAgent(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setContentInsetAdjustmentBehavior(string name, int behavior); | |||||
public static void SetContentInsetAdjustmentBehavior( | |||||
string name, UniWebViewContentInsetAdjustmentBehavior behavior | |||||
) | |||||
{ | |||||
CheckPlatform(); | |||||
uv_setContentInsetAdjustmentBehavior(name, (int)behavior); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setAllowAutoPlay(bool flag); | |||||
public static void SetAllowAutoPlay(bool flag) { | |||||
CheckPlatform(); | |||||
uv_setAllowAutoPlay(flag); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setAllowInlinePlay(bool flag); | |||||
public static void SetAllowInlinePlay(bool flag) { | |||||
CheckPlatform(); | |||||
uv_setAllowInlinePlay(flag); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setAllowFileAccess(string name, bool flag); | |||||
public static void SetAllowFileAccess(string name, bool flag) { | |||||
CheckPlatform(); | |||||
uv_setAllowFileAccess(name, flag); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setAllowFileAccessFromFileURLs(string name, bool flag); | |||||
public static void SetAllowFileAccessFromFileURLs(string name, bool flag) { | |||||
CheckPlatform(); | |||||
uv_setAllowFileAccessFromFileURLs(name, flag); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setAllowUniversalAccessFromFileURLs(bool flag); | |||||
public static void SetAllowUniversalAccessFromFileURLs(bool flag) { | |||||
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); | |||||
public static void SetAllowJavaScriptOpenWindow(bool flag) { | |||||
CheckPlatform(); | |||||
uv_setAllowJavaScriptOpenWindow(flag); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setJavaScriptEnabled(bool flag); | |||||
public static void SetJavaScriptEnabled(bool flag) { | |||||
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); | |||||
public static void CleanCache(string name) { | |||||
CheckPlatform(); | |||||
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() { | |||||
CheckPlatform(); | |||||
uv_clearCookies(); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setCookie(string url, string cookie, bool skipEncoding); | |||||
public static void SetCookie(string url, string cookie, bool skipEncoding) { | |||||
CheckPlatform(); | |||||
uv_setCookie(url, cookie, skipEncoding); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_removeCookies(string url, bool skipEncoding); | |||||
public static void RemoveCookies(string url, bool skipEncoding) { | |||||
CheckPlatform(); | |||||
uv_removeCookies(url, skipEncoding); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_removeCookie(string url, string key, bool skipEncoding); | |||||
public static void RemoveCookie(string url, string key, bool skipEncoding) { | |||||
CheckPlatform(); | |||||
uv_removeCookie(url, key, skipEncoding); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern string uv_getCookie(string url, string key, bool skipEncoding); | |||||
public static string GetCookie(string url, string key, bool skipEncoding) { | |||||
CheckPlatform(); | |||||
return uv_getCookie(url, key, skipEncoding); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_clearHttpAuthUsernamePasswordHost(string host, string realm); | |||||
public static void ClearHttpAuthUsernamePassword(string host, string realm) { | |||||
CheckPlatform(); | |||||
uv_clearHttpAuthUsernamePasswordHost(host, realm); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setBackgroundColor(string name, float r, float g, float b, float a); | |||||
public static void SetBackgroundColor(string name, float r, float g, float b, float a) { | |||||
CheckPlatform(); | |||||
uv_setBackgroundColor(name, r, g, b, a); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setWebViewAlpha(string name, float alpha); | |||||
public static void SetWebViewAlpha(string name, float alpha) { | |||||
CheckPlatform(); | |||||
uv_setWebViewAlpha(name, alpha); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern float uv_getWebViewAlpha(string name); | |||||
public static float GetWebViewAlpha(string name) { | |||||
CheckPlatform(); | |||||
return uv_getWebViewAlpha(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setShowSpinnerWhileLoading(string name, bool show); | |||||
public static void SetShowSpinnerWhileLoading(string name, bool show) { | |||||
CheckPlatform(); | |||||
uv_setShowSpinnerWhileLoading(name, show); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setSpinnerText(string name, string text); | |||||
public static void SetSpinnerText(string name, string text) { | |||||
CheckPlatform(); | |||||
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) { | |||||
CheckPlatform(); | |||||
return uv_canGoBack(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern bool uv_canGoForward(string name); | |||||
public static bool CanGoForward(string name) { | |||||
CheckPlatform(); | |||||
return uv_canGoForward(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_goBack(string name); | |||||
public static void GoBack(string name) { | |||||
CheckPlatform(); | |||||
uv_goBack(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_goForward(string name); | |||||
public static void GoForward(string name) { | |||||
CheckPlatform(); | |||||
uv_goForward(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setOpenLinksInExternalBrowser(string name, bool flag); | |||||
public static void SetOpenLinksInExternalBrowser(string name, bool flag) { | |||||
CheckPlatform(); | |||||
uv_setOpenLinksInExternalBrowser(name, flag); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setHorizontalScrollBarEnabled(string name, bool enabled); | |||||
public static void SetHorizontalScrollBarEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
uv_setHorizontalScrollBarEnabled(name, enabled); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setVerticalScrollBarEnabled(string name, bool enabled); | |||||
public static void SetVerticalScrollBarEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
uv_setVerticalScrollBarEnabled(name, enabled); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setBouncesEnabled(string name, bool enabled); | |||||
public static void SetBouncesEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
uv_setBouncesEnabled(name, enabled); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setZoomEnabled(string name, bool enabled); | |||||
public static void SetZoomEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
uv_setZoomEnabled(name, enabled); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setWindowUserResizeEnabled(string name, bool enabled); | |||||
public static void SetWindowUserResizeEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
uv_setWindowUserResizeEnabled(name, enabled); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setUserInteractionEnabled(string name, bool enabled); | |||||
public static void SetUserInteractionEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
uv_setUserInteractionEnabled(name, enabled); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setTransparencyClickingThroughEnabled(string name, bool enabled); | |||||
public static void SetTransparencyClickingThroughEnabled(string name, bool enabled) { | |||||
CheckPlatform(); | |||||
uv_setTransparencyClickingThroughEnabled(name, enabled); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setWebContentsDebuggingEnabled(bool enabled); | |||||
public static void SetWebContentsDebuggingEnabled(bool enabled) { | |||||
CheckPlatform(); | |||||
uv_setWebContentsDebuggingEnabled(enabled); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setAllowBackForwardNavigationGestures(string name, bool flag); | |||||
public static void SetAllowBackForwardNavigationGestures(string name, bool flag) { | |||||
CheckPlatform(); | |||||
uv_setAllowBackForwardNavigationGestures(name, flag); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setAllowHTTPAuthPopUpWindow(string name, bool flag); | |||||
public static void SetAllowHTTPAuthPopUpWindow(string name, bool flag) { | |||||
CheckPlatform(); | |||||
uv_setAllowHTTPAuthPopUpWindow(name, flag); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_print(string name); | |||||
public static void Print(string name) { | |||||
CheckPlatform(); | |||||
uv_print(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_captureSnapshot(string name, string fileName); | |||||
public static void CaptureSnapshot(string name, string fileName) { | |||||
CheckPlatform(); | |||||
uv_captureSnapshot(name, fileName); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_scrollTo(string name, int x, int y, bool animated); | |||||
public static void ScrollTo(string name, int x, int y, bool animated) { | |||||
CheckPlatform(); | |||||
uv_scrollTo(name, x, y, animated); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setCalloutEnabled(string name, bool flag); | |||||
public static void SetCalloutEnabled(string name, bool flag) { | |||||
CheckPlatform(); | |||||
uv_setCalloutEnabled(name, flag); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setSupportMultipleWindows(string name, bool enabled, bool allowJavaScriptOpening); | |||||
public static void SetSupportMultipleWindows(string name, bool enabled, bool allowJavaScriptOpening) { | |||||
CheckPlatform(); | |||||
uv_setSupportMultipleWindows(name, enabled, allowJavaScriptOpening); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setDragInteractionEnabled(string name, bool flag); | |||||
public static void SetDragInteractionEnabled(string name, bool flag) { | |||||
CheckPlatform(); | |||||
uv_setDragInteractionEnabled(name, flag); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern float uv_nativeScreenWidth(); | |||||
public static float NativeScreenWidth() { | |||||
#if UNITY_EDITOR_OSX | |||||
return Screen.width; | |||||
#else | |||||
return uv_nativeScreenWidth(); | |||||
#endif | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern float uv_nativeScreenHeight(); | |||||
public static float NativeScreenHeight() { | |||||
#if UNITY_EDITOR_OSX | |||||
return Screen.height; | |||||
#else | |||||
return uv_nativeScreenHeight(); | |||||
#endif | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_addDownloadURL(string name, string urlString, int type); | |||||
public static void AddDownloadURL(string name, string urlString, int type) { | |||||
CheckPlatform(); | |||||
uv_addDownloadURL(name, urlString, type); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_removeDownloadURL(string name, string urlString, int type); | |||||
public static void RemoveDownloadURL(string name, string urlString, int type) { | |||||
CheckPlatform(); | |||||
uv_removeDownloadURL(name, urlString, type); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_addDownloadMIMEType(string name, string MIMEType, int type); | |||||
public static void AddDownloadMIMEType(string name, string MIMEType, int type) { | |||||
CheckPlatform(); | |||||
uv_addDownloadMIMEType(name, MIMEType, type); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_removeDownloadMIMETypes(string name, string MIMEType, int type); | |||||
public static void RemoveDownloadMIMETypes(string name, string MIMEType, int type) { | |||||
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) { | |||||
CheckPlatform(); | |||||
uv_setAllowUserChooseActionAfterDownloading(name, allowed); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_safeBrowsingInit(string name, string url); | |||||
public static void SafeBrowsingInit(string name, string url) { | |||||
CheckPlatform(); | |||||
if (String.IsNullOrEmpty(name)) { | |||||
return; | |||||
} | |||||
uv_safeBrowsingInit(name, url); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_safeBrowsingShow(string name); | |||||
public static void SafeBrowsingShow(string name) { | |||||
CheckPlatform(); | |||||
uv_safeBrowsingShow(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_safeBrowsingSetToolbarColor(string name, float r, float g, float b); | |||||
public static void SafeBrowsingSetToolbarColor(string name, float r, float g, float b) { | |||||
CheckPlatform(); | |||||
uv_safeBrowsingSetToolbarColor(name, r, g, b); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_safeBrowsingSetToolbarItemColor(string name, float r, float g, float b); | |||||
public static void SafeBrowsingSetToolbarItemColor(string name, float r, float g, float b) { | |||||
CheckPlatform(); | |||||
uv_safeBrowsingSetToolbarItemColor(name, r, g, b); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_safeBrowsingDismiss(string name); | |||||
public static void SafeBrowsingDismiss(string name) { | |||||
CheckPlatform(); | |||||
uv_safeBrowsingDismiss(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern bool uv_authenticationIsSupported(); | |||||
public static bool IsAuthenticationIsSupported() { | |||||
CheckPlatform(); | |||||
return uv_authenticationIsSupported(); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_authenticationInit(string name, string url, string scheme); | |||||
public static void AuthenticationInit(string name, string url, string scheme) { | |||||
CheckPlatform(); | |||||
uv_authenticationInit(name, url, scheme); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_authenticationStart(string name); | |||||
public static void AuthenticationStart(string name) { | |||||
CheckPlatform(); | |||||
uv_authenticationStart(name); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_authenticationSetPrivateMode(string name, bool flag); | |||||
public static void AuthenticationSetPrivateMode(string name, bool flag) { | |||||
CheckPlatform(); | |||||
uv_authenticationSetPrivateMode(name, flag); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setShowEmbeddedToolbar(string name, bool show); | |||||
public static void SetShowEmbeddedToolbar(string name, bool show) { | |||||
CheckPlatform(); | |||||
uv_setShowEmbeddedToolbar(name, show); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setEmbeddedToolbarOnTop(string name, bool top); | |||||
public static void SetEmbeddedToolbarOnTop(string name, bool top) { | |||||
CheckPlatform(); | |||||
uv_setEmbeddedToolbarOnTop(name, top); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setEmbeddedToolbarDoneButtonText(string name, string text); | |||||
public static void SetEmbeddedToolbarDoneButtonText(string name, string text) { | |||||
CheckPlatform(); | |||||
uv_setEmbeddedToolbarDoneButtonText(name, text); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setEmbeddedToolbarGoBackButtonText(string name, string text); | |||||
public static void SetEmbeddedToolbarGoBackButtonText(string name, string text) { | |||||
CheckPlatform(); | |||||
uv_setEmbeddedToolbarGoBackButtonText(name, text); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setEmbeddedToolbarGoForwardButtonText(string name, string text); | |||||
public static void SetEmbeddedToolbarGoForwardButtonText(string name, string text) { | |||||
CheckPlatform(); | |||||
uv_setEmbeddedToolbarGoForwardButtonText(name, text); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setEmbeddedToolbarTitleText(string name, string text); | |||||
public static void SetEmbeddedToolbarTitleText(string name, string text) { | |||||
CheckPlatform(); | |||||
uv_setEmbeddedToolbarTitleText(name, text); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setEmbeddedToolbarBackgroundColor(string name, float r, float g, float b, float a); | |||||
public static void SetEmbeddedToolbarBackgroundColor(string name, Color color) { | |||||
CheckPlatform(); | |||||
uv_setEmbeddedToolbarBackgroundColor(name, color.r, color.g, color.b, color.a); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setEmbeddedToolbarButtonTextColor(string name, float r, float g, float b, float a); | |||||
public static void SetEmbeddedToolbarButtonTextColor(string name, Color color) { | |||||
CheckPlatform(); | |||||
uv_setEmbeddedToolbarButtonTextColor(name, color.r, color.g, color.b, color.a); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setEmbeddedToolbarTitleTextColor(string name, float r, float g, float b, float a); | |||||
public static void SetEmbeddedToolbarTitleTextColor(string name, Color color) { | |||||
CheckPlatform(); | |||||
uv_setEmbeddedToolbarTitleTextColor(name, color.r, color.g, color.b, color.a); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setEmbeddedToolbarNavigationButtonsShow(string name, bool show); | |||||
public static void SetEmeddedToolbarNavigationButtonsShow(string name, bool show) { | |||||
CheckPlatform(); | |||||
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)] | |||||
private static extern void uv_setShowToolbar(string name, bool show, bool animated, bool onTop, bool adjustInset); | |||||
public static void SetShowToolbar(string name, bool show, bool animated, bool onTop, bool adjustInset) { | |||||
CheckPlatform(); | |||||
uv_setShowToolbar(name, show, animated, onTop, adjustInset); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setShowToolbarNavigationButtons(string name, bool show); | |||||
public static void SetShowToolbarNavigationButtons(string name, bool show) { | |||||
CheckPlatform(); | |||||
uv_setShowToolbarNavigationButtons(name, show); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setToolbarDoneButtonText(string name, string text); | |||||
public static void SetToolbarDoneButtonText(string name, string text) { | |||||
CheckPlatform(); | |||||
uv_setToolbarDoneButtonText(name, text); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setGoBackButtonText(string name, string text); | |||||
public static void SetToolbarGoBackButtonText(string name, string text) { | |||||
CheckPlatform(); | |||||
uv_setGoBackButtonText(name, text); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setGoForwardButtonText(string name, string text); | |||||
public static void SetToolbarGoForwardButtonText(string name, string text) { | |||||
CheckPlatform(); | |||||
uv_setGoForwardButtonText(name, text); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setToolbarTintColor(string name, float r, float g, float b); | |||||
public static void SetToolbarTintColor(string name, float r, float g, float b) { | |||||
CheckPlatform(); | |||||
uv_setToolbarTintColor(name, r, g, b); | |||||
} | |||||
[DllImport(DllLib)] | |||||
private static extern void uv_setToolbarTextColor(string name, float r, float g, float b); | |||||
public static void SetToolbarTextColor(string name, float r, float g, float b) { | |||||
CheckPlatform(); | |||||
uv_setToolbarTextColor(name, r, g, b); | |||||
} | |||||
#endregion | |||||
public static void CheckPlatform() { | |||||
if (!correctPlatform) { | |||||
throw new System.InvalidOperationException( | |||||
"Method can only be performed on correct platform. Current: " + Application.platform | |||||
); | |||||
} | |||||
} | |||||
} | |||||
#endif |
@@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 2e905ba0b47304f4cbf9e3e3345f84eb | |||||
timeCreated: 1492400358 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,113 @@ | |||||
#if UNITY_EDITOR_WIN || UNITY_EDITOR_LINUX || (!UNITY_EDITOR_OSX && !UNITY_STANDALONE_OSX && !UNITY_IOS && !UNITY_ANDROID) | |||||
using UnityEngine; | |||||
public class UniWebViewInterface { | |||||
private static bool alreadyLoggedWarning = false; | |||||
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(); } | |||||
public static void Load(string name, string url, bool skipEncoding, string readAccessURL) { CheckPlatform(); } | |||||
public static void LoadHTMLString(string name, string html, string baseUrl, bool skipEncoding) { CheckPlatform(); } | |||||
public static void Reload(string name) { CheckPlatform(); } | |||||
public static void Stop(string name) { CheckPlatform(); } | |||||
public static string GetUrl(string name) { CheckPlatform(); return ""; } | |||||
public static void SetFrame(string name, int x, int y, int width, int height) { CheckPlatform(); } | |||||
public static void SetPosition(string name, int x, int y) { CheckPlatform(); } | |||||
public static void SetSize(string name, int width, int height) { CheckPlatform(); } | |||||
public static bool Show(string name, bool fade, int edge, float duration, bool useAsync, string identifier) { CheckPlatform(); return false; } | |||||
public static bool Hide(string name, bool fade, int edge, float duration, bool useAsync, string identifier) { CheckPlatform(); return false; } | |||||
public static bool AnimateTo(string name, int x, int y, int width, int height, float duration, float delay, string identifier) { CheckPlatform(); return false; } | |||||
public static void AddJavaScript(string name, string jsString, string identifier) { CheckPlatform(); } | |||||
public static void EvaluateJavaScript(string name, string jsString, string identifier) { CheckPlatform(); } | |||||
public static void AddUrlScheme(string name, string scheme) { CheckPlatform(); } | |||||
public static void RemoveUrlScheme(string name, string scheme) { CheckPlatform(); } | |||||
public static void AddSslExceptionDomain(string name, string domain) { CheckPlatform(); } | |||||
public static void RemoveSslExceptionDomain(string name, string domain) { CheckPlatform(); } | |||||
public static void SetHeaderField(string name, string key, string value) { CheckPlatform(); } | |||||
public static void SetUserAgent(string name, string userAgent) { CheckPlatform(); } | |||||
public static string GetUserAgent(string name) { CheckPlatform(); return ""; } | |||||
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(); } | |||||
public static void RemoveCookie(string url, string key, bool skipEncoding) { CheckPlatform(); } | |||||
public static string GetCookie(string url, string key, bool skipEncoding) { CheckPlatform(); return ""; } | |||||
public static void ClearHttpAuthUsernamePassword(string host, string realm) { CheckPlatform(); } | |||||
public static void SetBackgroundColor(string name, float r, float g, float b, float a) { CheckPlatform(); } | |||||
public static void SetWebViewAlpha(string name, float alpha) { CheckPlatform(); } | |||||
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(); } | |||||
public static void GoForward(string name) { CheckPlatform(); } | |||||
public static void SetOpenLinksInExternalBrowser(string name, bool flag) { CheckPlatform(); } | |||||
public static void SetHorizontalScrollBarEnabled(string name, bool enabled) { CheckPlatform(); } | |||||
public static void SetVerticalScrollBarEnabled(string name, bool enabled) { CheckPlatform(); } | |||||
public static void SetBouncesEnabled(string name, bool enabled) { CheckPlatform(); } | |||||
public static void SetZoomEnabled(string name, bool enabled) { CheckPlatform(); } | |||||
public static void SetShowToolbar(string name, bool show, bool animated, bool onTop, bool adjustInset) { CheckPlatform(); } | |||||
public static void SetToolbarDoneButtonText(string name, string text) { CheckPlatform(); } | |||||
public static void SetToolbarGoBackButtonText(string name, string text) { CheckPlatform(); } | |||||
public static void SetToolbarGoForwardButtonText(string name, string text) { CheckPlatform(); } | |||||
public static void SetToolbarTintColor(string name, float r, float g, float b) { CheckPlatform(); } | |||||
public static void SetToolbarTextColor(string name, float r, float g, float b) { CheckPlatform(); } | |||||
public static void SetUserInteractionEnabled(string name, bool enabled) { CheckPlatform(); } | |||||
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(); } | |||||
public static void SetSupportMultipleWindows(string name, bool enabled, bool allowJavaScriptOpening) { CheckPlatform(); } | |||||
public static void SetDragInteractionEnabled(string name, bool flag) { CheckPlatform(); } | |||||
public static void ScrollTo(string name, int x, int y, bool animated) { CheckPlatform(); } | |||||
public static float NativeScreenWidth() { CheckPlatform(); return 0.0f; } | |||||
public static float NativeScreenHeight() { CheckPlatform(); return 0.0f; } | |||||
public static void SafeBrowsingInit(string name, string url) { CheckPlatform(); } | |||||
public static void SafeBrowsingSetToolbarColor(string name, float r, float g, float b) { CheckPlatform(); } | |||||
public static void SafeBrowsingShow(string name) { CheckPlatform(); } | |||||
public static bool IsAuthenticationIsSupported() { CheckPlatform(); return false; } | |||||
public static void AuthenticationInit(string name, string url, string scheme) { CheckPlatform(); } | |||||
public static void AuthenticationStart(string name) { CheckPlatform(); } | |||||
public static void AuthenticationSetPrivateMode(string name, bool enabled) { CheckPlatform(); } | |||||
public static void SetShowEmbeddedToolbar(string name, bool show) { CheckPlatform(); } | |||||
public static void SetEmbeddedToolbarOnTop(string name, bool top) { CheckPlatform(); } | |||||
public static void SetEmbeddedToolbarDoneButtonText(string name, string text) { CheckPlatform(); } | |||||
public static void SetEmbeddedToolbarGoBackButtonText(string name, string text) { CheckPlatform(); } | |||||
public static void SetEmbeddedToolbarGoForwardButtonText(string name, string text) { CheckPlatform(); } | |||||
public static void SetEmbeddedToolbarTitleText(string name, string text) { CheckPlatform(); } | |||||
public static void SetEmbeddedToolbarBackgroundColor(string name, Color color) { CheckPlatform(); } | |||||
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) { | |||||
alreadyLoggedWarning = true; | |||||
Debug.LogWarning("UniWebView only supports iOS/Android/macOS Editor. You current platform " + Application.platform + " is not supported."); | |||||
} | |||||
} | |||||
} | |||||
#endif |
@@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 0b3bad20d12b1433ab8927c3effc605b | |||||
timeCreated: 1497403102 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: d5d657f2ee1114e20bccaace74235a99 | |||||
folderAsset: yes | |||||
timeCreated: 1491898971 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,60 @@ | |||||
%YAML 1.1 | |||||
%TAG !u! tag:unity3d.com,2011: | |||||
--- !u!1 &1900085666445226 | |||||
GameObject: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
serializedVersion: 6 | |||||
m_Component: | |||||
- component: {fileID: 4960404783511462} | |||||
- component: {fileID: 114939446366399424} | |||||
m_Layer: 0 | |||||
m_Name: UniWebView | |||||
m_TagString: Untagged | |||||
m_Icon: {fileID: 0} | |||||
m_NavMeshLayer: 0 | |||||
m_StaticEditorFlags: 0 | |||||
m_IsActive: 1 | |||||
--- !u!4 &4960404783511462 | |||||
Transform: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 1900085666445226} | |||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | |||||
m_LocalPosition: {x: 0, y: 0, z: 0} | |||||
m_LocalScale: {x: 1, y: 1, z: 1} | |||||
m_ConstrainProportionsScale: 0 | |||||
m_Children: [] | |||||
m_Father: {fileID: 0} | |||||
m_RootOrder: 0 | |||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} | |||||
--- !u!114 &114939446366399424 | |||||
MonoBehaviour: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 1900085666445226} | |||||
m_Enabled: 1 | |||||
m_EditorHideFlags: 0 | |||||
m_Script: {fileID: 11500000, guid: 598e18fb001004a81960f552978ecf4e, type: 3} | |||||
m_Name: | |||||
m_EditorClassIdentifier: | |||||
urlOnStart: | |||||
showOnStart: 1 | |||||
fullScreen: 1 | |||||
useToolbar: 0 | |||||
toolbarPosition: 0 | |||||
useEmbeddedToolbar: 1 | |||||
embeddedToolbarPosition: 0 | |||||
frame: | |||||
serializedVersion: 2 | |||||
x: 0 | |||||
y: 0 | |||||
width: 0 | |||||
height: 0 | |||||
referenceRectTransform: {fileID: 0} |
@@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 7e3f16a6f6303419cbd9837f6c746de4 | |||||
timeCreated: 1496204510 | |||||
licenseType: Store | |||||
NativeFormatImporter: | |||||
mainObjectFileID: 100100000 | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,46 @@ | |||||
%YAML 1.1 | |||||
%TAG !u! tag:unity3d.com,2011: | |||||
--- !u!1 &8236771505572270655 | |||||
GameObject: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
serializedVersion: 6 | |||||
m_Component: | |||||
- component: {fileID: 8236771505572270653} | |||||
- component: {fileID: 8236771505572270654} | |||||
m_Layer: 0 | |||||
m_Name: UniWebViewSafeBrowsing | |||||
m_TagString: Untagged | |||||
m_Icon: {fileID: 0} | |||||
m_NavMeshLayer: 0 | |||||
m_StaticEditorFlags: 0 | |||||
m_IsActive: 1 | |||||
--- !u!4 &8236771505572270653 | |||||
Transform: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 8236771505572270655} | |||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | |||||
m_LocalPosition: {x: 0, y: 0, z: 0} | |||||
m_LocalScale: {x: 1, y: 1, z: 1} | |||||
m_Children: [] | |||||
m_Father: {fileID: 0} | |||||
m_RootOrder: 0 | |||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} | |||||
--- !u!114 &8236771505572270654 | |||||
MonoBehaviour: | |||||
m_ObjectHideFlags: 0 | |||||
m_CorrespondingSourceObject: {fileID: 0} | |||||
m_PrefabInstance: {fileID: 0} | |||||
m_PrefabAsset: {fileID: 0} | |||||
m_GameObject: {fileID: 8236771505572270655} | |||||
m_Enabled: 1 | |||||
m_EditorHideFlags: 0 | |||||
m_Script: {fileID: 11500000, guid: 5843488507315421aa0a7d92c0604d10, type: 3} | |||||
m_Name: | |||||
m_EditorClassIdentifier: | |||||
url: |
@@ -0,0 +1,7 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 2d6c4899a63004f16bb4f791176f4ad3 | |||||
PrefabImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,30 @@ | |||||
# UniWebView | |||||
Thank you for purchasing UniWebView. | |||||
UniWebView is a Unity3D plugin built on native iOS/Android technology. It helps your users to enjoy web content and | |||||
interact with your game through the web views. | |||||
## Documentation | |||||
To get started, please visit our documentation site: [https://docs.uniwebview.com](https://docs.uniwebview.com). You | |||||
could find step-by-step guides on how to import UniWebView to your project, as well as some basic usage of this asset. | |||||
You could also find a full script reference on the same site in this page: [https://docs.uniwebview.com/api/](https://docs.uniwebview.com/api/). | |||||
## Upgrading | |||||
All purchased customers could get all updates for the same major version for free. Please check the place you have | |||||
purchased this asset to see whether there is an update or not. A release note is also contained in this asset, in the | |||||
"CHANGELOG.md" file. You could also find the same version list in [this page](https://docs.uniwebview.com/release-note). | |||||
## Getting Support | |||||
For frequently asked questions, we have a page to answer them. If you encountered any problems while using UniWebView, | |||||
we strongly suggest to visit the [FAQ page](https://docs.uniwebview.com/guide/faq.html) first. Also feel free to | |||||
[submit a ticket](https://onevcat.atlassian.net/servicedesk/customer/portal/2) if you cannot find a solution, we will | |||||
do our best to get you out! | |||||
## Other | |||||
For more information, please visit the [official web site](https://uniwebview.com) of UniWebView, or contact us through | |||||
a ticket. |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 95440ce70c5e14d6e96e166c45f7cf6d | |||||
timeCreated: 1499302025 | |||||
licenseType: Store | |||||
TextScriptImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: b34f36f9836464e04893f632434d0862 | |||||
folderAsset: yes | |||||
timeCreated: 1491898971 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 43c2ab2efc7244e0293d0a1cb0d869e8 | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -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: |
@@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 598e18fb001004a81960f552978ecf4e | |||||
timeCreated: 1491898971 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 5b17dc622ffd649da85854192714e429 | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,94 @@ | |||||
// | |||||
// UniWebViewAuthenticationCommonFlow.cs | |||||
// Created by Wang Wei (@onevcat) on 2022-06-25. | |||||
// | |||||
// 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.Collections.Generic; | |||||
using UnityEngine; | |||||
/// <summary> | |||||
/// Abstract class and general control for other authentication flows. This class determines the global behaviors of the | |||||
/// authentication flow, such as whether to start authentication as soon as the script `Start`s, and whether to use private | |||||
/// mode to authenticate the user. | |||||
/// This is a super and abstract class for all concrete auth flow. You are not expected to use this class directly. | |||||
/// Instead, to start a customized auth flow, you can use the `UniWebViewAuthenticationFlowCustomize` class. | |||||
/// </summary> | |||||
public abstract class UniWebViewAuthenticationCommonFlow: MonoBehaviour { | |||||
/// <summary> | |||||
/// 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. | |||||
/// | |||||
/// On iOS, this works on iOS 13 and later. | |||||
/// | |||||
/// On Android, it depends on the Chrome version and might require users to enable the incognito mode (and support | |||||
/// for third-party use) in Chrome's settings. Check settings with `chrome://flags/#cct-incognito` and | |||||
/// `chrome://flags/#cct-incognito-available-to-third-party` in Chrome to see the current status. | |||||
/// </summary> | |||||
public bool privateMode; | |||||
// Security. Store the state. | |||||
private string state; | |||||
// Security. Store the code challenge verifier. | |||||
protected string CodeVerify { get; private set; } | |||||
public void Start() { | |||||
if (authorizeOnStart) { | |||||
StartAuthenticationFlow(); | |||||
} | |||||
} | |||||
/// <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() { | |||||
state = UniWebViewAuthenticationUtils.GenerateRandomBase64URLString(); | |||||
return state; | |||||
} | |||||
// 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); | |||||
} | |||||
// Perform verifying for `state`. | |||||
protected void VerifyState(Dictionary<string, string> parameters, string key = "state") { | |||||
if (state == null) { | |||||
throw AuthenticationResponseException.InvalidState; | |||||
} | |||||
if (!parameters.TryGetValue(key, out var stateInResponse) || state != stateInResponse) { | |||||
throw AuthenticationResponseException.InvalidState; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 6bfa5bb9dc237400298563d614b62705 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,356 @@ | |||||
// | |||||
// UniWebViewAuthenticationFlow.cs | |||||
// Created by Wang Wei (@onevcat) on 2022-06-25. | |||||
// | |||||
// 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; | |||||
using UnityEngine.Networking; | |||||
using System.Collections.Generic; | |||||
using System.Collections.Specialized; | |||||
using UnityEngine; | |||||
using UnityEngine.Events; | |||||
/// <summary> | |||||
/// Interface for implementing a custom authentication flow. An authentication flow, in UniWebView, usually a "code" based | |||||
/// OAuth 2.0 flow, contains a standard set of steps: | |||||
/// | |||||
/// 1. User is navigated to a web page that requires authentication; | |||||
/// 2. A temporary code is generated by service provider and provided to client by a redirect URL with customized scheme. | |||||
/// 3. Client requests an access token using the temporary code by performing an "access token" exchange request. | |||||
/// | |||||
/// To use the common flow, any customize authentication flow must implement this interface and becomes a subclass of | |||||
/// UniWebViewAuthenticationCommonFlow. | |||||
/// </summary> | |||||
/// <typeparam name="TTokenType"></typeparam> | |||||
public interface IUniWebViewAuthenticationFlow<TTokenType> | |||||
{ | |||||
/// <summary> | |||||
/// Returns the redirect URL that is used to redirect the user after authenticated. This is used as the `redirect_uri` | |||||
/// parameter when navigating user to the authentication page. | |||||
/// | |||||
/// Usually this is a URL with customize scheme that later service provider may call. It takes intermediate code in its | |||||
/// query and can be used to open the current app in client. The native side of UniWebView will catch and handle it, | |||||
/// then send it to Unity side as the result of `UniWebViewAuthenticationSession`. | |||||
/// </summary> | |||||
/// <returns> | |||||
/// The redirect URL set in the OAuth settings. | |||||
/// </returns> | |||||
string GetCallbackUrl(); | |||||
/// <summary> | |||||
/// Returns the config of the authentication flow. It usually defines the authentication requests entry points. | |||||
/// </summary> | |||||
/// <returns>The config object of an authentication flow.</returns> | |||||
UniWebViewAuthenticationConfiguration GetAuthenticationConfiguration(); | |||||
/// <summary> | |||||
/// Returns a dictionary contains the parameters that are used to perform the authentication request. | |||||
/// The key value pairs in the dictionary are used to construct the query string of the authentication request. | |||||
/// | |||||
/// This usually contains fields like `client_id`, `redirect_uri`, `response_type`, etc. | |||||
/// </summary> | |||||
/// <returns>The dictionary indicates parameters that are used to perform the authentication request.</returns> | |||||
Dictionary<string, string> GetAuthenticationUriArguments(); | |||||
/// <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. | |||||
/// </summary> | |||||
/// <param name="authResponse"> | |||||
/// The response from authentication request. If the authentication succeeds, it is | |||||
/// usually a custom scheme URL with a `code` query as its parameter. Base on this, you could construct the body of the | |||||
/// access token exchange request. | |||||
/// </param> | |||||
/// <returns> | |||||
/// 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. | |||||
/// | |||||
/// When the token exchange request finishes without problem, the response body will be passed to this method and | |||||
/// any conforming class should construct the token object from the response body. | |||||
/// </summary> | |||||
/// <param name="exchangeResponse"> | |||||
/// The body response of the access token exchange request. Usually it contains the desired `access_token` and other | |||||
/// necessary fields to describe the authenticated result. | |||||
/// </param> | |||||
/// <returns> | |||||
/// A token object with `TToken` type that represents the authenticated result. | |||||
/// </returns> | |||||
TTokenType GenerateTokenFromExchangeResponse(string exchangeResponse); | |||||
/// <summary> | |||||
/// 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> | |||||
/// The manager object of an authentication flow. This defines and runs the common flow of an authentication process | |||||
/// with `code` response type. | |||||
/// </summary> | |||||
/// <typeparam name="TTokenType">The responsive token type expected for this authentication flow.</typeparam> | |||||
public class UniWebViewAuthenticationFlow<TTokenType> { | |||||
private IUniWebViewAuthenticationFlow<TTokenType> service; | |||||
public UniWebViewAuthenticationFlow( | |||||
IUniWebViewAuthenticationFlow<TTokenType> service | |||||
) | |||||
{ | |||||
this.service = service; | |||||
} | |||||
/// <summary> | |||||
/// Start the authentication flow. | |||||
/// </summary> | |||||
public void StartAuth() | |||||
{ | |||||
var callbackUri = new Uri(service.GetCallbackUrl()); | |||||
var authUrl = GetAuthUrl(); | |||||
var session = UniWebViewAuthenticationSession.Create(authUrl, callbackUri.Scheme); | |||||
var flow = service as UniWebViewAuthenticationCommonFlow; | |||||
if (flow != null && flow.privateMode) { | |||||
session.SetPrivateMode(true); | |||||
} | |||||
session.OnAuthenticationFinished += (_, resultUrl) => { | |||||
UniWebViewLogger.Instance.Verbose("Auth flow received callback url: " + resultUrl); | |||||
ExchangeToken(resultUrl); | |||||
}; | |||||
session.OnAuthenticationErrorReceived += (_, errorCode, message) => { | |||||
ExchangeTokenErrored(errorCode, message); | |||||
}; | |||||
UniWebViewLogger.Instance.Verbose("Starting auth flow with url: " + authUrl + "; Callback scheme: " + callbackUri.Scheme); | |||||
session.Start(); | |||||
} | |||||
private void ExchangeToken(string response) { | |||||
try { | |||||
var args = service.GetAccessTokenRequestParameters(response); | |||||
var request = GetTokenRequest(args); | |||||
MonoBehaviour context = (MonoBehaviour)service; | |||||
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 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 = new Dictionary<string, string>(); | |||||
foreach (var kv in service.GetAuthenticationUriArguments()) { | |||||
query.Add(kv.Key, kv.Value); | |||||
} | |||||
builder.Query = UniWebViewAuthenticationUtils.CreateQueryString(query); | |||||
return builder.ToString(); | |||||
} | |||||
private UnityWebRequest GetTokenRequest(Dictionary<string, string>args) { | |||||
var builder = new UriBuilder(service.GetAuthenticationConfiguration().tokenEndpoint); | |||||
var form = new WWWForm(); | |||||
foreach (var kv in args) { | |||||
form.AddField(kv.Key, kv.Value); | |||||
} | |||||
return UnityWebRequest.Post(builder.ToString(), form); | |||||
} | |||||
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 ExchangeTokenFinished(TTokenType token) { | |||||
if (service.OnAuthenticationFinished != null) { | |||||
service.OnAuthenticationFinished.Invoke(token); | |||||
} | |||||
service = null; | |||||
} | |||||
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> | |||||
/// The configuration object of an authentication flow. This defines the authentication entry points. | |||||
/// </summary> | |||||
public class UniWebViewAuthenticationConfiguration { | |||||
internal readonly string authorizationEndpoint; | |||||
internal readonly string tokenEndpoint; | |||||
/// <summary> | |||||
/// Creates a new authentication configuration object with the given entry points. | |||||
/// </summary> | |||||
/// <param name="authorizationEndpoint">The entry point to navigate end user to for authentication.</param> | |||||
/// <param name="tokenEndpoint">The entry point which is used to exchange the received code to a valid access token.</param> | |||||
public UniWebViewAuthenticationConfiguration(string authorizationEndpoint, string tokenEndpoint) { | |||||
this.authorizationEndpoint = authorizationEndpoint; | |||||
this.tokenEndpoint = tokenEndpoint; | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// The exception thrown when the authentication flow fails when handling the response. | |||||
/// </summary> | |||||
public class AuthenticationResponseException : Exception { | |||||
/// <summary> | |||||
/// Exception error code to identify the error type. See the static instance of this class to know detail of error codes. | |||||
/// </summary> | |||||
public int Code { get; } | |||||
/// <summary> | |||||
/// Creates an authentication response exception. | |||||
/// </summary> | |||||
/// <param name="code">The error code.</param> | |||||
/// <param name="message">A message that contains error detail.</param> | |||||
public AuthenticationResponseException(int code, string message): base(message) { | |||||
Code = code; | |||||
} | |||||
/// <summary> | |||||
/// An unexpected authentication callback is received. Error code 7001. | |||||
/// </summary> | |||||
public static AuthenticationResponseException UnexpectedAuthCallbackUrl | |||||
= new AuthenticationResponseException(7001, "The received callback url is not expected."); | |||||
/// <summary> | |||||
/// The `state` value in the callback url is not the same as the one in the request. Error code 7002. | |||||
/// </summary> | |||||
public static AuthenticationResponseException InvalidState | |||||
= new AuthenticationResponseException(7002, "The `state` is not valid."); | |||||
/// <summary> | |||||
/// The response is not a valid one. It does not contains a `code` field or cannot be parsed. Error code 7003. | |||||
/// </summary> | |||||
/// <param name="query">The query will be delivered as a part of error message.</param> | |||||
/// <returns>The created response exception that can be thrown out and handled by package user.</returns> | |||||
public static AuthenticationResponseException InvalidResponse(string query) { | |||||
return new AuthenticationResponseException(7003, "The service auth response is not valid: " + query); | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 4add539e6d306455c9da09b069e248bd | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,230 @@ | |||||
// | |||||
// UniWebViewAuthenticationFlowCustomize.cs | |||||
// Created by Wang Wei (@onevcat) on 2022-06-25. | |||||
// | |||||
// 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 UnityEngine; | |||||
using UnityEngine.Events; | |||||
/// <summary> | |||||
/// A customizable authentication flow behavior. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// Besides of the predefined authentication flows, such as Twitter (`UniWebViewAuthenticationFlowTwitter`) or Google | |||||
/// (`UniWebViewAuthenticationFlowGoogle`), this class allows you to determine the details of the authentication flow, | |||||
/// such as entry points, grant types, scopes and more. But similar to other target-specified flows, it follows the same | |||||
/// OAuth 2.0 code auth pattern. | |||||
/// | |||||
/// If you need to support other authentication flows for the platform targets other than the predefined ones, you can | |||||
/// use this class and set all necessary parameters. It runs the standard OAuth 2.0 flow and gives out a | |||||
/// `UniWebViewAuthenticationStandardToken` as the result. | |||||
/// | |||||
/// If you need to support authentication flows other than `code` based OAuth 2.0, try to derive from | |||||
/// `UniWebViewAuthenticationCommonFlow` and implement `IUniWebViewAuthenticationFlow` interface, or even use the | |||||
/// underneath `UniWebViewAuthenticationSession` to get a highly customizable flow. | |||||
/// | |||||
/// </remarks> | |||||
public class UniWebViewAuthenticationFlowCustomize : UniWebViewAuthenticationCommonFlow, IUniWebViewAuthenticationFlow<UniWebViewAuthenticationStandardToken> { | |||||
/// <summary> | |||||
/// The config object which defines the basic information of the authentication flow. | |||||
/// </summary> | |||||
public UniWebViewAuthenticationFlowCustomizeConfig config = new UniWebViewAuthenticationFlowCustomizeConfig(); | |||||
/// <summary> | |||||
/// 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. | |||||
/// | |||||
/// Also remember to add it to the "Auth Callback Urls" field in UniWebView's preference panel. | |||||
/// </summary> | |||||
public string redirectUri = ""; | |||||
/// <summary> | |||||
/// The scope of the authentication request. | |||||
/// </summary> | |||||
public string scope = ""; | |||||
/// <summary> | |||||
/// The optional object which defines some optional parameters of the authentication flow, such as whether supports | |||||
/// `state` or `PKCE`. | |||||
/// </summary> | |||||
public UniWebViewAuthenticationFlowCustomizeOptional optional; | |||||
/// <summary> | |||||
/// Starts the authentication flow with the standard OAuth 2.0. | |||||
/// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`. | |||||
/// </summary> | |||||
public override void StartAuthenticationFlow() { | |||||
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 virtual UniWebViewAuthenticationConfiguration GetAuthenticationConfiguration() { | |||||
return new UniWebViewAuthenticationConfiguration( | |||||
config.authorizationEndpoint, | |||||
config.tokenEndpoint | |||||
); | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public virtual string GetCallbackUrl() { | |||||
return redirectUri; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public virtual Dictionary<string, string> GetAuthenticationUriArguments() { | |||||
var authorizeArgs = new Dictionary<string, string> { | |||||
{ "client_id", clientId }, | |||||
{ "redirect_uri", redirectUri }, | |||||
{ "scope", scope }, | |||||
{ "response_type", config.responseType } | |||||
}; | |||||
if (optional != null) { | |||||
if (optional.enableState) { | |||||
var state = GenerateAndStoreState(); | |||||
authorizeArgs.Add("state", state); | |||||
} | |||||
if (optional.PKCESupport != UniWebViewAuthenticationPKCE.None) { | |||||
var codeChallenge = GenerateCodeChallengeAndStoreCodeVerify(optional.PKCESupport); | |||||
authorizeArgs.Add("code_challenge", codeChallenge); | |||||
var method = UniWebViewAuthenticationUtils.ConvertPKCEToString(optional.PKCESupport); | |||||
authorizeArgs.Add("code_challenge_method", method); | |||||
} | |||||
} | |||||
return authorizeArgs; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public virtual Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) { | |||||
if (!authResponse.StartsWith(redirectUri, StringComparison.InvariantCultureIgnoreCase)) { | |||||
throw AuthenticationResponseException.UnexpectedAuthCallbackUrl; | |||||
} | |||||
var uri = new Uri(authResponse); | |||||
var response = UniWebViewAuthenticationUtils.ParseFormUrlEncodedString(uri.Query); | |||||
if (!response.TryGetValue("code", out var code)) { | |||||
throw AuthenticationResponseException.InvalidResponse(authResponse); | |||||
} | |||||
if (optional != null && optional.enableState) { | |||||
VerifyState(response); | |||||
} | |||||
var parameters = new Dictionary<string, string> { | |||||
{ "client_id", clientId }, | |||||
{ "code", code }, | |||||
{ "redirect_uri", redirectUri }, | |||||
{ "grant_type", config.grantType }, | |||||
}; | |||||
if (CodeVerify != null) { | |||||
parameters.Add("code_verifier", CodeVerify); | |||||
} | |||||
if (optional != null && !String.IsNullOrEmpty(optional.clientSecret)) { | |||||
parameters.Add("client_secret", optional.clientSecret); | |||||
} | |||||
return parameters; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
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); | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
[field: SerializeField] | |||||
public UnityEvent<UniWebViewAuthenticationStandardToken> 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<UniWebViewAuthenticationStandardToken> OnRefreshTokenFinished { get; set; } | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
[field: SerializeField] | |||||
public UnityEvent<long, string> OnRefreshTokenErrored { get; set; } | |||||
} | |||||
[Serializable] | |||||
public class UniWebViewAuthenticationFlowCustomizeConfig { | |||||
public string authorizationEndpoint = ""; | |||||
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 = ""; | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: bfcc799dc1a0c4ef58bc3486a8af7afa | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,222 @@ | |||||
// | |||||
// UniWebViewAuthenticationFlowDiscord.cs | |||||
// Created by Wang Wei (@onevcat) on 2022-06-25. | |||||
// | |||||
// 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 UnityEngine; | |||||
using UnityEngine.Events; | |||||
/// <summary> | |||||
/// A predefined authentication flow for Discord. | |||||
/// | |||||
/// This implementation follows the flow described here: | |||||
/// https://discord.com/developers/docs/topics/oauth2 | |||||
/// | |||||
/// See https://docs.uniwebview.com/guide/oauth2.html for a more detailed guide of authentication in UniWebView. | |||||
/// </summary> | |||||
public class UniWebViewAuthenticationFlowDiscord : UniWebViewAuthenticationCommonFlow, IUniWebViewAuthenticationFlow<UniWebViewAuthenticationDiscordToken> { | |||||
/// <summary> | |||||
/// The client ID of your Discord application. | |||||
/// </summary> | |||||
public string clientId = ""; | |||||
/// <summary> | |||||
/// The client secret of your Discord application. | |||||
/// </summary> | |||||
public string clientSecret = ""; | |||||
/// <summary> | |||||
/// The redirect URI of this Discord application. | |||||
/// </summary> | |||||
public string redirectUri = ""; | |||||
/// <summary> | |||||
/// The scope string of all your required scopes. | |||||
/// </summary> | |||||
public string scope = ""; | |||||
/// <summary> | |||||
/// Optional to control this flow's behaviour. | |||||
/// </summary> | |||||
public UniWebViewAuthenticationFlowDiscordOptional optional; | |||||
private const string responseType = "code"; | |||||
private const string grantType = "authorization_code"; | |||||
private readonly UniWebViewAuthenticationConfiguration config = | |||||
new UniWebViewAuthenticationConfiguration( | |||||
"https://discord.com/api/oauth2/authorize", | |||||
"https://discord.com/api/oauth2/token" | |||||
); | |||||
/// <summary> | |||||
/// Starts the authentication flow with the standard OAuth 2.0. | |||||
/// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`. | |||||
/// </summary> | |||||
public override void StartAuthenticationFlow() { | |||||
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> | |||||
public UniWebViewAuthenticationConfiguration GetAuthenticationConfiguration() { | |||||
return config; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public string GetCallbackUrl() { | |||||
return redirectUri; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public Dictionary<string, string> GetAuthenticationUriArguments() { | |||||
var authorizeArgs = new Dictionary<string, string> { | |||||
{ "client_id", clientId }, | |||||
{ "redirect_uri", redirectUri }, | |||||
{ "scope", scope }, | |||||
{ "response_type", responseType } | |||||
}; | |||||
if (optional != null) { | |||||
if (optional.enableState) { | |||||
var state = GenerateAndStoreState(); | |||||
authorizeArgs.Add("state", state); | |||||
} | |||||
if (optional.PKCESupport != UniWebViewAuthenticationPKCE.None) { | |||||
var codeChallenge = GenerateCodeChallengeAndStoreCodeVerify(optional.PKCESupport); | |||||
authorizeArgs.Add("code_challenge", codeChallenge); | |||||
var method = UniWebViewAuthenticationUtils.ConvertPKCEToString(optional.PKCESupport); | |||||
authorizeArgs.Add("code_challenge_method", method); | |||||
} | |||||
} | |||||
return authorizeArgs; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) { | |||||
if (!authResponse.StartsWith(redirectUri, StringComparison.InvariantCultureIgnoreCase)) { | |||||
throw AuthenticationResponseException.UnexpectedAuthCallbackUrl; | |||||
} | |||||
var uri = new Uri(authResponse); | |||||
var response = UniWebViewAuthenticationUtils.ParseFormUrlEncodedString(uri.Query); | |||||
if (!response.TryGetValue("code", out var code)) { | |||||
throw AuthenticationResponseException.InvalidResponse(authResponse); | |||||
} | |||||
if (optional.enableState) { | |||||
VerifyState(response); | |||||
} | |||||
var parameters = new Dictionary<string, string> { | |||||
{ "client_id", clientId }, | |||||
{ "client_secret", clientSecret }, | |||||
{ "code", code }, | |||||
{ "redirect_uri", redirectUri }, | |||||
{ "grant_type", grantType }, | |||||
}; | |||||
if (CodeVerify != null) { | |||||
parameters.Add("code_verifier", CodeVerify); | |||||
} | |||||
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> | |||||
public UniWebViewAuthenticationDiscordToken GenerateTokenFromExchangeResponse(string exchangeResponse) { | |||||
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> | |||||
/// The authentication flow's optional settings for Discord. | |||||
/// </summary> | |||||
[Serializable] | |||||
public class UniWebViewAuthenticationFlowDiscordOptional { | |||||
/// <summary> | |||||
/// Whether to enable PKCE when performing authentication. On mobile platforms, this has to be enabled as `S256`, | |||||
/// otherwise, Discord will reject the authentication request. | |||||
/// </summary> | |||||
public UniWebViewAuthenticationPKCE PKCESupport = UniWebViewAuthenticationPKCE.S256; | |||||
/// <summary> | |||||
/// Whether to enable the state verification. If enabled, the state will be generated and verified in the | |||||
/// authentication callback. Default is `true`. | |||||
/// </summary> | |||||
public bool enableState = true; | |||||
} | |||||
/// <summary> | |||||
/// The token object from Discord. Check `UniWebViewAuthenticationStandardToken` for more. | |||||
/// </summary> | |||||
public class UniWebViewAuthenticationDiscordToken : UniWebViewAuthenticationStandardToken { } | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 69ed5d1df2635470ca5c939d11d16924 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,217 @@ | |||||
// | |||||
// UniWebViewAuthenticationFlowFacebook.cs | |||||
// Created by Wang Wei (@onevcat) on 2022-06-25. | |||||
// | |||||
// 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.Collections.Specialized; | |||||
using UnityEngine; | |||||
using UnityEngine.Events; | |||||
/// <summary> | |||||
/// A predefined authentication flow for Facebook Login. | |||||
/// | |||||
/// It is not a standard OAuth2 flow, and using a plain web view. There once was a policy that Facebook did not allow | |||||
/// any third-party customize authentication flow other than using their official SDK. Recently Facebook started to provide | |||||
/// a so-called manual flow way to perform authentication. But it is originally only for Desktop apps, it is not stable | |||||
/// and not standard. | |||||
/// | |||||
/// Facebook suggests "For mobile apps, use the Facebook SDKs for iOS and Android, and follow the separate guides for | |||||
/// these platforms." So on mobile, use this class with your own risk since it might be invalidated or forbidden by | |||||
/// Facebook in the future. | |||||
/// | |||||
/// This implementation is based on the manual flow described in the following document: | |||||
/// https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow | |||||
/// | |||||
/// See https://docs.uniwebview.com/guide/oauth2.html for a more detailed guide of authentication in UniWebView. | |||||
/// </summary> | |||||
public class UniWebViewAuthenticationFlowFacebook: UniWebViewAuthenticationCommonFlow { | |||||
/// <summary> | |||||
/// The App ID of your Facebook application | |||||
/// </summary> | |||||
public string appId = ""; | |||||
/// <summary> | |||||
/// Optional to control this flow's behaviour. | |||||
/// </summary> | |||||
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 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 const string responseType = "token"; | |||||
[field: SerializeField] | |||||
public UnityEvent<UniWebViewAuthenticationFacebookToken> OnAuthenticationFinished { get; set; } | |||||
[field: SerializeField] | |||||
public UnityEvent<long, string> OnAuthenticationErrored { get; set; } | |||||
private readonly UniWebViewAuthenticationConfiguration config = | |||||
new UniWebViewAuthenticationConfiguration( | |||||
"https://www.facebook.com/v14.0/dialog/oauth", | |||||
// This `access_token` entry point is in fact not used in current auth model. | |||||
"https://graph.facebook.com/v14.0/oauth/access_token" | |||||
); | |||||
/// <summary> | |||||
/// Starts the authentication flow. | |||||
/// | |||||
/// This flow is executed in a customized web view and it is not a standard OAuth2 flow. | |||||
/// </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) { | |||||
OnAuthenticationErrored.Invoke(status, "Error while loading auth page."); | |||||
} | |||||
webView.Hide(false, UniWebViewTransitionEdge.Bottom, 0.25f, () => { | |||||
Destroy(webView); | |||||
}); | |||||
return; | |||||
} | |||||
if (url.StartsWith(redirectUri)) { | |||||
UniWebViewLogger.Instance.Info("Received redirect url: " + url); | |||||
var uri = new Uri(url); | |||||
var response = UniWebViewAuthenticationUtils.ParseFormUrlEncodedString(uri.Fragment); | |||||
try { | |||||
VerifyState(response); | |||||
var token = new UniWebViewAuthenticationFacebookToken(url, response); | |||||
if (OnAuthenticationFinished != null) { | |||||
OnAuthenticationFinished.Invoke(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 response: " + e + ". Code: " + code + | |||||
". Message: " + message); | |||||
if (OnAuthenticationErrored != null) { | |||||
OnAuthenticationErrored.Invoke(code, message); | |||||
} | |||||
} | |||||
finally { | |||||
webView.Hide(false, UniWebViewTransitionEdge.Bottom, 0.25f, () => { | |||||
Destroy(webView); | |||||
}); | |||||
} | |||||
} | |||||
}; | |||||
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.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 = new Dictionary<string, string>(); | |||||
foreach (var kv in GetAuthenticationUriArguments()) { | |||||
query.Add(kv.Key, kv.Value); | |||||
} | |||||
builder.Query = UniWebViewAuthenticationUtils.CreateQueryString(query); | |||||
return builder.ToString(); | |||||
} | |||||
private Dictionary<string, string> GetAuthenticationUriArguments() { | |||||
var state = GenerateAndStoreState(); | |||||
var authorizeArgs = new Dictionary<string, string> { | |||||
{ "client_id", appId }, | |||||
{ "redirect_uri", redirectUri }, | |||||
{ "state", state}, | |||||
{ "response_type", responseType } | |||||
}; | |||||
if (optional != null) { | |||||
if (!String.IsNullOrEmpty(optional.scope)) { | |||||
authorizeArgs.Add("scope", optional.scope); | |||||
} | |||||
} | |||||
return authorizeArgs; | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// The authentication flow's optional settings for Facebook. | |||||
/// </summary> | |||||
[Serializable] | |||||
public class UniWebViewAuthenticationFlowFacebookOptional { | |||||
/// <summary> | |||||
/// The scope string of all your required scopes. | |||||
/// </summary> | |||||
public string scope = ""; | |||||
} | |||||
/// The token object from Facebook. | |||||
public class UniWebViewAuthenticationFacebookToken { | |||||
/// <summary> | |||||
/// The access token received from Facebook Login. | |||||
/// </summary> | |||||
public string AccessToken { get; } | |||||
/// <summary> | |||||
/// The expiration duration that your app can access requested items of user data. | |||||
/// </summary> | |||||
public long DataAccessExpirationTime { get; } | |||||
/// <summary> | |||||
/// The expiration duration that the access token is valid for authentication purpose. | |||||
/// </summary> | |||||
public long ExpiresIn { get; } | |||||
/// <summary> | |||||
/// The raw value of the response of the exchange token request. | |||||
/// If the predefined fields are not enough, you can parse the raw value to get the extra information. | |||||
/// </summary> | |||||
public string RawValue { get; } | |||||
public UniWebViewAuthenticationFacebookToken(string response, Dictionary<string, string> values) { | |||||
RawValue = response; | |||||
AccessToken = values.ContainsKey("access_token") ? values["access_token"] : null ; | |||||
if (AccessToken == null) { | |||||
throw AuthenticationResponseException.InvalidResponse(response); | |||||
} | |||||
DataAccessExpirationTime = values.ContainsKey("data_access_expiration_time") ? long.Parse(values["data_access_expiration_time"]) : 0; | |||||
ExpiresIn = values.ContainsKey("expires_in") ? long.Parse(values["expires_in"]) : 0; | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: ba2d51a8adc8e42b4aaff50218dfe058 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,255 @@ | |||||
// | |||||
// UniWebViewAuthenticationFlowGitHub.cs | |||||
// Created by Wang Wei (@onevcat) on 2022-06-25. | |||||
// | |||||
// 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 UnityEngine; | |||||
using UnityEngine.Events; | |||||
using System.Collections.Generic; | |||||
using System; | |||||
/// <summary> | |||||
/// A predefined authentication flow for GitHub. | |||||
/// | |||||
/// This implementation follows the flow described here: | |||||
/// https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps | |||||
/// | |||||
/// See https://docs.uniwebview.com/guide/oauth2.html for a more detailed guide of authentication in UniWebView. | |||||
/// </summary> | |||||
public class UniWebViewAuthenticationFlowGitHub: UniWebViewAuthenticationCommonFlow, IUniWebViewAuthenticationFlow<UniWebViewAuthenticationGitHubToken> { | |||||
/// <summary> | |||||
/// The client ID of your GitHub application. | |||||
/// </summary> | |||||
public string clientId = ""; | |||||
/// <summary> | |||||
/// The client secret of your GitHub application. | |||||
/// </summary> | |||||
public string clientSecret = ""; | |||||
/// <summary> | |||||
/// The callback URL of your GitHub application. | |||||
/// </summary> | |||||
public string callbackUrl = ""; | |||||
/// <summary> | |||||
/// Optional to control this flow's behaviour. | |||||
/// </summary> | |||||
public UniWebViewAuthenticationFlowGitHubOptional optional; | |||||
private readonly UniWebViewAuthenticationConfiguration config = | |||||
new UniWebViewAuthenticationConfiguration( | |||||
"https://github.com/login/oauth/authorize", | |||||
"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. | |||||
/// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`. | |||||
/// </summary> | |||||
public override void StartAuthenticationFlow() { | |||||
var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationGitHubToken>(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<UniWebViewAuthenticationGitHubToken>(this); | |||||
flow.RefreshToken(refreshToken); | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public Dictionary<string, string> GetAuthenticationUriArguments() { | |||||
var authorizeArgs = new Dictionary<string, string> { { "client_id", clientId } }; | |||||
if (optional != null) { | |||||
if (!String.IsNullOrEmpty(optional.redirectUri)) { | |||||
authorizeArgs.Add("redirect_uri", optional.redirectUri); | |||||
} | |||||
if (!String.IsNullOrEmpty(optional.login)) { | |||||
authorizeArgs.Add("login", optional.login); | |||||
} | |||||
if (!String.IsNullOrEmpty(optional.scope)) { | |||||
authorizeArgs.Add("scope", optional.scope); | |||||
} | |||||
if (optional.enableState) { | |||||
var state = GenerateAndStoreState(); | |||||
authorizeArgs.Add("state", state); | |||||
} | |||||
if (!optional.allowSignup) { // The default value is true. | |||||
authorizeArgs.Add("allow_signup", "false"); | |||||
} | |||||
} | |||||
return authorizeArgs; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public string GetCallbackUrl() { | |||||
return callbackUrl; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public UniWebViewAuthenticationConfiguration GetAuthenticationConfiguration() { | |||||
return config; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) { | |||||
if (!authResponse.StartsWith(callbackUrl, StringComparison.InvariantCultureIgnoreCase)) { | |||||
throw AuthenticationResponseException.UnexpectedAuthCallbackUrl; | |||||
} | |||||
var uri = new Uri(authResponse); | |||||
var response = UniWebViewAuthenticationUtils.ParseFormUrlEncodedString(uri.Query); | |||||
if (!response.TryGetValue("code", out var code)) { | |||||
throw AuthenticationResponseException.InvalidResponse(authResponse); | |||||
} | |||||
if (optional.enableState) { | |||||
VerifyState(response); | |||||
} | |||||
var result = new Dictionary<string, string> { | |||||
{ "client_id", clientId }, | |||||
{ "client_secret", clientSecret }, | |||||
{ "code", code } | |||||
}; | |||||
if (optional != null && String.IsNullOrEmpty(optional.redirectUri)) { | |||||
result.Add("redirect_uri", optional.redirectUri); | |||||
} | |||||
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> | |||||
public UniWebViewAuthenticationGitHubToken GenerateTokenFromExchangeResponse(string exchangeResponse) { | |||||
return new UniWebViewAuthenticationGitHubToken(exchangeResponse); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// The authentication flow's optional settings for GitHub. | |||||
/// </summary> | |||||
[Serializable] | |||||
public class UniWebViewAuthenticationFlowGitHubOptional { | |||||
/// <summary> | |||||
/// The redirect URI should be used in exchange token request. | |||||
/// </summary> | |||||
public string redirectUri = ""; | |||||
/// <summary> | |||||
/// Suggests a specific account to use for signing in and authorizing the app. | |||||
/// </summary> | |||||
public string login = ""; | |||||
/// <summary> | |||||
/// The scope string of all your required scopes. | |||||
/// </summary> | |||||
public string scope = ""; | |||||
/// <summary> | |||||
/// Whether to enable the state verification. If enabled, the state will be generated and verified in the | |||||
/// authentication callback. | |||||
/// </summary> | |||||
public bool enableState = false; | |||||
/// <summary> | |||||
/// Whether unauthenticated users will be offered an option to sign up for GitHub during the OAuth flow. | |||||
/// </summary> | |||||
public bool allowSignup = true; | |||||
} | |||||
/// <summary> | |||||
/// The token object from GitHub. | |||||
/// </summary> | |||||
public class UniWebViewAuthenticationGitHubToken { | |||||
/// <summary>The access token retrieved from the service provider.</summary> | |||||
public string AccessToken { get; } | |||||
/// <summary>The granted scopes of the token.</summary> | |||||
public string Scope { get; } | |||||
/// <summary>The token type. Usually `bearer`.</summary> | |||||
public string TokenType { get; } | |||||
/// <summary>The refresh token retrieved from the service provider.</summary> | |||||
public string RefreshToken { get; } | |||||
/// <summary>Expiration duration for the refresh token.</summary> | |||||
public long RefreshTokenExpiresIn { get; } | |||||
/// <summary> | |||||
/// The raw value of the response of the exchange token request. | |||||
/// If the predefined fields are not enough, you can parse the raw value to get the extra information. | |||||
/// </summary> | |||||
public string RawValue { get; } | |||||
public UniWebViewAuthenticationGitHubToken(string result) { | |||||
RawValue = result; | |||||
var values = UniWebViewAuthenticationUtils.ParseFormUrlEncodedString(result); | |||||
AccessToken = values.ContainsKey("access_token") ? values["access_token"] : null ; | |||||
if (AccessToken == null) { | |||||
throw AuthenticationResponseException.InvalidResponse(result); | |||||
} | |||||
Scope = values.ContainsKey("scope") ? values["scope"] : null; | |||||
TokenType = values.ContainsKey("token_type") ? values["token_type"] : null; | |||||
RefreshToken = values.ContainsKey("refresh_token") ? values["refresh_token"] : null; | |||||
RefreshTokenExpiresIn = values.ContainsKey("refresh_token_expires_in") ? long.Parse(values["refresh_token_expires_in"]) : 0; | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 1925cdd8b9d284ec48146d7d03c41672 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,231 @@ | |||||
// | |||||
// UniWebViewAuthenticationFlowGoogle.cs | |||||
// Created by Wang Wei (@onevcat) on 2022-06-25. | |||||
// | |||||
// 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 UnityEngine; | |||||
using UnityEngine.Events; | |||||
/// <summary> | |||||
/// A predefined authentication flow for Google Identity. | |||||
/// | |||||
/// This implementation follows the flow described here: | |||||
/// https://developers.google.com/identity/protocols/oauth2/native-app | |||||
/// | |||||
/// Google authentication flow is a bit different from the other standard authentication flows. Please read the link | |||||
/// above carefully to understand it. | |||||
/// | |||||
/// See https://docs.uniwebview.com/guide/oauth2.html for a more detailed guide of authentication in UniWebView. | |||||
/// </summary> | |||||
public class UniWebViewAuthenticationFlowGoogle : UniWebViewAuthenticationCommonFlow, IUniWebViewAuthenticationFlow<UniWebViewAuthenticationGoogleToken> { | |||||
/// <summary> | |||||
/// The client ID of your Google application. | |||||
/// </summary> | |||||
public string clientId = ""; | |||||
/// <summary> | |||||
/// The redirect URI of your Google application. | |||||
/// | |||||
/// It might be something like "com.googleusercontent.apps.${clientId}:${redirect_uri_path}". Be caution that the URI does not | |||||
/// contain regular double slashes `//`, but should be only one. | |||||
/// </summary> | |||||
public string redirectUri = ""; | |||||
/// <summary> | |||||
/// The scope of your Google application. | |||||
/// | |||||
/// It might be some full URL in recent Google services, such as "https://www.googleapis.com/auth/userinfo.profile" | |||||
/// </summary> | |||||
public string scope = ""; | |||||
/// <summary> | |||||
/// Optional to control this flow's behaviour. | |||||
/// </summary> | |||||
public UniWebViewAuthenticationFlowGoogleOptional optional; | |||||
private const string responseType = "code"; | |||||
private const string grantType = "authorization_code"; | |||||
private readonly UniWebViewAuthenticationConfiguration config = | |||||
new UniWebViewAuthenticationConfiguration( | |||||
"https://accounts.google.com/o/oauth2/v2/auth", | |||||
"https://oauth2.googleapis.com/token" | |||||
); | |||||
/// <summary> | |||||
/// Starts the authentication flow with the standard OAuth 2.0. | |||||
/// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`. | |||||
/// </summary> | |||||
public override void StartAuthenticationFlow() { | |||||
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> | |||||
public UniWebViewAuthenticationConfiguration GetAuthenticationConfiguration() { | |||||
return config; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public string GetCallbackUrl() { | |||||
return redirectUri; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public Dictionary<string, string> GetAuthenticationUriArguments() { | |||||
var authorizeArgs = new Dictionary<string, string> { | |||||
{ "client_id", clientId }, | |||||
{ "redirect_uri", redirectUri }, | |||||
{ "scope", scope }, | |||||
{ "response_type", responseType } | |||||
}; | |||||
if (optional != null) { | |||||
if (optional.enableState) { | |||||
var state = GenerateAndStoreState(); | |||||
authorizeArgs.Add("state", state); | |||||
} | |||||
if (optional.PKCESupport != UniWebViewAuthenticationPKCE.None) { | |||||
var codeChallenge = GenerateCodeChallengeAndStoreCodeVerify(optional.PKCESupport); | |||||
authorizeArgs.Add("code_challenge", codeChallenge); | |||||
var method = UniWebViewAuthenticationUtils.ConvertPKCEToString(optional.PKCESupport); | |||||
authorizeArgs.Add("code_challenge_method", method); | |||||
} | |||||
if (!String.IsNullOrEmpty(optional.loginHint)) { | |||||
authorizeArgs.Add("login_hint", optional.loginHint); | |||||
} | |||||
} | |||||
return authorizeArgs; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) { | |||||
if (!authResponse.StartsWith(redirectUri, StringComparison.InvariantCultureIgnoreCase)) { | |||||
throw AuthenticationResponseException.UnexpectedAuthCallbackUrl; | |||||
} | |||||
var uri = new Uri(authResponse); | |||||
var response = UniWebViewAuthenticationUtils.ParseFormUrlEncodedString(uri.Query); | |||||
if (!response.TryGetValue("code", out var code)) { | |||||
throw AuthenticationResponseException.InvalidResponse(authResponse); | |||||
} | |||||
if (optional.enableState) { | |||||
VerifyState(response); | |||||
} | |||||
var parameters = new Dictionary<string, string> { | |||||
{ "client_id", clientId }, | |||||
{ "code", code }, | |||||
{ "redirect_uri", redirectUri }, | |||||
{ "grant_type", grantType }, | |||||
}; | |||||
if (CodeVerify != null) { | |||||
parameters.Add("code_verifier", CodeVerify); | |||||
} | |||||
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> | |||||
public UniWebViewAuthenticationGoogleToken GenerateTokenFromExchangeResponse(string exchangeResponse) { | |||||
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> | |||||
/// The authentication flow's optional settings for Google. | |||||
/// </summary> | |||||
[Serializable] | |||||
public class UniWebViewAuthenticationFlowGoogleOptional { | |||||
/// <summary> | |||||
/// Whether to enable PKCE when performing authentication. Default is `S256`. | |||||
/// </summary> | |||||
public UniWebViewAuthenticationPKCE PKCESupport = UniWebViewAuthenticationPKCE.S256; | |||||
/// <summary> | |||||
/// Whether to enable the state verification. If enabled, the state will be generated and verified in the | |||||
/// authentication callback. Default is `true`. | |||||
/// </summary> | |||||
public bool enableState = true; | |||||
/// <summary> | |||||
/// If your application knows which user is trying to authenticate, it can use this parameter to provide a hint to | |||||
/// the Google Authentication Server. | |||||
/// </summary> | |||||
public string loginHint = ""; | |||||
} | |||||
/// <summary> | |||||
/// The token object from Google. Check `UniWebViewAuthenticationStandardToken` for more. | |||||
/// </summary> | |||||
public class UniWebViewAuthenticationGoogleToken : UniWebViewAuthenticationStandardToken { } | |||||
@@ -0,0 +1,3 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 9a51fd6394dc49ed954d995b9f7b0bbc | |||||
timeCreated: 1655992593 |
@@ -0,0 +1,227 @@ | |||||
// | |||||
// UniWebViewAuthenticationFlowLine.cs | |||||
// Created by Wang Wei (@onevcat) on 2022-06-25. | |||||
// | |||||
// 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.Collections.Specialized; | |||||
using UnityEngine; | |||||
using UnityEngine.Events; | |||||
/// <summary> | |||||
/// A predefined authentication flow LINE Login. | |||||
/// | |||||
/// This implementation follows the flow described here: | |||||
/// https://developers.line.biz/en/reference/line-login/ | |||||
/// | |||||
/// Google authentication flow is a bit different from the other standard authentication flows. Please read the link | |||||
/// above carefully to understand it. | |||||
/// | |||||
/// See https://docs.uniwebview.com/guide/oauth2.html for a more detailed guide of authentication in UniWebView. | |||||
/// </summary> | |||||
public class UniWebViewAuthenticationFlowLine : UniWebViewAuthenticationCommonFlow, IUniWebViewAuthenticationFlow<UniWebViewAuthenticationLineToken> { | |||||
/// <summary> | |||||
/// The client ID (Channel ID) of your LINE Login application. | |||||
/// </summary> | |||||
public string clientId = ""; | |||||
/// <summary> | |||||
/// The iOS bundle Id you set in LINE developer console. | |||||
/// </summary> | |||||
public string iOSBundleId = ""; | |||||
/// <summary> | |||||
/// The Android package name you set in LINE developer console. | |||||
/// </summary> | |||||
public string androidPackageName = ""; | |||||
/// <summary> | |||||
/// The scope of your LINE application. | |||||
/// </summary> | |||||
public string scope = ""; | |||||
/// <summary> | |||||
/// Optional to control this flow's behaviour. | |||||
/// </summary> | |||||
public UniWebViewAuthenticationFlowLineOptional optional; | |||||
private const string responseType = "code"; | |||||
private const string grantType = "authorization_code"; | |||||
private string RedirectUri { | |||||
get { | |||||
if (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.OSXEditor) { | |||||
return $"line3rdp.{iOSBundleId}://auth"; | |||||
} | |||||
if (Application.platform == RuntimePlatform.Android) { | |||||
return "intent://auth#Intent;package=" + androidPackageName + ";scheme=lineauth;end"; | |||||
} | |||||
UniWebViewLogger.Instance.Critical("Not supported platform for LINE Login."); | |||||
return ""; | |||||
} | |||||
} | |||||
private readonly UniWebViewAuthenticationConfiguration config = | |||||
new UniWebViewAuthenticationConfiguration( | |||||
"https://access.line.me/oauth2/v2.1/login", | |||||
"https://api.line.me/oauth2/v2.1/token" | |||||
); | |||||
/// <summary> | |||||
/// Starts the authentication flow with the standard OAuth 2.0. | |||||
/// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`. | |||||
/// </summary> | |||||
public override void StartAuthenticationFlow() { | |||||
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> | |||||
public UniWebViewAuthenticationConfiguration GetAuthenticationConfiguration() { | |||||
return config; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public string GetCallbackUrl() { | |||||
return RedirectUri; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public Dictionary<string, string> GetAuthenticationUriArguments() { | |||||
var authorizeArgs = new Dictionary<string, string> { | |||||
{ "loginChannelId", clientId }, | |||||
{ "returnUri", GenerateReturnUri() }, | |||||
}; | |||||
return authorizeArgs; | |||||
} | |||||
private string GenerateReturnUri() { | |||||
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); | |||||
if (!String.IsNullOrEmpty(scope)) { | |||||
query.Add("scope", scope); | |||||
} else { | |||||
query.Add("scope", "profile"); | |||||
} | |||||
if (optional != null) { | |||||
if (optional.PKCESupport != UniWebViewAuthenticationPKCE.None) { | |||||
var codeChallenge = GenerateCodeChallengeAndStoreCodeVerify(optional.PKCESupport); | |||||
query.Add("code_challenge", codeChallenge); | |||||
var method = UniWebViewAuthenticationUtils.ConvertPKCEToString(optional.PKCESupport); | |||||
query.Add("code_challenge_method", method); | |||||
} | |||||
} | |||||
return "/oauth2/v2.1/authorize/consent?" + UniWebViewAuthenticationUtils.CreateQueryString(query); | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) { | |||||
var normalizedRedirectUri = UniWebViewAuthenticationUtils.ConvertIntentUri(RedirectUri); | |||||
if (!authResponse.StartsWith(normalizedRedirectUri, StringComparison.InvariantCultureIgnoreCase)) { | |||||
throw AuthenticationResponseException.UnexpectedAuthCallbackUrl; | |||||
} | |||||
var uri = new Uri(authResponse); | |||||
var response = UniWebViewAuthenticationUtils.ParseFormUrlEncodedString(uri.Query); | |||||
VerifyState(response); | |||||
if (!response.TryGetValue("code", out var code)) { | |||||
throw AuthenticationResponseException.InvalidResponse(authResponse); | |||||
} | |||||
var parameters = new Dictionary<string, string> { | |||||
{ "client_id", clientId }, | |||||
{ "code", code }, | |||||
{ "redirect_uri", RedirectUri }, | |||||
{ "grant_type", grantType }, | |||||
}; | |||||
if (CodeVerify != null) { | |||||
parameters.Add("code_verifier", CodeVerify); | |||||
} | |||||
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> | |||||
public UniWebViewAuthenticationLineToken GenerateTokenFromExchangeResponse(string exchangeResponse) { | |||||
return UniWebViewAuthenticationTokenFactory<UniWebViewAuthenticationLineToken>.Parse(exchangeResponse); | |||||
} | |||||
[field: SerializeField] | |||||
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> | |||||
/// The authentication flow's optional settings for LINE. | |||||
/// </summary> | |||||
[Serializable] | |||||
public class UniWebViewAuthenticationFlowLineOptional { | |||||
/// <summary> | |||||
/// Whether to enable PKCE when performing authentication. Default is `S256`. | |||||
/// </summary> | |||||
public UniWebViewAuthenticationPKCE PKCESupport = UniWebViewAuthenticationPKCE.S256; | |||||
} | |||||
/// <summary> | |||||
/// The token object from LINE. Check `UniWebViewAuthenticationStandardToken` for more. | |||||
/// </summary> | |||||
public class UniWebViewAuthenticationLineToken : UniWebViewAuthenticationStandardToken { } |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 69f3e0ea73db84403850bcdbc6531a62 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,211 @@ | |||||
// | |||||
// UniWebViewAuthenticationFlowTwitter.cs | |||||
// Created by Wang Wei (@onevcat) on 2022-06-25. | |||||
// | |||||
// 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 UnityEngine; | |||||
using UnityEngine.Events; | |||||
/// <summary> | |||||
/// A predefined authentication flow for Twitter. | |||||
/// | |||||
/// This implementation follows the flow described here: | |||||
/// https://developer.twitter.com/en/docs/authentication/oauth-2-0/authorization-code | |||||
/// | |||||
/// See https://docs.uniwebview.com/guide/oauth2.html for a more detailed guide of authentication in UniWebView. | |||||
/// </summary> | |||||
public class UniWebViewAuthenticationFlowTwitter : UniWebViewAuthenticationCommonFlow, IUniWebViewAuthenticationFlow<UniWebViewAuthenticationTwitterToken> { | |||||
/// <summary> | |||||
/// The client ID of your Twitter application. | |||||
/// </summary> | |||||
public string clientId = ""; | |||||
/// <summary> | |||||
/// The redirect URI of your Twitter application. | |||||
/// </summary> | |||||
public string redirectUri = ""; | |||||
/// <summary> | |||||
/// The scope string of all your required scopes. | |||||
/// </summary> | |||||
public string scope = ""; | |||||
/// <summary> | |||||
/// Optional to control this flow's behaviour. | |||||
/// </summary> | |||||
public UniWebViewAuthenticationFlowTwitterOptional optional; | |||||
private const string responseType = "code"; | |||||
private const string grantType = "authorization_code"; | |||||
private readonly UniWebViewAuthenticationConfiguration config = | |||||
new UniWebViewAuthenticationConfiguration( | |||||
"https://twitter.com/i/oauth2/authorize", | |||||
"https://api.twitter.com/2/oauth2/token" | |||||
); | |||||
/// <summary> | |||||
/// Starts the authentication flow with the standard OAuth 2.0. | |||||
/// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`. | |||||
/// </summary> | |||||
public override void StartAuthenticationFlow() { | |||||
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> | |||||
public UniWebViewAuthenticationConfiguration GetAuthenticationConfiguration() { | |||||
return config; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public string GetCallbackUrl() { | |||||
return redirectUri; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public Dictionary<string, string> GetAuthenticationUriArguments() { | |||||
var authorizeArgs = new Dictionary<string, string> { | |||||
{ "client_id", clientId }, | |||||
{ "redirect_uri", redirectUri }, | |||||
{ "scope", scope }, | |||||
{ "response_type", responseType } | |||||
}; | |||||
if (optional != null) { | |||||
if (optional.enableState) { | |||||
var state = GenerateAndStoreState(); | |||||
authorizeArgs.Add("state", state); | |||||
} | |||||
if (optional.PKCESupport != UniWebViewAuthenticationPKCE.None) { | |||||
var codeChallenge = GenerateCodeChallengeAndStoreCodeVerify(optional.PKCESupport); | |||||
authorizeArgs.Add("code_challenge", codeChallenge); | |||||
var method = UniWebViewAuthenticationUtils.ConvertPKCEToString(optional.PKCESupport); | |||||
authorizeArgs.Add("code_challenge_method", method); | |||||
} | |||||
} | |||||
return authorizeArgs; | |||||
} | |||||
/// <summary> | |||||
/// Implements required method in `IUniWebViewAuthenticationFlow`. | |||||
/// </summary> | |||||
public Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) { | |||||
if (!authResponse.StartsWith(redirectUri, StringComparison.InvariantCultureIgnoreCase)) { | |||||
throw AuthenticationResponseException.UnexpectedAuthCallbackUrl; | |||||
} | |||||
var uri = new Uri(authResponse); | |||||
var response = UniWebViewAuthenticationUtils.ParseFormUrlEncodedString(uri.Query); | |||||
if (!response.TryGetValue("code", out var code)) { | |||||
throw AuthenticationResponseException.InvalidResponse(authResponse); | |||||
} | |||||
if (optional.enableState) { | |||||
VerifyState(response); | |||||
} | |||||
var parameters = new Dictionary<string, string> { | |||||
{ "client_id", clientId }, | |||||
{ "code", code }, | |||||
{ "redirect_uri", redirectUri }, | |||||
{ "grant_type", grantType }, | |||||
}; | |||||
if (CodeVerify != null) { | |||||
parameters.Add("code_verifier", CodeVerify); | |||||
} | |||||
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> | |||||
public UniWebViewAuthenticationTwitterToken GenerateTokenFromExchangeResponse(string exchangeResponse) { | |||||
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> | |||||
/// The authentication flow's optional settings for Twitter. | |||||
/// </summary> | |||||
[Serializable] | |||||
public class UniWebViewAuthenticationFlowTwitterOptional { | |||||
/// <summary> | |||||
/// Whether to enable PKCE when performing authentication.This has to be enabled as `S256`, | |||||
/// otherwise, Twitter will reject the authentication request. | |||||
/// </summary> | |||||
public UniWebViewAuthenticationPKCE PKCESupport = UniWebViewAuthenticationPKCE.S256; | |||||
/// <summary> | |||||
/// Whether to enable the state verification. If enabled, the state will be generated and verified in the | |||||
/// authentication callback. This has to be `true`, otherwise, Twitter will reject the authentication request. | |||||
/// </summary> | |||||
public bool enableState = true; | |||||
} | |||||
/// <summary> | |||||
/// The token object from Twitter. Check `UniWebViewAuthenticationStandardToken` for more. | |||||
/// </summary> | |||||
public class UniWebViewAuthenticationTwitterToken : UniWebViewAuthenticationStandardToken { } |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 86840692470304a76b6d2ac73771b7af | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,182 @@ | |||||
// | |||||
// UniWebViewAuthenticationSession.cs | |||||
// Created by Wang Wei(@onevcat) on 2022-06-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 UnityEngine; | |||||
using System; | |||||
/// <summary> | |||||
/// Represents a session that can be used to authenticate a user through a web service. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// Initialize the session with a URL that points to the authentication webpage. A browser or a secure web view loads | |||||
/// and displays the page. On completion, the service sends a callback URL to the session with an authentication token, | |||||
/// and this triggers the `OnAuthenticationFinished` with the received URL. To make your app be invoked by the system, | |||||
/// you need to also add the correct callback URL starting with the value of `CallbackScheme` to UniWebView's preferences. | |||||
/// | |||||
/// Usually this session processes an OAuth 2 flow. It will be used along with a following "exchange token" request, to | |||||
/// finally get the user's access token to allow you use the service APIs on behalf of the user. This token exchange can | |||||
/// happen in the client app, or you can pass the code to your server and let your server do the left work. | |||||
/// | |||||
/// UniWebView also provides built-in integrated authentication flows for several popular service. The the | |||||
/// `UniWebViewAuthenticationFlow` cluster classes to use them and simplify your work. If the built-in models do not | |||||
/// fit your work, you can use this class as a starting point of your own authentication integration. | |||||
/// | |||||
/// See https://docs.uniwebview.com/guide/oauth2.html for a more detailed guide of authentication in UniWebView. | |||||
/// </remarks> | |||||
public class UniWebViewAuthenticationSession: UnityEngine.Object { | |||||
/// <summary> | |||||
/// Delegate for authentication session finished event. | |||||
/// </summary> | |||||
/// <param name="session">The session which raised this event.</param> | |||||
/// <param name="url"> | |||||
/// The received URL from service. It might contain a valid `code` from the service, or an error. | |||||
/// </param> | |||||
public delegate void AuthenticationFinishedDelegate(UniWebViewAuthenticationSession session, string url); | |||||
/// <summary> | |||||
/// Raised when the session finishes authentication. | |||||
/// | |||||
/// This event will be invoked when the service provider calls the callback URL. regardless of the authentication code | |||||
/// is retrieved or an error is returned in the callback URL. | |||||
/// </summary> | |||||
public event AuthenticationFinishedDelegate OnAuthenticationFinished; | |||||
/// <summary> | |||||
/// Delegate for authentication session error encounter event. | |||||
/// </summary> | |||||
/// <param name="session">The session which raised this event.</param> | |||||
/// <param name="errorCode">The error code represents the error type.</param> | |||||
/// <param name="errorMessage">The error message describes the error in detail.</param> | |||||
public delegate void AuthErrorReceivedDelegate(UniWebViewAuthenticationSession session, int errorCode, string errorMessage); | |||||
/// <summary> | |||||
/// Raised when the session encounters an error. | |||||
/// | |||||
/// This event will be invoked when the authentication session cannot finishes with a URL callback. This usually | |||||
/// happens when a network error or the user dismisses the authentication page from native UI. | |||||
/// </summary> | |||||
public event AuthErrorReceivedDelegate OnAuthenticationErrorReceived; | |||||
private readonly string id = Guid.NewGuid().ToString(); | |||||
private UniWebViewNativeListener listener; | |||||
/// <summary> | |||||
/// The URL of the authentication webpage. This is the value you used to create this session. | |||||
/// </summary> | |||||
public string Url { get; private set; } | |||||
/// <summary> | |||||
/// The callback scheme of the authentication webpage. This is the value you used to create this session. The service | |||||
/// is expected to use a URL with this scheme to return to your app. | |||||
/// </summary> | |||||
public string CallbackScheme { get; private set; } | |||||
internal void InternalAuthenticationFinished(string url) { | |||||
if (OnAuthenticationFinished != null) { | |||||
OnAuthenticationFinished(this, url); | |||||
} | |||||
UniWebViewNativeListener.RemoveListener(listener.Name); | |||||
Destroy(listener.gameObject); | |||||
} | |||||
internal void InternalAuthenticationErrorReceived(UniWebViewNativeResultPayload payload) { | |||||
if (OnAuthenticationErrorReceived != null) { | |||||
int errorCode = int.TryParse(payload.resultCode, out errorCode) ? errorCode : -1; | |||||
OnAuthenticationErrorReceived(this, errorCode, payload.data); | |||||
} | |||||
UniWebViewNativeListener.RemoveListener(listener.Name); | |||||
Destroy(listener.gameObject); | |||||
} | |||||
private UniWebViewAuthenticationSession() { | |||||
var listenerObject = new GameObject("UniWebViewAuthSession-" + id); | |||||
listener = listenerObject.AddComponent<UniWebViewNativeListener>(); | |||||
UniWebViewNativeListener.AddListener(listener); | |||||
} | |||||
/// <summary> | |||||
/// Check whether the current device and system supports the authentication session. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// This property always returns `true` on iOS 11, macOS 10.15 and later. On Android, it depends on whether there | |||||
/// is an Intent can handle the safe browsing request, which is use to display the authentication page. Usually | |||||
/// it is provided by Chrome. If there is no Intent can open the URL in safe browsing mode, this property will | |||||
/// return `false`. | |||||
/// | |||||
/// To use this API on Android when you set your Target SDK to Android 11 or later, you need to declare the correct | |||||
/// intent query explicitly in your AndroidManifest.xml, to follow the Package Visibility | |||||
/// (https://developer.android.com/about/versions/11/privacy/package-visibility): | |||||
/// | |||||
/// ```xml | |||||
/// <queries> | |||||
/// <intent> | |||||
/// <action android:name="android.support.customtabs.action.CustomTabsService" /> | |||||
/// </intent> | |||||
/// </queries> | |||||
/// ``` | |||||
/// </remarks> | |||||
/// <returns> | |||||
/// 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 => UniWebViewInterface.IsAuthenticationIsSupported(); | |||||
/// <summary> | |||||
/// Creates a new authentication session with a given authentication page URL and a callback scheme. | |||||
/// </summary> | |||||
/// <param name="url"> | |||||
/// The authentication page which is provided by the service. It should be a URL with some information like your app's | |||||
/// client id and required scopes, etc. | |||||
/// </param> | |||||
/// <param name="callbackScheme">The URL scheme which the service will use to navigate back to your client app.</param> | |||||
/// <returns></returns> | |||||
public static UniWebViewAuthenticationSession Create(string url, string callbackScheme) { | |||||
var session = new UniWebViewAuthenticationSession(); | |||||
session.listener.session = session; | |||||
session.Url = url; | |||||
session.CallbackScheme = callbackScheme; | |||||
UniWebViewInterface.AuthenticationInit(session.listener.Name, url, callbackScheme); | |||||
return session; | |||||
} | |||||
/// <summary> | |||||
/// Start the authentication session process. It will show up a secured web page and navigate users to the `Url`. | |||||
/// </summary> | |||||
public void Start() { | |||||
UniWebViewInterface.AuthenticationStart(listener.Name); | |||||
} | |||||
/// <summary> | |||||
/// Sets to use the private mode for the authentication. If running under private mode, the previous stored | |||||
/// authentication information will not be used. | |||||
/// <remarks> | |||||
/// On Apple's platform, this works from iOS 13 and macOS 10.15. On Android, this depends on the Chrome setting on the | |||||
/// device. The users should enable the "incognito" and "third-party incognito" to allow to use this feature. | |||||
/// | |||||
/// Check them in Chrome app: | |||||
/// chrome://flags/#cct-incognito | |||||
/// chrome://flags/#cct-incognito-available-to-third-party | |||||
/// </remarks> | |||||
/// </summary> | |||||
/// <param name="flag">Whether the session should run in private mode or not.</param> | |||||
public void SetPrivateMode(bool flag) { | |||||
UniWebViewInterface.AuthenticationSetPrivateMode(listener.Name, flag); | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: e5e52f46b3fba4e1c868230b0da9fd5f | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,116 @@ | |||||
// | |||||
// UniWebViewAuthenticationStandardToken.cs | |||||
// Created by Wang Wei (@onevcat) on 2022-06-25. | |||||
// | |||||
// 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 standard token used in the OAuth 2 process. | |||||
/// </summary> | |||||
[Serializable] | |||||
public class UniWebViewAuthenticationStandardToken { | |||||
// Unity's JsonUtility.FromJson is quite stupid on this. | |||||
// Switch to Newtonsoft.Json when we can support from Unity 2021. | |||||
[SerializeField] | |||||
private string access_token = default; | |||||
/// <summary> | |||||
/// The access token retrieved from the service provider. | |||||
/// | |||||
/// This usually comes from the `access_token` field in the response. | |||||
/// Use this token to access the service provider's API. | |||||
/// | |||||
/// If you do not need the token "offline", just use it and discard. UniWebView will not store this token, if you | |||||
/// need to keep it for other purpose, please make sure you do not violate any policies and put it to a secure | |||||
/// place yourself. | |||||
/// </summary> | |||||
public string AccessToken => access_token; | |||||
[SerializeField] | |||||
private string scope = default; | |||||
/// <summary> | |||||
/// The granted scopes of the token. This is usually comes from the `scope` field in the response. | |||||
/// | |||||
/// If there are optional scopes in the initial auth request, the user can choose to not give you some of the | |||||
/// permissions. Check this field before you use the access token to perform certain actions to avoid failure | |||||
/// before actual attempts. | |||||
/// </summary> | |||||
public string Scope => scope; | |||||
[SerializeField] | |||||
private string token_type = default; | |||||
/// <summary> | |||||
/// The token type. This usually comes from the `token_type` field in the response. | |||||
/// | |||||
/// For most OAuth 2.0 services, it is fixed to `Bearer`. | |||||
/// </summary> | |||||
public string TokenType => token_type; | |||||
[SerializeField] | |||||
private string refresh_token = default; | |||||
/// <summary> | |||||
/// The refresh token retrieved from the service provider. This usually comes from the `refresh_token` field in the | |||||
/// response. | |||||
/// | |||||
/// If the access token is refreshable, you can use this | |||||
/// refresh token to perform a refresh operation and get a new access token without the user's consent again. | |||||
/// | |||||
/// The refresh policy can be different from the service providers. Read the documentation of the service provider | |||||
/// to determine the use of refresh token. | |||||
/// | |||||
/// If the response does not contain a refresh token, this field will be `null`. | |||||
/// </summary> | |||||
public string RefreshToken => refresh_token; | |||||
[SerializeField] | |||||
private long expires_in = default; | |||||
/// <summary> | |||||
/// How long does this token remain valid. This usually comes from the `expires_in` field in the response. | |||||
/// </summary> | |||||
public long ExpiresIn => expires_in; | |||||
[SerializeField] | |||||
private string id_token = default; | |||||
/// <summary> | |||||
/// The ID token retrieved from the service provider. This usually comes from the `id_token` field in the response. | |||||
/// | |||||
/// If the service provider does not support ID token or you did not apply for it, this field will be `null`. | |||||
/// The ID token is usually a JWT token that contains information about the user. | |||||
/// </summary> | |||||
public string IdToken => id_token; | |||||
/// <summary> | |||||
/// The raw value of the response of the exchange token request. | |||||
/// | |||||
/// If the predefined fields are not enough, you can parse the raw value to get the extra information. | |||||
/// </summary> | |||||
public string RawValue { get; set; } | |||||
} | |||||
/// <summary> | |||||
/// Util class to generate the standard token from a JSON based exchange token response. | |||||
/// </summary> | |||||
/// <typeparam name="TToken">The type of target token.</typeparam> | |||||
public abstract class UniWebViewAuthenticationTokenFactory<TToken> where TToken : UniWebViewAuthenticationStandardToken { | |||||
public static TToken Parse(string result) { | |||||
var json = JsonUtility.FromJson<TToken>(result); | |||||
json.RawValue = result; | |||||
if (json.AccessToken == null) { | |||||
throw AuthenticationResponseException.InvalidResponse(result); | |||||
} | |||||
return json; | |||||
} | |||||
} |
@@ -0,0 +1,3 @@ | |||||
fileFormatVersion: 2 | |||||
guid: bda3fe59d0bb48c890f1545b0b22dee4 | |||||
timeCreated: 1656308530 |
@@ -0,0 +1,198 @@ | |||||
// | |||||
// UniWebViewAuthenticationUtils.cs | |||||
// Created by Wang Wei (@onevcat) on 2022-06-25. | |||||
// | |||||
// 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 System.Security.Cryptography; | |||||
using System.Text; | |||||
using UnityEngine.Networking; | |||||
/// <summary> | |||||
/// This class provides some helper utils for performing the authentication flow. | |||||
/// | |||||
/// They are used inside the built-in flows, but you can also use them to implement your own flow. | |||||
/// </summary> | |||||
public class UniWebViewAuthenticationUtils { | |||||
internal static Dictionary<string, string> ParseFormUrlEncodedString(string input) { | |||||
var result = new Dictionary<string, string>(); | |||||
if (input.StartsWith("?") || input.StartsWith("#")) { | |||||
input = input.Substring(1); | |||||
} | |||||
var pairs = input.Split('&'); | |||||
foreach (var pair in pairs) { | |||||
var kv = pair.Split('='); | |||||
result.Add(UnityWebRequest.UnEscapeURL(kv[0]), UnityWebRequest.UnEscapeURL(kv[1])); | |||||
} | |||||
return result; | |||||
} | |||||
/// <summary> | |||||
/// Generates a random Base64 encoded string. | |||||
/// </summary> | |||||
/// <returns>A random Base64 encoded string.</returns> | |||||
public static string GenerateRandomBase64String() { | |||||
var randomNumber = new byte[32]; | |||||
string value = ""; | |||||
using (var rng = RandomNumberGenerator.Create()) { | |||||
rng.GetBytes(randomNumber); | |||||
value = Convert.ToBase64String(randomNumber); | |||||
} | |||||
return value; | |||||
} | |||||
/// <summary> | |||||
/// Generates a random Base64URL encoded string. | |||||
/// </summary> | |||||
/// <returns>A random Base64URL encoded string.</returns> | |||||
public static string GenerateRandomBase64URLString() { | |||||
var value = GenerateRandomBase64String(); | |||||
return ConvertToBase64URLString(value); | |||||
} | |||||
static readonly char[] padding = { '=' }; | |||||
/// <summary> | |||||
/// Converts a Base64 encoded string to a Base64URL encoded string. | |||||
/// </summary> | |||||
/// <param name="input">The Base64 encoded string.</param> | |||||
/// <returns>A string with Base64URL encoded for the input.</returns> | |||||
public static string ConvertToBase64URLString(string input) { | |||||
return input.TrimEnd(padding).Replace('+', '-').Replace('/', '_'); | |||||
} | |||||
/// <summary> | |||||
/// Converts a Base64URL encoded string to a Base64 encoded string. | |||||
/// </summary> | |||||
/// <param name="input">The Base64URL encoded string.</param> | |||||
/// <returns>A string with Base64 encoded for the input.</returns> | |||||
public static string ConvertToBase64String(string input) { | |||||
var result = input.Replace('_', '/').Replace('-', '+'); | |||||
switch (input.Length % 4) { | |||||
case 2: | |||||
result += "=="; | |||||
break; | |||||
case 3: | |||||
result += "="; | |||||
break; | |||||
} | |||||
return result; | |||||
} | |||||
/// <summary> | |||||
/// Generates a code verifier for PKCE usage. | |||||
/// </summary> | |||||
/// <param name="length">The length of the target code verifier. Default is 64.</param> | |||||
/// <returns>A generated code verifier for PKCE usage.</returns> | |||||
public static string GenerateCodeVerifier(int length = 64) { | |||||
var randomNumber = new byte[32]; | |||||
string value; | |||||
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; | |||||
} | |||||
/// <summary> | |||||
/// Calculates the code challenge for PKCE usage, with a given code verifier and hash method. | |||||
/// </summary> | |||||
/// <param name="codeVerifier">The code verifier you generated.</param> | |||||
/// <param name="method">The hash method you want to use.</param> | |||||
/// <returns>The result of the code challenge.</returns> | |||||
public static string CalculateCodeChallenge(string codeVerifier, UniWebViewAuthenticationPKCE method) { | |||||
switch (method) { | |||||
case UniWebViewAuthenticationPKCE.None: | |||||
throw new ArgumentOutOfRangeException(nameof(method), method, null); | |||||
case UniWebViewAuthenticationPKCE.S256: | |||||
var sha256 = SHA256.Create(); | |||||
var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(codeVerifier)); | |||||
return ConvertToBase64URLString(System.Convert.ToBase64String(hash)); | |||||
case UniWebViewAuthenticationPKCE.Plain: | |||||
return codeVerifier; | |||||
default: | |||||
throw new ArgumentOutOfRangeException(nameof(method), method, null); | |||||
} | |||||
} | |||||
public static string ConvertPKCEToString(UniWebViewAuthenticationPKCE method) { | |||||
switch (method) { | |||||
case UniWebViewAuthenticationPKCE.None: | |||||
return null; | |||||
case UniWebViewAuthenticationPKCE.S256: | |||||
return "S256"; | |||||
case UniWebViewAuthenticationPKCE.Plain: | |||||
return "plain"; | |||||
} | |||||
return null; | |||||
} | |||||
public static string ConvertIntentUri(string input) { | |||||
var uri = new Uri(input); | |||||
if (uri.Scheme != "intent") { | |||||
return input; | |||||
} | |||||
var host = uri.Host; | |||||
string scheme = null; | |||||
var fragments = uri.Fragment; | |||||
fragments.Split(';').ToList().ForEach(fragment => { | |||||
var kv = fragment.Split('='); | |||||
if (kv.Length == 2 && kv[0] == "scheme") { | |||||
scheme = kv[1]; | |||||
} | |||||
}); | |||||
if (!String.IsNullOrEmpty(scheme)) { | |||||
return scheme + "://" + host; | |||||
} | |||||
return input; | |||||
} | |||||
public static string CreateQueryString(Dictionary<string, string> collection) { | |||||
int count = collection.Count; | |||||
if (count == 0) { | |||||
return ""; | |||||
} | |||||
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) { | |||||
sb.Length--; | |||||
} | |||||
return sb.ToString(); | |||||
} | |||||
} | |||||
public enum UniWebViewAuthenticationPKCE | |||||
{ | |||||
None, | |||||
S256, | |||||
Plain | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: cb86a1823d1e54d558d6f77e3c36719b | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -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 |