@@ -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 |