From b465c65f605debd5d8a73b14db8f3677aecd6ed9 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Fri, 22 Aug 2025 14:39:55 +1200 Subject: [PATCH 01/75] feat: poc volt webview --- .../Assets/Scenes/Passport/WebViewTest.unity | 3808 +++++++++++++++++ .../Scenes/Passport/WebViewTest.unity.meta | 7 + .../Scripts/Passport/WebViewTesting.meta | 8 + .../WebViewTesting/IWebViewAdapter.cs | 82 + .../WebViewTesting/IWebViewAdapter.cs.meta | 11 + .../Immutable.Passport.WebViewTesting.asmdef | 19 + ...utable.Passport.WebViewTesting.asmdef.meta | 7 + .../Scripts/Passport/WebViewTesting/README.md | 211 + .../Passport/WebViewTesting/README.md.meta | 7 + .../VoltUnityWebBrowserAdapter.cs | 692 +++ .../VoltUnityWebBrowserAdapter.cs.meta | 11 + .../WebViewTesting/WebViewTestManager.cs | 476 +++ .../WebViewTesting/WebViewTestManager.cs.meta | 11 + .../WebViewTesting/WebViewTestSceneSetup.cs | 417 ++ .../WebViewTestSceneSetup.cs.meta | 11 + .../PackageManagerSettings.asset | 8 +- 16 files changed, 5782 insertions(+), 4 deletions(-) create mode 100644 sample/Assets/Scenes/Passport/WebViewTest.unity create mode 100644 sample/Assets/Scenes/Passport/WebViewTest.unity.meta create mode 100644 sample/Assets/Scripts/Passport/WebViewTesting.meta create mode 100644 sample/Assets/Scripts/Passport/WebViewTesting/IWebViewAdapter.cs create mode 100644 sample/Assets/Scripts/Passport/WebViewTesting/IWebViewAdapter.cs.meta create mode 100644 sample/Assets/Scripts/Passport/WebViewTesting/Immutable.Passport.WebViewTesting.asmdef create mode 100644 sample/Assets/Scripts/Passport/WebViewTesting/Immutable.Passport.WebViewTesting.asmdef.meta create mode 100644 sample/Assets/Scripts/Passport/WebViewTesting/README.md create mode 100644 sample/Assets/Scripts/Passport/WebViewTesting/README.md.meta create mode 100644 sample/Assets/Scripts/Passport/WebViewTesting/VoltUnityWebBrowserAdapter.cs create mode 100644 sample/Assets/Scripts/Passport/WebViewTesting/VoltUnityWebBrowserAdapter.cs.meta create mode 100644 sample/Assets/Scripts/Passport/WebViewTesting/WebViewTestManager.cs create mode 100644 sample/Assets/Scripts/Passport/WebViewTesting/WebViewTestManager.cs.meta create mode 100644 sample/Assets/Scripts/Passport/WebViewTesting/WebViewTestSceneSetup.cs create mode 100644 sample/Assets/Scripts/Passport/WebViewTesting/WebViewTestSceneSetup.cs.meta diff --git a/sample/Assets/Scenes/Passport/WebViewTest.unity b/sample/Assets/Scenes/Passport/WebViewTest.unity new file mode 100644 index 00000000..72287d38 --- /dev/null +++ b/sample/Assets/Scenes/Passport/WebViewTest.unity @@ -0,0 +1,3808 @@ +%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: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + 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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &31055057 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 31055058} + - component: {fileID: 31055060} + - component: {fileID: 31055059} + - component: {fileID: 31055061} + m_Layer: 0 + m_Name: Button_Navigate + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &31055058 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 31055057} + 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: + - {fileID: 718943776} + m_Father: {fileID: 607041414} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -150, y: -70} + m_SizeDelta: {x: 180, y: 40} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &31055059 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 31055057} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.2, g: 0.6, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &31055060 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 31055057} + m_CullTransparentMesh: 1 +--- !u!114 &31055061 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 31055057} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 31055059} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &111233482 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 111233483} + - component: {fileID: 111233486} + - component: {fileID: 111233485} + - component: {fileID: 111233484} + m_Layer: 0 + m_Name: Viewport + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &111233483 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 111233482} + 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: + - {fileID: 1246568564} + m_Father: {fileID: 1563024407} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &111233484 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 111233482} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ShowMaskGraphic: 0 +--- !u!114 &111233485 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 111233482} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &111233486 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 111233482} + m_CullTransparentMesh: 1 +--- !u!1 &161426650 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 161426651} + - component: {fileID: 161426653} + - component: {fileID: 161426652} + - component: {fileID: 161426654} + m_Layer: 0 + m_Name: Button_TestInput + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &161426651 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 161426650} + 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: + - {fileID: 1775934779} + m_Father: {fileID: 607041414} + m_RootOrder: 12 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -75, y: -110} + m_SizeDelta: {x: 180, y: 40} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &161426652 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 161426650} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.2, g: 0.6, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &161426653 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 161426650} + m_CullTransparentMesh: 1 +--- !u!114 &161426654 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 161426650} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 161426652} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &170613142 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 170613143} + - component: {fileID: 170613145} + - component: {fileID: 170613144} + m_Layer: 0 + m_Name: Placeholder + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &170613143 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 170613142} + 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: 206812580} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &170613144 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 170613142} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Enter URL... +--- !u!222 &170613145 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 170613142} + m_CullTransparentMesh: 1 +--- !u!1 &206812579 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 206812580} + - component: {fileID: 206812582} + - component: {fileID: 206812581} + - component: {fileID: 206812583} + m_Layer: 0 + m_Name: InputField_URL + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &206812580 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 206812579} + 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: + - {fileID: 2009206667} + - {fileID: 170613143} + m_Father: {fileID: 607041414} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: -30} + m_SizeDelta: {x: 400, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &206812581 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 206812579} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0.8} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &206812582 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 206812579} + m_CullTransparentMesh: 1 +--- !u!114 &206812583 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 206812579} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d199490a83bb2b844b9695cbf13b01ef, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 206812581} + m_TextComponent: {fileID: 2009206668} + m_Placeholder: {fileID: 170613144} + m_ContentType: 0 + m_InputType: 0 + m_AsteriskChar: 42 + m_KeyboardType: 0 + m_LineType: 0 + m_HideMobileInput: 0 + m_CharacterValidation: 0 + m_CharacterLimit: 0 + m_OnSubmit: + m_PersistentCalls: + m_Calls: [] + m_OnDidEndEdit: + m_PersistentCalls: + m_Calls: [] + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] + m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_CustomCaretColor: 0 + m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412} + m_Text: + m_CaretBlinkRate: 0.85 + m_CaretWidth: 1 + m_ReadOnly: 0 + m_ShouldActivateOnSelect: 1 +--- !u!1 &223196514 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 223196515} + - component: {fileID: 223196517} + - component: {fileID: 223196516} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &223196515 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 223196514} + 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: 273482722} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &223196516 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 223196514} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Test Login Page +--- !u!222 &223196517 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 223196514} + m_CullTransparentMesh: 1 +--- !u!1 &273482721 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 273482722} + - component: {fileID: 273482724} + - component: {fileID: 273482723} + - component: {fileID: 273482725} + m_Layer: 0 + m_Name: Button_TestLoginPage + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &273482722 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 273482721} + 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: + - {fileID: 223196515} + m_Father: {fileID: 607041414} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -100, y: 100} + m_SizeDelta: {x: 180, y: 40} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &273482723 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 273482721} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.2, g: 0.6, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &273482724 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 273482721} + m_CullTransparentMesh: 1 +--- !u!114 &273482725 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 273482721} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 273482723} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &328518055 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 328518056} + - component: {fileID: 328518058} + - component: {fileID: 328518057} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &328518056 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 328518055} + 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: 1959602689} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &328518057 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 328518055} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Close WebView +--- !u!222 &328518058 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 328518055} + m_CullTransparentMesh: 1 +--- !u!1 &418322770 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 418322774} + - component: {fileID: 418322773} + - component: {fileID: 418322772} + - component: {fileID: 418322771} + m_Layer: 0 + m_Name: Button_FindWebView + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &418322771 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 418322770} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 418322772} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &418322772 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 418322770} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.2, g: 0.6, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &418322773 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 418322770} + m_CullTransparentMesh: 1 +--- !u!224 &418322774 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 418322770} + 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: + - {fileID: 1664369642} + m_Father: {fileID: 607041414} + m_RootOrder: 13 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 75, y: -110} + m_SizeDelta: {x: 180, y: 40} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &607041413 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 607041414} + - component: {fileID: 607041416} + - component: {fileID: 607041415} + m_Layer: 0 + m_Name: TestPanel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &607041414 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 607041413} + 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: + - {fileID: 1887783926} + - {fileID: 1760376665} + - {fileID: 2067238213} + - {fileID: 273482722} + - {fileID: 941722161} + - {fileID: 1959602689} + - {fileID: 1134813101} + - {fileID: 206812580} + - {fileID: 31055058} + - {fileID: 965247883} + - {fileID: 965891923} + - {fileID: 1947544483} + - {fileID: 161426651} + - {fileID: 418322774} + - {fileID: 1807635083} + - {fileID: 761869978} + m_Father: {fileID: 945998443} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -100, y: -100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &607041415 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 607041413} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.1, g: 0.1, b: 0.1, a: 0.8} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &607041416 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 607041413} + m_CullTransparentMesh: 1 +--- !u!1 &718943775 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 718943776} + - component: {fileID: 718943778} + - component: {fileID: 718943777} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &718943776 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 718943775} + 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: 31055058} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &718943777 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 718943775} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Navigate +--- !u!222 &718943778 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 718943775} + m_CullTransparentMesh: 1 +--- !u!1 &721759347 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 721759349} + - component: {fileID: 721759348} + m_Layer: 0 + m_Name: WebViewTestManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &721759348 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 721759347} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 811054e6f94ff3140a9c103882b0ce43, type: 3} + m_Name: + m_EditorClassIdentifier: + selectedPackage: 0 + testUrl: https://passport.immutable.com/sdk-sample-app + messageTestUrl: file:///test-message-page.html + testLoginButton: {fileID: 273482725} + testMessagingButton: {fileID: 941722164} + closeWebViewButton: {fileID: 1959602692} + statusOutput: {fileID: 1807635085} + performanceOutput: {fileID: 761869980} + urlInputField: {fileID: 206812583} + navigateButton: {fileID: 31055061} + backButton: {fileID: 965247886} + forwardButton: {fileID: 965891926} + refreshButton: {fileID: 1947544486} + testInputButton: {fileID: 161426654} + findWebViewButton: {fileID: 418322771} + webViewWidth: 1024 + webViewHeight: 768 + fullScreenMode: 1 +--- !u!4 &721759349 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 721759347} + 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: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &761869977 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 761869978} + - component: {fileID: 761869979} + - component: {fileID: 761869980} + m_Layer: 0 + m_Name: PerformanceOutput + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &761869978 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 761869977} + 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: 607041414} + m_RootOrder: 15 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: -200} + m_SizeDelta: {x: 400, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &761869979 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 761869977} + m_CullTransparentMesh: 1 +--- !u!114 &761869980 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 761869977} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 12 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 'Performance: -' +--- !u!1 &854032325 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 854032326} + - component: {fileID: 854032327} + m_Layer: 0 + m_Name: Item + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &854032326 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 854032325} + 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: + - {fileID: 1382102607} + - {fileID: 1722110113} + m_Father: {fileID: 1246568564} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &854032327 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 854032325} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1382102608} + toggleTransition: 1 + graphic: {fileID: 0} + m_Group: {fileID: 0} + onValueChanged: + m_PersistentCalls: + m_Calls: [] + m_IsOn: 0 +--- !u!1 &941722160 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 941722161} + - component: {fileID: 941722163} + - component: {fileID: 941722162} + - component: {fileID: 941722164} + m_Layer: 0 + m_Name: Button_TestMessaging + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &941722161 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 941722160} + 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: + - {fileID: 1705142168} + m_Father: {fileID: 607041414} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 100, y: 100} + m_SizeDelta: {x: 180, y: 40} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &941722162 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 941722160} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.2, g: 0.6, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &941722163 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 941722160} + m_CullTransparentMesh: 1 +--- !u!114 &941722164 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 941722160} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 941722162} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &945998442 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 945998443} + - component: {fileID: 945998446} + - component: {fileID: 945998445} + - component: {fileID: 945998444} + m_Layer: 0 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &945998443 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 945998442} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 607041414} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!114 &945998444 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 945998442} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &945998445 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 945998442} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &945998446 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 945998442} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!1 &965247882 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 965247883} + - component: {fileID: 965247885} + - component: {fileID: 965247884} + - component: {fileID: 965247886} + m_Layer: 0 + m_Name: Button_Back + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &965247883 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 965247882} + 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: + - {fileID: 1688075393} + m_Father: {fileID: 607041414} + m_RootOrder: 9 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -50, y: -70} + m_SizeDelta: {x: 180, y: 40} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &965247884 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 965247882} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.2, g: 0.6, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &965247885 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 965247882} + m_CullTransparentMesh: 1 +--- !u!114 &965247886 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 965247882} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 965247884} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &965891922 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 965891923} + - component: {fileID: 965891925} + - component: {fileID: 965891924} + - component: {fileID: 965891926} + m_Layer: 0 + m_Name: Button_Forward + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &965891923 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 965891922} + 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: + - {fileID: 1226525528} + m_Father: {fileID: 607041414} + m_RootOrder: 10 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 50, y: -70} + m_SizeDelta: {x: 180, y: 40} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &965891924 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 965891922} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.2, g: 0.6, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &965891925 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 965891922} + m_CullTransparentMesh: 1 +--- !u!114 &965891926 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 965891922} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 965891924} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &1134813100 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1134813101} + - component: {fileID: 1134813103} + - component: {fileID: 1134813102} + m_Layer: 0 + m_Name: Text_Navigation + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1134813101 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1134813100} + 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: 607041414} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 400, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1134813102 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1134813100} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 16 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 'Navigation Controls:' +--- !u!222 &1134813103 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1134813100} + m_CullTransparentMesh: 1 +--- !u!1 &1226525527 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1226525528} + - component: {fileID: 1226525530} + - component: {fileID: 1226525529} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1226525528 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1226525527} + 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: 965891923} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1226525529 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1226525527} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Forward +--- !u!222 &1226525530 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1226525527} + m_CullTransparentMesh: 1 +--- !u!1 &1246568563 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1246568564} + m_Layer: 0 + m_Name: Content + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1246568564 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1246568563} + 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: + - {fileID: 854032326} + m_Father: {fileID: 111233483} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 20} + m_Pivot: {x: 0.5, y: 1} +--- !u!1 &1382102606 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1382102607} + - component: {fileID: 1382102609} + - component: {fileID: 1382102608} + m_Layer: 0 + m_Name: Item Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1382102607 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1382102606} + 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: 854032326} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1382102608 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1382102606} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.96, g: 0.96, b: 0.96, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1382102609 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1382102606} + m_CullTransparentMesh: 1 +--- !u!1 &1413060897 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1413060898} + - component: {fileID: 1413060900} + - component: {fileID: 1413060899} + m_Layer: 0 + m_Name: Arrow + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1413060898 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1413060897} + 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: 2067238213} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -15, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1413060899 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1413060897} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 12 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: "\u25BC" +--- !u!222 &1413060900 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1413060897} + m_CullTransparentMesh: 1 +--- !u!1 &1563024406 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1563024407} + - component: {fileID: 1563024409} + - component: {fileID: 1563024408} + m_Layer: 0 + m_Name: Template + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &1563024407 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1563024406} + 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: + - {fileID: 111233483} + m_Father: {fileID: 2067238213} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: 0, y: 2} + m_SizeDelta: {x: 0, y: 150} + m_Pivot: {x: 0.5, y: 1} +--- !u!114 &1563024408 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1563024406} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0.95} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1563024409 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1563024406} + m_CullTransparentMesh: 1 +--- !u!1 &1659039520 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1659039523} + - component: {fileID: 1659039522} + - component: {fileID: 1659039521} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1659039521 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1659039520} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &1659039522 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1659039520} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &1659039523 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1659039520} + 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: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1664369641 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1664369642} + - component: {fileID: 1664369644} + - component: {fileID: 1664369643} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1664369642 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1664369641} + 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: 418322774} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1664369643 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1664369641} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Find WebView +--- !u!222 &1664369644 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1664369641} + m_CullTransparentMesh: 1 +--- !u!1 &1688075392 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1688075393} + - component: {fileID: 1688075395} + - component: {fileID: 1688075394} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1688075393 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1688075392} + 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: 965247883} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1688075394 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1688075392} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Back +--- !u!222 &1688075395 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1688075392} + m_CullTransparentMesh: 1 +--- !u!1 &1705142167 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1705142168} + - component: {fileID: 1705142170} + - component: {fileID: 1705142169} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1705142168 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1705142167} + 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: 941722161} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1705142169 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1705142167} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Test Messaging +--- !u!222 &1705142170 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1705142167} + m_CullTransparentMesh: 1 +--- !u!1 &1722110112 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1722110113} + - component: {fileID: 1722110115} + - component: {fileID: 1722110114} + m_Layer: 0 + m_Name: Item Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1722110113 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1722110112} + 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: 854032326} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -0.5} + m_SizeDelta: {x: -20, y: -3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1722110114 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1722110112} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 12 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Option +--- !u!222 &1722110115 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1722110112} + m_CullTransparentMesh: 1 +--- !u!1 &1760376664 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1760376665} + - component: {fileID: 1760376667} + - component: {fileID: 1760376666} + m_Layer: 0 + m_Name: Text_SelectWebV + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1760376665 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1760376664} + 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: 607041414} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -200, y: 150} + m_SizeDelta: {x: 400, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1760376666 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1760376664} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 16 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 'Select WebView Package:' +--- !u!222 &1760376667 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1760376664} + m_CullTransparentMesh: 1 +--- !u!1 &1772212348 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1772212351} + - component: {fileID: 1772212350} + - component: {fileID: 1772212349} + 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 &1772212349 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1772212348} + m_Enabled: 1 +--- !u!20 &1772212350 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1772212348} + 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 &1772212351 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1772212348} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1775934778 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1775934779} + - component: {fileID: 1775934781} + - component: {fileID: 1775934780} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1775934779 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1775934778} + 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: 161426651} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1775934780 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1775934778} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Test Input +--- !u!222 &1775934781 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1775934778} + m_CullTransparentMesh: 1 +--- !u!1 &1807635082 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1807635083} + - component: {fileID: 1807635084} + - component: {fileID: 1807635085} + m_Layer: 0 + m_Name: StatusOutput + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1807635083 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1807635082} + 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: 607041414} + m_RootOrder: 14 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: -150} + m_SizeDelta: {x: 400, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &1807635084 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1807635082} + m_CullTransparentMesh: 1 +--- !u!114 &1807635085 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1807635082} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 'Status: Ready' +--- !u!1 &1831214734 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1831214735} + - component: {fileID: 1831214737} + - component: {fileID: 1831214736} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1831214735 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1831214734} + 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: 1947544483} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1831214736 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1831214734} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Refresh +--- !u!222 &1831214737 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1831214734} + m_CullTransparentMesh: 1 +--- !u!1 &1887783925 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1887783926} + - component: {fileID: 1887783928} + - component: {fileID: 1887783927} + m_Layer: 0 + m_Name: Text_WebViewTes + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1887783926 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1887783925} + 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: 607041414} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 200} + m_SizeDelta: {x: 400, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1887783927 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1887783925} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 24 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: WebView Testing Framework +--- !u!222 &1887783928 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1887783925} + m_CullTransparentMesh: 1 +--- !u!1 &1947544482 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1947544483} + - component: {fileID: 1947544485} + - component: {fileID: 1947544484} + - component: {fileID: 1947544486} + m_Layer: 0 + m_Name: Button_Refresh + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1947544483 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1947544482} + 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: + - {fileID: 1831214735} + m_Father: {fileID: 607041414} + m_RootOrder: 11 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 150, y: -70} + m_SizeDelta: {x: 180, y: 40} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1947544484 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1947544482} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.2, g: 0.6, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1947544485 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1947544482} + m_CullTransparentMesh: 1 +--- !u!114 &1947544486 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1947544482} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1947544484} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &1959602688 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1959602689} + - component: {fileID: 1959602691} + - component: {fileID: 1959602690} + - component: {fileID: 1959602692} + m_Layer: 0 + m_Name: Button_CloseWebView + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1959602689 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1959602688} + 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: + - {fileID: 328518056} + m_Father: {fileID: 607041414} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 50} + m_SizeDelta: {x: 180, y: 40} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1959602690 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1959602688} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.2, g: 0.6, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1959602691 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1959602688} + m_CullTransparentMesh: 1 +--- !u!114 &1959602692 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1959602688} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1959602690} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &2009206666 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2009206667} + - component: {fileID: 2009206669} + - component: {fileID: 2009206668} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2009206667 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2009206666} + 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: 206812580} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2009206668 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2009206666} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: +--- !u!222 &2009206669 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2009206666} + m_CullTransparentMesh: 1 +--- !u!1 &2010408301 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2010408302} + - component: {fileID: 2010408304} + - component: {fileID: 2010408303} + m_Layer: 0 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2010408302 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2010408301} + 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: 2067238213} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: -7.5, y: 0} + m_SizeDelta: {x: -35, y: -4} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2010408303 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2010408301} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 12 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Volt Unity Web Browser +--- !u!222 &2010408304 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2010408301} + m_CullTransparentMesh: 1 +--- !u!1 &2067238212 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2067238213} + - component: {fileID: 2067238216} + - component: {fileID: 2067238215} + - component: {fileID: 2067238214} + m_Layer: 0 + m_Name: PackageDropdown + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2067238213 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2067238212} + 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: + - {fileID: 2010408302} + - {fileID: 1413060898} + - {fileID: 1563024407} + m_Father: {fileID: 607041414} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 100, y: 150} + m_SizeDelta: {x: 200, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2067238214 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2067238212} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0d0b652f32a2cc243917e4028fa0f046, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 2067238215} + m_Template: {fileID: 1563024407} + m_CaptionText: {fileID: 2010408303} + m_CaptionImage: {fileID: 0} + m_ItemText: {fileID: 1722110114} + m_ItemImage: {fileID: 0} + m_Value: 0 + m_Options: + m_Options: + - m_Text: Volt Unity Web Browser + m_Image: {fileID: 0} + - m_Text: Alacrity WebView + m_Image: {fileID: 0} + - m_Text: UWebView2 + m_Image: {fileID: 0} + - m_Text: ZenFulcrum Browser + m_Image: {fileID: 0} + - m_Text: Vuplex 3D WebView + m_Image: {fileID: 0} + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] + m_AlphaFadeSpeed: 0.15 +--- !u!114 &2067238215 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2067238212} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0.9} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2067238216 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2067238212} + m_CullTransparentMesh: 1 diff --git a/sample/Assets/Scenes/Passport/WebViewTest.unity.meta b/sample/Assets/Scenes/Passport/WebViewTest.unity.meta new file mode 100644 index 00000000..5936649b --- /dev/null +++ b/sample/Assets/Scenes/Passport/WebViewTest.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c8e2712d59c2d9a479d9177e52dd28c5 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sample/Assets/Scripts/Passport/WebViewTesting.meta b/sample/Assets/Scripts/Passport/WebViewTesting.meta new file mode 100644 index 00000000..3f39cbea --- /dev/null +++ b/sample/Assets/Scripts/Passport/WebViewTesting.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 75a849bb19b9de44fb93dbc29a0d874d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sample/Assets/Scripts/Passport/WebViewTesting/IWebViewAdapter.cs b/sample/Assets/Scripts/Passport/WebViewTesting/IWebViewAdapter.cs new file mode 100644 index 00000000..9c66e7b7 --- /dev/null +++ b/sample/Assets/Scripts/Passport/WebViewTesting/IWebViewAdapter.cs @@ -0,0 +1,82 @@ +using System; + +namespace Immutable.Passport.WebViewTesting +{ + /// + /// Interface for WebView adapters to standardize testing across different packages + /// + public interface IWebViewAdapter : IDisposable + { + /// + /// Event fired when navigation completes + /// + event Action OnNavigationCompleted; + + /// + /// Event fired when a message is received from JavaScript + /// + event Action OnMessageReceived; + + /// + /// Event fired when an error occurs + /// + event Action OnError; + + /// + /// Whether the WebView is currently active + /// + bool IsActive { get; } + + /// + /// Initialize the WebView with specified dimensions + /// + /// Width in pixels + /// Height in pixels + void Initialize(int width, int height); + + /// + /// Navigate to the specified URL + /// + /// URL to navigate to + void Navigate(string url); + + /// + /// Execute JavaScript in the WebView + /// + /// JavaScript code to execute + void ExecuteJavaScript(string script); + + /// + /// Send a message to the WebView + /// + /// Message to send + void SendMessage(string message); + + /// + /// Show the WebView + /// + void Show(); + + /// + /// Hide the WebView + /// + void Hide(); + + /// + /// Get current performance metrics + /// + /// Performance data + WebViewPerformanceMetrics GetPerformanceMetrics(); + } + + [System.Serializable] + public class WebViewPerformanceMetrics + { + public float memoryUsageMB; + public float cpuUsagePercent; + public float renderTimeMsAvg; + public int textureWidth; + public int textureHeight; + public string engineVersion; + } +} diff --git a/sample/Assets/Scripts/Passport/WebViewTesting/IWebViewAdapter.cs.meta b/sample/Assets/Scripts/Passport/WebViewTesting/IWebViewAdapter.cs.meta new file mode 100644 index 00000000..ed9dbe79 --- /dev/null +++ b/sample/Assets/Scripts/Passport/WebViewTesting/IWebViewAdapter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7f7956cb54bc9714fa7f7d4a6a38de2d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sample/Assets/Scripts/Passport/WebViewTesting/Immutable.Passport.WebViewTesting.asmdef b/sample/Assets/Scripts/Passport/WebViewTesting/Immutable.Passport.WebViewTesting.asmdef new file mode 100644 index 00000000..59f8cc81 --- /dev/null +++ b/sample/Assets/Scripts/Passport/WebViewTesting/Immutable.Passport.WebViewTesting.asmdef @@ -0,0 +1,19 @@ +{ + "name": "Immutable.Passport.WebViewTesting", + "rootNamespace": "Immutable.Passport.WebViewTesting", + "references": [ + "Unity.TextMeshPro", + "Immutable.Passport.Runtime.Public", + "Immutable.Passport.Runtime.Private", + "VoltstroStudios.UnityWebBrowser" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} diff --git a/sample/Assets/Scripts/Passport/WebViewTesting/Immutable.Passport.WebViewTesting.asmdef.meta b/sample/Assets/Scripts/Passport/WebViewTesting/Immutable.Passport.WebViewTesting.asmdef.meta new file mode 100644 index 00000000..d13d20ab --- /dev/null +++ b/sample/Assets/Scripts/Passport/WebViewTesting/Immutable.Passport.WebViewTesting.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9a372424c65725748b76e8390c147ff1 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sample/Assets/Scripts/Passport/WebViewTesting/README.md b/sample/Assets/Scripts/Passport/WebViewTesting/README.md new file mode 100644 index 00000000..b3483bd5 --- /dev/null +++ b/sample/Assets/Scripts/Passport/WebViewTesting/README.md @@ -0,0 +1,211 @@ +# 🧪 WebView Testing Framework + +This framework provides a standardized way to test different WebView packages for Unity integration with Immutable Passport. + +## 🎯 Purpose + +- **Compare WebView packages** (Volt Unity Web Browser, Alacrity, UWebView2, ZenFulcrum, Vuplex 3D WebView) +- **Test login page rendering** with actual Passport authentication +- **Evaluate message passing** between JavaScript and Unity +- **Measure performance** (FPS, memory usage, rendering quality) +- **Validate SDK integration** for distribution + +## 📁 Structure + +``` +WebViewTesting/ +├── WebViewTestManager.cs # Main test controller +├── IWebViewAdapter.cs # Common interface for all WebView packages +├── AlacrityWebViewAdapter.cs # Alacrity WebView implementation +├── UWebView2Adapter.cs # UWebView2 implementation +├── ZenFulcrumWebViewAdapter.cs # ZenFulcrum implementation +├── Vuplex3DWebViewAdapter.cs # Vuplex 3D WebView implementation +├── WebViewTestSceneSetup.cs # Editor utility to create test scene +└── README.md # This file +``` + +## 🚀 Quick Start + +### 1. Create Test Scene + +```csharp +// In Unity Editor: +Immutable → WebView Testing → Create WebView Test Scene +``` + +### 2. Import WebView Package + +Download and import one of the WebView packages: + +- **Volt Unity Web Browser (UWB)**: `https://projects.voltstro.dev/UnityWebBrowser/latest/` ⭐ **RECOMMENDED** (MIT License, Multi-Platform) +- **Alacrity**: `https://alacrity.kevinbedi.com/` +- **UWebView2**: `https://uwebview.com/` +- **ZenFulcrum**: `https://zenfulcrum.com/browser` +- **Vuplex 3D WebView**: `https://store.vuplex.com/webview/windows-mac` + +### 3. Update Adapter Implementation + +Replace the template code in the corresponding adapter (e.g., `AlacrityWebViewAdapter.cs`) with actual WebView API calls. + +### 4. Run Tests + +1. Open the WebView Test scene +2. +3. Select the WebView package in the dropdown +4. Click "Test Login Page" to test with `https://auth.immutable.com` +5. Click "Test Messaging" to test JavaScript ↔ Unity communication + +## 🧪 Test Scenarios + +### Login Page Test + +- ✅ **Load Passport login page** (`https://auth.immutable.com`) +- ✅ **Render CSS3 features** (rounded corners, gradients, fonts) +- ✅ **Interactive elements** (buttons, forms, dropdowns) +- ✅ **OAuth flow handling** (redirects, callbacks) +- ✅ **Performance monitoring** (FPS, memory usage) + +### Message Passing Test + +- ✅ **Unity → JavaScript** communication +- ✅ **JavaScript → Unity** communication +- ✅ **OAuth callback simulation** +- ✅ **Performance data exchange** +- ✅ **Error handling** + +## 📊 Evaluation Criteria + +Rate each WebView package (1-5 stars): + +| Criteria | Weight | Description | +|----------|--------|-------------| +| **Rendering Quality** | High | CSS3 support, font clarity, animations | +| **Performance** | High | FPS, memory usage, startup time | +| **Input Handling** | Medium | Mouse, keyboard responsiveness | +| **Message Passing** | High | JavaScript ↔ Unity communication | +| **Setup Complexity** | Medium | Import → working in minutes | +| **Documentation** | Low | Examples, API docs quality | +| **Licensing Cost** | High | SDK distribution feasibility | +| **Platform Support** | Medium | Windows, macOS, Linux | + +## 🔧 Implementation Guide + +### Adding a New WebView Package + +1. **Create Adapter Class**: + +```csharp +public class MyWebViewAdapter : IWebViewAdapter +{ + // Implement all interface methods +} +``` + +1. **Update WebViewTestManager**: + +```csharp +// Add to WebViewPackage enum +public enum WebViewPackage +{ + MyWebView // Add here +} + +// Add to CreateWebViewAdapter method +case WebViewPackage.MyWebView: + return new MyWebViewAdapter(); +``` + +1. **Test Implementation**: + +- Run login page test +- Verify message passing +- Check performance metrics + +### Message Passing Implementation + +**JavaScript → Unity**: + +```javascript +// In WebView +window.unityInstance.SendMessage('WebViewTestManager', 'OnTestMessage', 'Hello Unity!'); +``` + +**Unity → JavaScript**: + +```csharp +// In Unity +webView.ExecuteJavaScript("window.receiveUnityMessage('Hello WebView!');"); +``` + +## 📝 Test Results Template + +```markdown +## WebView Package: [Package Name] + +### Setup +- **Import Time**: X minutes +- **Complexity**: Easy/Medium/Hard +- **Dependencies**: List any required dependencies + +### Login Page Test +- **Rendering Quality**: ⭐⭐⭐⭐⭐ (5/5) +- **Load Time**: X seconds +- **Interactive Elements**: Working/Broken +- **CSS3 Support**: Full/Partial/None + +### Performance +- **Average FPS**: X fps +- **Memory Usage**: X MB +- **Startup Time**: X seconds + +### Message Passing +- **Unity → JS**: Working/Broken +- **JS → Unity**: Working/Broken +- **OAuth Simulation**: Working/Broken + +### Licensing +- **Cost**: $X or Free +- **SDK Distribution**: Allowed/Restricted +- **Commercial Use**: Allowed/Restricted + +### Overall Rating: ⭐⭐⭐⭐⭐ (X/5) +**Recommendation**: Use/Don't Use for SDK +**Notes**: Additional observations... +``` + +## 🔗 Test Resources + +- **Login Page**: `https://auth.immutable.com` +- **Message Test Page**: `StreamingAssets/test-message-page.html` +- **OAuth Callback**: `immutablerunner://callback?code=test&state=test` + +## 🎯 Success Criteria + +A WebView package is suitable for SDK distribution if: + +1. ✅ **Renders login page correctly** (all elements visible and interactive) +2. ✅ **Maintains 30+ FPS** during normal operation +3. ✅ **Supports JavaScript ↔ Unity** message passing +4. ✅ **Licensing allows SDK distribution** (free or reasonable cost) +5. ✅ **Easy integration** (< 1 hour setup for developers) +6. ✅ **Handles OAuth callbacks** properly + +## 🚨 Known Issues + +- **Template adapters** need actual WebView API implementation +- **Message passing** methods vary between packages +- **Performance metrics** may need package-specific implementation +- **IL2CPP compatibility** should be tested for each package + +## 📞 Support + +For questions about this testing framework: + +1. Check the adapter implementation for your WebView package +2. Review the test HTML page for message passing examples +3. Consult the WebView package documentation +4. Test with the actual Passport login page + +--- + +**Happy Testing!** 🧪✨ diff --git a/sample/Assets/Scripts/Passport/WebViewTesting/README.md.meta b/sample/Assets/Scripts/Passport/WebViewTesting/README.md.meta new file mode 100644 index 00000000..2755f16f --- /dev/null +++ b/sample/Assets/Scripts/Passport/WebViewTesting/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: da52d0842a6765641ab96ea45c9dc314 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sample/Assets/Scripts/Passport/WebViewTesting/VoltUnityWebBrowserAdapter.cs b/sample/Assets/Scripts/Passport/WebViewTesting/VoltUnityWebBrowserAdapter.cs new file mode 100644 index 00000000..8178a9e4 --- /dev/null +++ b/sample/Assets/Scripts/Passport/WebViewTesting/VoltUnityWebBrowserAdapter.cs @@ -0,0 +1,692 @@ +using System; +using System.IO; +using System.Linq; +using UnityEngine; +using UnityEngine.UI; + +#if UNITY_STANDALONE_WIN || (UNITY_ANDROID && UNITY_EDITOR_WIN) || (UNITY_IPHONE && UNITY_EDITOR_WIN) +using VoltstroStudios.UnityWebBrowser; +using VoltstroStudios.UnityWebBrowser.Core; +using VoltstroStudios.UnityWebBrowser.Core.Engines; +using VoltstroStudios.UnityWebBrowser.Communication; +using VoltstroStudios.UnityWebBrowser.Shared; +using VoltstroStudios.UnityWebBrowser.Shared.Core; +using VoltstroStudios.UnityWebBrowser.Shared.Popups; +#endif + +namespace Immutable.Passport.WebViewTesting +{ + /// + /// Adapter for Volt Unity Web Browser (UWB) package + /// https://projects.voltstro.dev/UnityWebBrowser/latest/ + /// Uses the existing UWB integration from the Immutable SDK + /// + public class VoltUnityWebBrowserAdapter : IWebViewAdapter + { + public event Action OnNavigationCompleted; + public event Action OnMessageReceived; + public event Action OnError; + + public bool IsActive { get; private set; } + +#if UNITY_STANDALONE_WIN || (UNITY_ANDROID && UNITY_EDITOR_WIN) || (UNITY_IPHONE && UNITY_EDITOR_WIN) + private GameObject uwbGameObject; + private WebBrowserUIFull webBrowserUI; + private WebBrowserClient webBrowserClient; +#endif + private bool isInitialized = false; + + // Performance timing + private System.Diagnostics.Stopwatch initializationTimer; + private float initStartTime; + private float engineReadyTime; + private float navigationStartTime; + private System.Diagnostics.Stopwatch navigationTimer; + + // Navigation queue + private string queuedNavigationUrl; + private bool hasQueuedNavigation; + + public void Initialize(int width, int height) + { + try + { + // Start timing the initialization + initializationTimer = System.Diagnostics.Stopwatch.StartNew(); + initStartTime = Time.realtimeSinceStartup; + + Debug.Log($"[VoltUWBAdapter] 🚀 Creating SEPARATE UWB instance for UI testing (isolated from SDK bridge)"); + Debug.Log($"[VoltUWBAdapter] Initializing Volt Unity Web Browser {width}x{height} - Timer started"); + +#if UNITY_STANDALONE_WIN || (UNITY_ANDROID && UNITY_EDITOR_WIN) || (UNITY_IPHONE && UNITY_EDITOR_WIN) + // Find or create Canvas for WebView display + Canvas canvas = FindOrCreateCanvas(); + + // Create UWB GameObject as child of Canvas with unique name + uwbGameObject = new GameObject("VoltUWB_TestInstance_UI"); + uwbGameObject.transform.SetParent(canvas.transform, false); + + // Add RectTransform for UI positioning + RectTransform rectTransform = uwbGameObject.AddComponent(); + rectTransform.anchorMin = Vector2.zero; + rectTransform.anchorMax = Vector2.one; + rectTransform.offsetMin = Vector2.zero; + rectTransform.offsetMax = Vector2.zero; + + Debug.Log("[VoltUWBAdapter] 🎮 Adding WebBrowserUIFull component (separate from SDK's WebBrowserNoUi)"); + + // Add WebBrowserUIFull component - THIS IS DIFFERENT FROM SDK's WebBrowserNoUi + webBrowserUI = uwbGameObject.AddComponent(); + + // Configure input handling + ConfigureInputHandler(); + + Debug.Log($"[VoltUWBAdapter] WebBrowser UI added to Canvas: {canvas.name}"); + Debug.Log($"[VoltUWBAdapter] 🔍 HIERARCHY INFO: WebView GameObject created as '{uwbGameObject.name}' under Canvas '{canvas.name}'"); + Debug.Log($"[VoltUWBAdapter] 🔍 INSPECTOR TIP: Look for '{uwbGameObject.name}' in the hierarchy to see WebView config"); + + // Get the browser client and configure for UI display + webBrowserClient = webBrowserUI.browserClient; + + Debug.Log("[VoltUWBAdapter] 🔧 Configuring UI instance (headless=false, separate from SDK bridge)"); + webBrowserClient.headless = false; // CRITICAL: SDK uses headless=true, we need UI + + // Configure popup handling + webBrowserClient.popupAction = PopupAction.OpenExternalWindow; + Debug.Log("[VoltUWBAdapter] 🪟 Popup Action set to: OpenExternalWindow (allows popups to open in external browser)"); + Debug.Log("[VoltUWBAdapter] 💡 Note: CSP 'frame-ancestors' errors are expected and indicate proper security behavior"); + + // Ensure complete isolation from SDK's UWB instance + ConfigureIsolatedUWBInstance(); + + // Subscribe to events + webBrowserClient.OnLoadFinish += OnLoadFinishHandler; + webBrowserClient.OnLoadStart += OnLoadStartHandler; + + IsActive = true; + // Note: Don't set isInitialized = true yet, wait for the browser to be ready + + float componentSetupTime = Time.realtimeSinceStartup - initStartTime; + Debug.Log($"[VoltUWBAdapter] Component setup completed in {componentSetupTime:F3}s. Waiting for UWB engine to be ready..."); + + Debug.Log("[VoltUWBAdapter] ✅ Separate UWB UI instance created successfully (isolated from SDK bridge)"); +#else + OnError?.Invoke("Volt Unity Web Browser only supported on Windows platforms"); + Debug.LogWarning("[VoltUWBAdapter] UWB not supported on this platform"); +#endif + } + catch (Exception ex) + { + OnError?.Invoke($"UWB initialization failed: {ex.Message}"); + Debug.LogError($"[VoltUWBAdapter] Initialization error: {ex}"); + throw; + } + } + + public void Navigate(string url) + { + if (!IsActive) + { + OnError?.Invoke("Volt Unity Web Browser not active"); + return; + } + + try + { + navigationStartTime = Time.realtimeSinceStartup; + navigationTimer = System.Diagnostics.Stopwatch.StartNew(); + + Debug.Log($"[VoltUWBAdapter] 🚀 Navigating to: {url}"); + +#if UNITY_STANDALONE_WIN || (UNITY_ANDROID && UNITY_EDITOR_WIN) || (UNITY_IPHONE && UNITY_EDITOR_WIN) + // Check if engine is ready before navigating + if (webBrowserClient != null && webBrowserClient.ReadySignalReceived) + { + webBrowserClient.LoadUrl(url); + Debug.Log($"[VoltUWBAdapter] ✅ Navigation started immediately (engine ready)"); + } + else + { + Debug.Log($"[VoltUWBAdapter] ⏳ Engine not ready, queueing navigation..."); + // Queue the navigation for when engine becomes ready + QueueNavigation(url); + } +#else + OnError?.Invoke("Navigation not supported on this platform"); +#endif + } + catch (Exception ex) + { + OnError?.Invoke($"Navigation failed: {ex.Message}"); + Debug.LogError($"[VoltUWBAdapter] Navigation error: {ex}"); + } + } + + public void ExecuteJavaScript(string script) + { + if (!isInitialized) + { + OnError?.Invoke("Volt Unity Web Browser not initialized"); + return; + } + + try + { + Debug.Log($"[VoltUWBAdapter] Executing JavaScript: {script.Substring(0, Math.Min(100, script.Length))}..."); + +#if UNITY_STANDALONE_WIN || (UNITY_ANDROID && UNITY_EDITOR_WIN) || (UNITY_IPHONE && UNITY_EDITOR_WIN) + webBrowserClient?.ExecuteJs(script); +#else + OnError?.Invoke("JavaScript execution not supported on this platform"); +#endif + } + catch (Exception ex) + { + OnError?.Invoke($"JavaScript execution failed: {ex.Message}"); + Debug.LogError($"[VoltUWBAdapter] JavaScript execution error: {ex}"); + } + } + + public void TestInputFunctionality() + { + if (!isInitialized) + { + Debug.LogWarning("[VoltUWBAdapter] ⚠️ Cannot test input - WebView not initialized"); + return; + } + + try + { + // Test if we can detect input elements and add event listeners + string testScript = @" + console.log('=== UWB INPUT TEST ==='); + + // Find all input elements + const inputs = document.querySelectorAll('input, textarea, button, select'); + console.log('Found ' + inputs.length + ' input elements'); + + // Add click listeners to test input detection + inputs.forEach((input, index) => { + input.addEventListener('click', function() { + console.log('Input clicked: ' + index + ' - ' + input.tagName + ' - ' + input.type); + }); + input.addEventListener('focus', function() { + console.log('Input focused: ' + index + ' - ' + input.tagName); + }); + }); + + // Test document click + document.addEventListener('click', function(e) { + console.log('Document clicked at: ' + e.clientX + ', ' + e.clientY); + console.log('Target: ' + e.target.tagName); + }); + + console.log('Input test listeners added'); + "; + + ExecuteJavaScript(testScript); + Debug.Log("[VoltUWBAdapter] 🧪 Input functionality test injected - check browser console for click events"); + } + catch (Exception ex) + { + Debug.LogError($"[VoltUWBAdapter] ❌ Input test failed: {ex.Message}"); + } + } + + public void SetPopupAction(PopupAction action) + { +#if UNITY_STANDALONE_WIN || (UNITY_ANDROID && UNITY_EDITOR_WIN) || (UNITY_IPHONE && UNITY_EDITOR_WIN) + if (webBrowserClient != null) + { + webBrowserClient.popupAction = action; + Debug.Log($"[VoltUWBAdapter] 🪟 Popup Action changed to: {action}"); + } + else + { + Debug.LogWarning("[VoltUWBAdapter] ⚠️ Cannot set popup action - WebBrowserClient not initialized"); + } +#endif + } + + public PopupAction GetPopupAction() + { +#if UNITY_STANDALONE_WIN || (UNITY_ANDROID && UNITY_EDITOR_WIN) || (UNITY_IPHONE && UNITY_EDITOR_WIN) + return webBrowserClient?.popupAction ?? PopupAction.Ignore; +#else + return PopupAction.Ignore; +#endif + } + + public void SendMessage(string message) + { + if (!isInitialized) + { + OnError?.Invoke("Volt Unity Web Browser not initialized"); + return; + } + + try + { + Debug.Log($"[VoltUWBAdapter] Sending message: {message}"); + + // TODO: Implement actual message sending + // webBrowserClient.SendMessage(message); + } + catch (Exception ex) + { + OnError?.Invoke($"Send message failed: {ex.Message}"); + } + } + + public void Show() + { +#if UNITY_STANDALONE_WIN || (UNITY_ANDROID && UNITY_EDITOR_WIN) || (UNITY_IPHONE && UNITY_EDITOR_WIN) + if (uwbGameObject != null) + { + uwbGameObject.SetActive(true); + + // Ensure WebView is on top + Canvas canvas = uwbGameObject.GetComponentInParent(); + if (canvas != null) + { + canvas.sortingOrder = 1000; // Very high priority + Debug.Log($"[VoltUWBAdapter] 📱 Canvas sorting order set to {canvas.sortingOrder}"); + } + + Debug.Log("[VoltUWBAdapter] 👁️ Volt Unity Web Browser shown"); + } +#endif + } + + public void Hide() + { +#if UNITY_STANDALONE_WIN || (UNITY_ANDROID && UNITY_EDITOR_WIN) || (UNITY_IPHONE && UNITY_EDITOR_WIN) + if (uwbGameObject != null) + { + uwbGameObject.SetActive(false); + Debug.Log("[VoltUWBAdapter] 🙈 Volt Unity Web Browser hidden"); + } +#endif + } + + public WebViewPerformanceMetrics GetPerformanceMetrics() + { + float initTime = isInitialized ? (engineReadyTime - initStartTime) : -1f; + long preciseInitTime = initializationTimer?.ElapsedMilliseconds ?? -1; + + return new WebViewPerformanceMetrics + { + memoryUsageMB = UnityEngine.Profiling.Profiler.GetTotalAllocatedMemory() / (1024f * 1024f), + cpuUsagePercent = 0f, // TODO: Get from UWB + renderTimeMsAvg = preciseInitTime > 0 ? preciseInitTime : 16.67f, + textureWidth = 1024, // TODO: Get from UWB + textureHeight = 768, // TODO: Get from UWB + engineVersion = $"Volt Unity Web Browser (Init: {(initTime > 0 ? $"{initTime:F2}s" : "Pending")})" + }; + } + + public void Dispose() + { + try + { + if (isInitialized) + { + // TODO: Implement proper disposal + // webBrowserClient?.Dispose(); + + IsActive = false; + isInitialized = false; + Debug.Log("[VoltUWBAdapter] Volt Unity Web Browser disposed"); + } + } + catch (Exception ex) + { + Debug.LogError($"[VoltUWBAdapter] Disposal error: {ex.Message}"); + } + } + + // Event handlers for UWB +#if UNITY_STANDALONE_WIN || (UNITY_ANDROID && UNITY_EDITOR_WIN) || (UNITY_IPHONE && UNITY_EDITOR_WIN) + private void OnLoadFinishHandler(string url) + { + Debug.Log($"[VoltUWBAdapter] 🏁 Load finished: {url}"); + + // Mark as fully initialized on first load and record timing + if (!isInitialized) + { + isInitialized = true; + engineReadyTime = Time.realtimeSinceStartup; + initializationTimer?.Stop(); + + float totalInitTime = engineReadyTime - initStartTime; + long preciseInitTime = initializationTimer?.ElapsedMilliseconds ?? 0; + + Debug.Log($"[VoltUWBAdapter] 🎉 UWB engine fully initialized!"); + Debug.Log($"[VoltUWBAdapter] ⏱️ Total initialization time: {totalInitTime:F3}s ({preciseInitTime}ms)"); + Debug.Log($"[VoltUWBAdapter] 🚀 Ready for navigation!"); + + // Process any queued navigation + if (hasQueuedNavigation && !string.IsNullOrEmpty(queuedNavigationUrl)) + { + Debug.Log($"[VoltUWBAdapter] 🔄 Processing queued navigation to: {queuedNavigationUrl}"); + string urlToLoad = queuedNavigationUrl; + queuedNavigationUrl = null; + hasQueuedNavigation = false; + + // Navigate to the queued URL + webBrowserClient.LoadUrl(urlToLoad); + return; // Don't fire OnNavigationCompleted yet, wait for the actual page + } + } + + // Check if this looks like a real webpage load + if (url.StartsWith("http")) + { + Debug.Log($"[VoltUWBAdapter] 🌐 HTTP page loaded successfully: {url}"); + + // Inject JavaScript to check page content + ExecuteJavaScript(@" + try { + const bodyContent = document.body ? document.body.innerHTML : 'No body'; + const title = document.title || 'No title'; + const readyState = document.readyState; + console.log('Page Debug Info:', { + url: window.location.href, + title: title, + readyState: readyState, + bodyLength: bodyContent.length, + hasContent: bodyContent.length > 100 + }); + + if (bodyContent.length < 100) { + console.warn('Page appears to have minimal content!'); + } + } catch(e) { + console.error('Page debug check failed:', e); + } + "); + } + else + { + Debug.Log($"[VoltUWBAdapter] 📄 Non-HTTP page loaded: {url}"); + } + + // Calculate navigation timing + float navigationTime = navigationTimer != null ? (float)navigationTimer.ElapsedMilliseconds / 1000f : 0f; + navigationTimer?.Stop(); + + Debug.Log($"[VoltUWBAdapter] ⏱️ Page load completed in {navigationTime:F3}s"); + OnNavigationCompleted?.Invoke(url); + + // Auto-execute test script to check page elements + ExecuteJavaScript(@" + try { + const result = { + hasLoginButton: !!document.querySelector('button[type=""submit""], .login-button, [class*=""login""]'), + hasEmailInput: !!document.querySelector('input[type=""email""], input[name=""email""]'), + pageTitle: document.title, + timestamp: Date.now(), + engine: 'Volt Unity Web Browser (UWB)', + platform: 'Windows (CEF)', + license: 'MIT License', + url: window.location.href + }; + console.log('UWB Test Result:', JSON.stringify(result)); + } catch(e) { + console.error('UWB Test Error:', e); + } + "); + } + + private void OnLoadStartHandler(string url) + { + float loadStartTime = navigationTimer != null ? (float)navigationTimer.ElapsedMilliseconds / 1000f : 0f; + Debug.Log($"[VoltUWBAdapter] 🚀 Load started: {url} (after {loadStartTime:F3}s)"); + + // Check if WebView is visible + if (uwbGameObject != null) + { + bool isActive = uwbGameObject.activeInHierarchy; + Canvas canvas = uwbGameObject.GetComponentInParent(); + Debug.Log($"[VoltUWBAdapter] 📱 WebView Status - Active: {isActive}, Canvas: {(canvas != null ? canvas.name : "null")}"); + + if (canvas != null) + { + Debug.Log($"[VoltUWBAdapter] 📱 Canvas - RenderMode: {canvas.renderMode}, SortingOrder: {canvas.sortingOrder}"); + } + } + } + + private Canvas FindOrCreateCanvas() + { + // Try to find existing Canvas + Canvas existingCanvas = UnityEngine.Object.FindObjectOfType(); + if (existingCanvas != null) + { + Debug.Log($"[VoltUWBAdapter] Using existing Canvas: {existingCanvas.name}"); + return existingCanvas; + } + + // Create new Canvas if none exists + GameObject canvasGO = new GameObject("VoltUWB_Canvas"); + Canvas canvas = canvasGO.AddComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + canvas.sortingOrder = 100; // Ensure WebView appears on top + + canvasGO.AddComponent(); + canvasGO.AddComponent(); + + // Create EventSystem if it doesn't exist + if (UnityEngine.Object.FindObjectOfType() == null) + { + GameObject eventSystemGO = new GameObject("VoltUWB_EventSystem"); + eventSystemGO.AddComponent(); + eventSystemGO.AddComponent(); + Debug.Log("[VoltUWBAdapter] Created EventSystem for input handling"); + } + + Debug.Log("[VoltUWBAdapter] Created new Canvas for WebView display"); + return canvas; + } + + private void QueueNavigation(string url) + { + queuedNavigationUrl = url; + hasQueuedNavigation = true; + Debug.Log($"[VoltUWBAdapter] 📝 Navigation queued: {url}"); + } + + private void ConfigureIsolatedUWBInstance() + { + try + { + Debug.Log("[VoltUWBAdapter] 🔧 Configuring COMPLETELY ISOLATED UWB instance (separate from SDK bridge)..."); + + // CRITICAL: Use completely different ports from SDK's bridge instance + var tcpLayer = ScriptableObject.CreateInstance(); + var rnd = new System.Random(); + int attempts = 0; + do + { + // Use high port range to avoid SDK's bridge ports (which use 1024-65353) + tcpLayer.inPort = rnd.Next(45000, 49999); + tcpLayer.outPort = tcpLayer.inPort + 1; + attempts++; + if (attempts > 100) break; // Prevent infinite loop + } while (!IsPortAvailable(tcpLayer.inPort) || !IsPortAvailable(tcpLayer.outPort)); + + webBrowserClient.communicationLayer = tcpLayer; + Debug.Log($"[VoltUWBAdapter] 🔌 Using isolated ports: {tcpLayer.inPort}/{tcpLayer.outPort}"); + + // CRITICAL: Set up engine configuration (this was missing!) + ConfigureUWBEngine(); + + // Configure for UI display (not API communication like SDK) + webBrowserClient.engineStartupTimeout = 15000; // 15 seconds (longer than SDK's 10s) + webBrowserClient.noSandbox = true; // Allow broader web content + + // Different cache path to avoid conflicts + var cacheDir = Path.Combine(Application.persistentDataPath, "UWB_TestCache"); + webBrowserClient.CachePath = new System.IO.FileInfo(cacheDir); + Debug.Log($"[VoltUWBAdapter] 💾 Using isolated cache: {cacheDir}"); + + // Set logging + webBrowserClient.logSeverity = LogSeverity.Debug; + + Debug.Log("[VoltUWBAdapter] ✅ Isolated UWB instance configured successfully"); + } + catch (System.Exception ex) + { + Debug.LogError($"[VoltUWBAdapter] ❌ Failed to configure isolated UWB instance: {ex}"); + } + } + + private bool IsPortAvailable(int port) + { + try + { + var tcpConnInfoArray = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners(); + return tcpConnInfoArray.All(endpoint => endpoint.Port != port); + } + catch + { + return false; + } + } + + private void ConfigureUWBEngine() + { + try + { + Debug.Log("[VoltUWBAdapter] 🔧 Configuring UWB CEF Engine for UI instance..."); + + // Create engine configuration (same as SDK but for UI instance) + var engineConfig = ScriptableObject.CreateInstance(); + engineConfig.engineAppName = "UnityWebBrowser.Engine.Cef"; + engineConfig.engineFiles = new Engine.EnginePlatformFiles[] + { + new Engine.EnginePlatformFiles() + { + platform = Platform.Windows64, + engineBaseAppLocation = "", + engineRuntimeLocation = "UWB/" +#if UNITY_EDITOR + , + engineEditorLocation = "Packages/com.immutable.passport/Runtime/ThirdParty/UnityWebBrowser/dev.voltstro.unitywebbrowser.engine.cef.win.x64@2.2.5-130.1.16/Engine~" +#endif + } + }; + + // Assign engine to our UI instance + webBrowserClient.engine = engineConfig; + + Debug.Log("[VoltUWBAdapter] ✅ CEF Engine configured successfully for UI instance"); + Debug.Log("[VoltUWBAdapter] 🎯 Engine path: Packages/com.immutable.passport/Runtime/ThirdParty/UnityWebBrowser/dev.voltstro.unitywebbrowser.engine.cef.win.x64@2.2.5-130.1.16/Engine~"); + } + catch (System.Exception ex) + { + Debug.LogError($"[VoltUWBAdapter] ❌ Failed to configure UWB engine: {ex}"); + } + } + + private void SetupInputHandler() + { + try + { + if (webBrowserUI.inputHandler == null) + { + Debug.Log("[VoltUWBAdapter] 🎮 Creating WebBrowser Input Handler..."); + +#if ENABLE_INPUT_SYSTEM + // Use new Input System if available + Debug.Log("[VoltUWBAdapter] 🆕 Using New Input System (Input System Package)"); + var inputHandler = ScriptableObject.CreateInstance(); + Debug.Log("[VoltUWBAdapter] ✅ Input Handler created: WebBrowserInputSystemHandler"); +#else + // Fall back to legacy Input Manager + Debug.Log("[VoltUWBAdapter] 🔄 Using Legacy Input Manager (Old Input System)"); + var inputHandler = ScriptableObject.CreateInstance(); + Debug.Log("[VoltUWBAdapter] ✅ Input Handler created: WebBrowserOldInputHandler"); +#endif + + // Assign it to the WebBrowserUIFull + webBrowserUI.inputHandler = inputHandler; + + Debug.Log($"[VoltUWBAdapter] ✅ Input Handler assigned to WebBrowserUIFull"); + } + else + { + Debug.Log($"[VoltUWBAdapter] ✅ Input Handler already exists: {webBrowserUI.inputHandler.GetType().Name}"); + } + } + catch (System.Exception ex) + { + Debug.LogError($"[VoltUWBAdapter] ❌ Failed to setup input handler: {ex.Message}"); + Debug.LogError($"[VoltUWBAdapter] 💡 You may need to manually assign an Input Handler in the Inspector"); + } + } + + private void ConfigureInputHandler() + { + if (webBrowserUI == null) return; + + try + { + Debug.Log("[VoltUWBAdapter] 🎮 Configuring WebBrowserUIFull input handling..."); + + // Ensure EventSystem exists + var eventSystem = UnityEngine.Object.FindObjectOfType(); + if (eventSystem == null) + { + Debug.LogWarning("[VoltUWBAdapter] ⚠️ No EventSystem found! Input may not work properly."); + } + else + { + Debug.Log($"[VoltUWBAdapter] ✅ EventSystem found: {eventSystem.name}"); + } + + // WebBrowserUIFull inherits from RawImageUwbClientInputHandler which handles input automatically + // It needs a RawImage component to work properly + var rawImage = uwbGameObject.GetComponent(); + if (rawImage == null) + { + Debug.Log("[VoltUWBAdapter] 🖼️ Adding RawImage component for WebBrowserUIFull"); + rawImage = uwbGameObject.AddComponent(); + + // Set up the RawImage to be transparent initially + rawImage.color = new Color(1, 1, 1, 1); + rawImage.raycastTarget = true; // Important for input handling! + } + + // Ensure GraphicRaycaster exists on the Canvas for UI input + Canvas canvas = uwbGameObject.GetComponentInParent(); + if (canvas != null) + { + var raycaster = canvas.GetComponent(); + if (raycaster == null) + { + Debug.Log("[VoltUWBAdapter] 🎯 Adding GraphicRaycaster to Canvas"); + raycaster = canvas.gameObject.AddComponent(); + } + } + + // Create and assign the input handler + SetupInputHandler(); + + Debug.Log("[VoltUWBAdapter] ✅ WebBrowserUIFull input configuration complete"); + Debug.Log("[VoltUWBAdapter] 💡 WebBrowserUIFull should now handle mouse clicks and keyboard input automatically"); + + // Debug input setup + Debug.Log($"[VoltUWBAdapter] 🔍 Input Debug - RawImage: {(webBrowserUI.GetComponent() != null)}"); + Debug.Log($"[VoltUWBAdapter] 🔍 Input Debug - InputHandler: {(webBrowserUI.inputHandler != null)}"); + Debug.Log($"[VoltUWBAdapter] 🔍 Input Debug - EventSystem: {(UnityEngine.Object.FindObjectOfType() != null)}"); + Debug.Log($"[VoltUWBAdapter] 🔍 Input Debug - GraphicRaycaster: {(canvas.GetComponent() != null)}"); + } + catch (System.Exception ex) + { + Debug.LogError($"[VoltUWBAdapter] ❌ Failed to configure input handler: {ex.Message}"); + } + } +#endif + } +} diff --git a/sample/Assets/Scripts/Passport/WebViewTesting/VoltUnityWebBrowserAdapter.cs.meta b/sample/Assets/Scripts/Passport/WebViewTesting/VoltUnityWebBrowserAdapter.cs.meta new file mode 100644 index 00000000..255107dc --- /dev/null +++ b/sample/Assets/Scripts/Passport/WebViewTesting/VoltUnityWebBrowserAdapter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8da6c84e33e340148bd41420b85f347d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sample/Assets/Scripts/Passport/WebViewTesting/WebViewTestManager.cs b/sample/Assets/Scripts/Passport/WebViewTesting/WebViewTestManager.cs new file mode 100644 index 00000000..8882f223 --- /dev/null +++ b/sample/Assets/Scripts/Passport/WebViewTesting/WebViewTestManager.cs @@ -0,0 +1,476 @@ +using System; +using System.Linq; +using UnityEngine; +using UnityEngine.UI; + +#if UNITY_STANDALONE_WIN || (UNITY_ANDROID && UNITY_EDITOR_WIN) || (UNITY_IPHONE && UNITY_EDITOR_WIN) +using VoltstroStudios.UnityWebBrowser; +#endif + +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace Immutable.Passport.WebViewTesting +{ + /// + /// Manages WebView testing for different packages + /// + public class WebViewTestManager : MonoBehaviour + { + [Header("Test Configuration")] + public WebViewPackage selectedPackage = WebViewPackage.VoltUnityWebBrowser; + public string testUrl = "https://passport.immutable.com/sdk-sample-app"; + public string messageTestUrl = "file:///test-message-page.html"; + + [Header("UI References")] + public Button testLoginButton; + public Button testMessagingButton; + public Button closeWebViewButton; + public Text statusOutput; + public Text performanceOutput; + + [Header("Navigation Controls")] + public InputField urlInputField; + public Button navigateButton; + public Button backButton; + public Button forwardButton; + public Button refreshButton; + public Button testInputButton; + public Button findWebViewButton; + public Button testPopupButton; + + [Header("WebView Settings")] + public int webViewWidth = 1024; + public int webViewHeight = 768; + public bool fullScreenMode = true; + + private IWebViewAdapter currentWebView; + private float startTime; + private int frameCount; + + public enum WebViewPackage + { + VoltUnityWebBrowser, // Add Volt UWB as first option + Alacrity, + UWebView2, + ZenFulcrum, + Vuplex3D + } + + void Start() + { + SetupUI(); + ShowOutput("WebView Test Manager initialized. Select a package to test."); + } + + void Update() + { + // Performance monitoring + if (currentWebView != null && currentWebView.IsActive) + { + frameCount++; + float elapsed = Time.time - startTime; + if (elapsed >= 1f) + { + float fps = frameCount / elapsed; + ShowPerformance($"FPS: {fps:F1} | Memory: {GetMemoryUsage():F1}MB"); + frameCount = 0; + startTime = Time.time; + } + } + } + + private void SetupUI() + { + if (testLoginButton != null) + testLoginButton.onClick.AddListener(TestLoginPage); + + if (testMessagingButton != null) + testMessagingButton.onClick.AddListener(TestMessagePassing); + + if (closeWebViewButton != null) + closeWebViewButton.onClick.AddListener(CloseWebView); + + // Navigation controls + if (navigateButton != null) + navigateButton.onClick.AddListener(NavigateToUrl); + + if (backButton != null) + backButton.onClick.AddListener(GoBack); + + if (forwardButton != null) + forwardButton.onClick.AddListener(GoForward); + + if (refreshButton != null) + refreshButton.onClick.AddListener(RefreshPage); + + if (testInputButton != null) + testInputButton.onClick.AddListener(TestInput); + + if (findWebViewButton != null) + findWebViewButton.onClick.AddListener(FindWebViewInHierarchy); + + if (testPopupButton != null) + testPopupButton.onClick.AddListener(TestPopupFunctionality); + + // Set default URL in input field + if (urlInputField != null) + urlInputField.text = testUrl; + } + + public void TestLoginPage() + { + ShowOutput($"Testing {selectedPackage} with login page..."); + + try + { + currentWebView = CreateWebViewAdapter(selectedPackage); + currentWebView.Initialize(webViewWidth, webViewHeight); + currentWebView.Navigate(testUrl); + currentWebView.OnNavigationCompleted += OnLoginPageLoaded; + currentWebView.OnMessageReceived += OnWebViewMessage; + + startTime = Time.time; + frameCount = 0; + + ShowOutput($"WebView created successfully. Loading {testUrl}"); + } + catch (Exception ex) + { + ShowOutput($"Failed to create WebView: {ex.Message}"); + } + } + + public void TestMessagePassing() + { + ShowOutput($"Testing {selectedPackage} message passing..."); + + try + { + currentWebView = CreateWebViewAdapter(selectedPackage); + currentWebView.Initialize(webViewWidth, webViewHeight); + currentWebView.Navigate(messageTestUrl); + currentWebView.OnMessageReceived += OnTestMessage; + + ShowOutput($"Loading message test page: {messageTestUrl}"); + } + catch (Exception ex) + { + ShowOutput($"Failed to create message test: {ex.Message}"); + } + } + + public void CloseWebView() + { + if (currentWebView != null) + { + currentWebView.Dispose(); + currentWebView = null; + ShowOutput("WebView closed."); + } + } + + public void NavigateToUrl() + { + if (currentWebView != null && urlInputField != null && !string.IsNullOrEmpty(urlInputField.text)) + { + string url = urlInputField.text; + ShowOutput($"Navigating to: {url}"); + currentWebView.Navigate(url); + } + else if (currentWebView == null) + { + ShowOutput("No active WebView. Please test a WebView first."); + } + else + { + ShowOutput("Please enter a URL to navigate to."); + } + } + + public void GoBack() + { + if (currentWebView != null) + { + // For now, we'll use JavaScript to go back + currentWebView.ExecuteJavaScript("window.history.back();"); + ShowOutput("Going back..."); + } + else + { + ShowOutput("No active WebView."); + } + } + + public void GoForward() + { + if (currentWebView != null) + { + // For now, we'll use JavaScript to go forward + currentWebView.ExecuteJavaScript("window.history.forward();"); + ShowOutput("Going forward..."); + } + else + { + ShowOutput("No active WebView."); + } + } + + public void RefreshPage() + { + if (currentWebView != null) + { + // For now, we'll use JavaScript to refresh + currentWebView.ExecuteJavaScript("window.location.reload();"); + ShowOutput("Refreshing page..."); + } + else + { + ShowOutput("No active WebView."); + } + } + + public void TestInput() + { + if (currentWebView != null) + { + // Test input functionality for Volt Unity Web Browser + if (currentWebView is VoltUnityWebBrowserAdapter voltAdapter) + { + voltAdapter.TestInputFunctionality(); + ShowOutput("Input test injected - try clicking on form fields and check console logs"); + } + else + { + ShowOutput("Input testing not implemented for this WebView adapter yet"); + } + } + else + { + ShowOutput("No active WebView."); + } + } + + public void FindWebViewInHierarchy() + { + ShowOutput("Searching for WebView components in scene..."); + +#if UNITY_STANDALONE_WIN || (UNITY_ANDROID && UNITY_EDITOR_WIN) || (UNITY_IPHONE && UNITY_EDITOR_WIN) + // Find WebView GameObjects in the scene + var webViewObjects = FindObjectsOfType(); + if (webViewObjects.Length > 0) + { + foreach (var webView in webViewObjects) + { + ShowOutput($"Found WebView: '{webView.gameObject.name}' under '{webView.transform.parent?.name ?? "Root"}'"); + Debug.Log($"[WebViewTestManager] 🔍 WebView GameObject: {webView.gameObject.name} (Active: {webView.gameObject.activeInHierarchy})"); + + // Log component details + var components = webView.GetComponents(); + Debug.Log($"[WebViewTestManager] 📋 Components on {webView.gameObject.name}: {string.Join(", ", components.Select(c => c.GetType().Name))}"); + +#if UNITY_EDITOR + // Highlight in hierarchy (Editor only) + Selection.activeGameObject = webView.gameObject; +#endif + } + } + else + { + ShowOutput("No WebView components found in scene hierarchy"); + } +#else + ShowOutput("WebView search not available on this platform"); +#endif + } + + public void TestPopupFunctionality() + { + if (currentWebView != null) + { + // Test popup functionality for Volt Unity Web Browser + if (currentWebView is VoltUnityWebBrowserAdapter voltAdapter) + { + var currentAction = voltAdapter.GetPopupAction(); + ShowOutput($"Current popup action: {currentAction}"); + + // Test popup with JavaScript + string popupTestScript = @" + console.log('=== POPUP TEST ==='); + + // Test window.open (popup) + function testPopup() { + console.log('Testing popup...'); + const popup = window.open('https://www.google.com', 'testPopup', 'width=600,height=400'); + if (popup) { + console.log('Popup opened successfully'); + } else { + console.log('Popup blocked or failed'); + } + } + + // Test external link + function testExternalLink() { + console.log('Testing external link...'); + window.open('https://github.com/Voltstro-Studios/UnityWebBrowser', '_blank'); + } + + // Add test buttons to page + const testDiv = document.createElement('div'); + testDiv.style.position = 'fixed'; + testDiv.style.top = '10px'; + testDiv.style.right = '10px'; + testDiv.style.zIndex = '9999'; + testDiv.style.background = 'rgba(0,0,0,0.8)'; + testDiv.style.color = 'white'; + testDiv.style.padding = '10px'; + testDiv.style.borderRadius = '5px'; + + const popupBtn = document.createElement('button'); + popupBtn.textContent = 'Test Popup'; + popupBtn.onclick = testPopup; + popupBtn.style.margin = '5px'; + + const linkBtn = document.createElement('button'); + linkBtn.textContent = 'Test External Link'; + linkBtn.onclick = testExternalLink; + linkBtn.style.margin = '5px'; + + testDiv.appendChild(popupBtn); + testDiv.appendChild(document.createElement('br')); + testDiv.appendChild(linkBtn); + document.body.appendChild(testDiv); + + console.log('Popup test buttons added to page'); + "; + + currentWebView.ExecuteJavaScript(popupTestScript); + ShowOutput("Popup test injected - look for test buttons in top-right corner of WebView"); + } + else + { + ShowOutput("Popup testing not implemented for this WebView adapter yet"); + } + } + else + { + ShowOutput("No active WebView."); + } + } + + private IWebViewAdapter CreateWebViewAdapter(WebViewPackage package) + { + switch (package) + { + case WebViewPackage.VoltUnityWebBrowser: + return new VoltUnityWebBrowserAdapter(); + case WebViewPackage.Alacrity: + return new AlacrityWebViewAdapter(); + case WebViewPackage.UWebView2: + return new UWebView2Adapter(); + case WebViewPackage.ZenFulcrum: + return new ZenFulcrumWebViewAdapter(); + case WebViewPackage.Vuplex3D: + return new Vuplex3DWebViewAdapter(); + default: + throw new NotSupportedException($"WebView package {package} not supported"); + } + } + + private void OnLoginPageLoaded(string url) + { + ShowOutput($"Login page loaded: {url}"); + + // Show the WebView once it's loaded + currentWebView?.Show(); + + // Test basic functionality + TestWebViewFeatures(); + } + + private void TestWebViewFeatures() + { + ShowOutput("Testing WebView features..."); + + // Test JavaScript execution + currentWebView?.ExecuteJavaScript(@" + console.log('Unity WebView test message'); + + // Test if login elements are present + const loginButton = document.querySelector('button[type=""submit""]'); + const emailInput = document.querySelector('input[type=""email""]'); + + window.unityWebViewTest = { + hasLoginButton: !!loginButton, + hasEmailInput: !!emailInput, + pageTitle: document.title, + timestamp: Date.now() + }; + + // Send test message to Unity + if (window.unityInstance) { + window.unityInstance.SendMessage('WebViewTestManager', 'OnTestResult', JSON.stringify(window.unityWebViewTest)); + } + "); + } + + private void OnWebViewMessage(string message) + { + ShowOutput($"Received message: {message}"); + + try + { + var testResult = JsonUtility.FromJson(message); + ShowOutput($"Login elements found - Button: {testResult.hasLoginButton}, Email: {testResult.hasEmailInput}"); + } + catch (Exception ex) + { + ShowOutput($"Failed to parse message: {ex.Message}"); + } + } + + private void OnTestMessage(string message) + { + ShowOutput($"Test message received: {message}"); + } + + // Called from JavaScript + public void OnTestResult(string jsonResult) + { + OnWebViewMessage(jsonResult); + } + + private void ShowOutput(string message) + { + if (statusOutput != null) + { + statusOutput.text = message; + } + + Debug.Log($"[WebViewTest] {message}"); + } + + private void ShowPerformance(string message) + { + if (performanceOutput != null) + { + performanceOutput.text = message; + } + } + + private float GetMemoryUsage() + { + return UnityEngine.Profiling.Profiler.GetTotalAllocatedMemory() / (1024f * 1024f); + } + + [System.Serializable] + public class WebViewTestResult + { + public bool hasLoginButton; + public bool hasEmailInput; + public string pageTitle; + public long timestamp; + } + } +} diff --git a/sample/Assets/Scripts/Passport/WebViewTesting/WebViewTestManager.cs.meta b/sample/Assets/Scripts/Passport/WebViewTesting/WebViewTestManager.cs.meta new file mode 100644 index 00000000..9991718b --- /dev/null +++ b/sample/Assets/Scripts/Passport/WebViewTesting/WebViewTestManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 811054e6f94ff3140a9c103882b0ce43 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sample/Assets/Scripts/Passport/WebViewTesting/WebViewTestSceneSetup.cs b/sample/Assets/Scripts/Passport/WebViewTesting/WebViewTestSceneSetup.cs new file mode 100644 index 00000000..6e784549 --- /dev/null +++ b/sample/Assets/Scripts/Passport/WebViewTesting/WebViewTestSceneSetup.cs @@ -0,0 +1,417 @@ +#if UNITY_EDITOR +using UnityEngine; +using UnityEngine.UI; +using UnityEditor; +using UnityEditor.SceneManagement; + +namespace Immutable.Passport.WebViewTesting +{ + /// + /// Editor utility to create WebView test scene + /// + public class WebViewTestSceneSetup + { + [MenuItem("Immutable/WebView Testing/Create WebView Test Scene")] + public static void CreateWebViewTestScene() + { + CreateWebViewTestSceneInternal(false); + } + + [MenuItem("Immutable/WebView Testing/Recreate WebView Test Scene")] + public static void RecreateWebViewTestScene() + { + CreateWebViewTestSceneInternal(true); + } + + private static void CreateWebViewTestSceneInternal(bool forceRecreate) + { + string scenePath = "Assets/Scenes/Passport/WebViewTest.unity"; + + if (forceRecreate) + { + Debug.Log("Recreating WebView Test Scene with updated UI (including dropdown)..."); + } + + // Create new scene + var scene = EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single); + + // Create Canvas + GameObject canvasGO = new GameObject("Canvas"); + Canvas canvas = canvasGO.AddComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + canvasGO.AddComponent(); + canvasGO.AddComponent(); + + // Create EventSystem + GameObject eventSystemGO = new GameObject("EventSystem"); + eventSystemGO.AddComponent(); + eventSystemGO.AddComponent(); + + // Create WebView Test Manager + GameObject managerGO = new GameObject("WebViewTestManager"); + WebViewTestManager manager = managerGO.AddComponent(); + + // Create UI Panel + GameObject panelGO = CreateUIPanel(canvasGO.transform); + + // Create UI Elements + CreateTestUI(panelGO.transform, manager); + + // Save scene + EditorSceneManager.SaveScene(scene, scenePath); + + Debug.Log($"WebView Test Scene created at: {scenePath}"); + } + + private static GameObject CreateUIPanel(Transform parent) + { + GameObject panelGO = new GameObject("TestPanel"); + panelGO.transform.SetParent(parent, false); + + RectTransform rectTransform = panelGO.AddComponent(); + rectTransform.anchorMin = Vector2.zero; + rectTransform.anchorMax = Vector2.one; + rectTransform.offsetMin = new Vector2(50, 50); + rectTransform.offsetMax = new Vector2(-50, -50); + + Image image = panelGO.AddComponent(); + image.color = new Color(0.1f, 0.1f, 0.1f, 0.8f); + + return panelGO; + } + + private static void CreateTestUI(Transform parent, WebViewTestManager manager) + { + // Title + CreateText(parent, "WebView Testing Framework", new Vector2(0, 200), 24, TextAnchor.MiddleCenter); + + // Package Selection + CreateText(parent, "Select WebView Package:", new Vector2(-200, 150), 16, TextAnchor.MiddleLeft); + CreateDropdown(parent, new Vector2(100, 150), manager); + + // Test Buttons + GameObject testLoginBtn = CreateButton(parent, "Test Login Page", new Vector2(-100, 100)); + GameObject testMsgBtn = CreateButton(parent, "Test Messaging", new Vector2(100, 100)); + GameObject closeBtn = CreateButton(parent, "Close WebView", new Vector2(0, 50)); + + // Navigation Controls Section + CreateText(parent, "Navigation Controls:", new Vector2(0, 0), 16, TextAnchor.MiddleCenter); + + // URL Input Field + GameObject urlInputGO = CreateInputField(parent, "Enter URL...", new Vector2(0, -30)); + + // Navigation Buttons + GameObject navigateBtn = CreateButton(parent, "Navigate", new Vector2(-150, -70)); + GameObject backBtn = CreateButton(parent, "Back", new Vector2(-50, -70)); + GameObject forwardBtn = CreateButton(parent, "Forward", new Vector2(50, -70)); + GameObject refreshBtn = CreateButton(parent, "Refresh", new Vector2(150, -70)); + + // Debug/Test Buttons + GameObject testInputBtn = CreateButton(parent, "Test Input", new Vector2(-100, -110)); + GameObject findWebViewBtn = CreateButton(parent, "Find WebView", new Vector2(0, -110)); + GameObject testPopupBtn = CreateButton(parent, "Test Popup", new Vector2(100, -110)); + + // Status Output + CreateText(parent, "Status: Ready", new Vector2(0, -150), 14, TextAnchor.MiddleCenter, "StatusOutput"); + + // Performance Output + CreateText(parent, "Performance: -", new Vector2(0, -200), 12, TextAnchor.MiddleCenter, "PerformanceOutput"); + + // Wire up manager references + manager.testLoginButton = testLoginBtn.GetComponent + + + + + + +
+ or +
+ + + +
+

+ By continuing, you agree to our Terms of Service, and acknowledge you have understood our Privacy Policy and Collection Statement +

+
+ + + +
+ + + + + diff --git a/WebviewTestPage/package-lock.json b/WebviewTestPage/package-lock.json new file mode 100644 index 00000000..d4d3a99c --- /dev/null +++ b/WebviewTestPage/package-lock.json @@ -0,0 +1,595 @@ +{ + "name": "webviewtestpage", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "webviewtestpage", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "http-server": "^14.1.1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-server": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", + "license": "MIT", + "dependencies": { + "basic-auth": "^2.0.1", + "chalk": "^4.1.2", + "corser": "^2.0.1", + "he": "^1.2.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy": "^1.18.1", + "mime": "^1.6.0", + "minimist": "^1.2.6", + "opener": "^1.5.1", + "portfinder": "^1.0.28", + "secure-compare": "3.0.1", + "union": "~0.5.0", + "url-join": "^4.0.1" + }, + "bin": { + "http-server": "bin/http-server" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/portfinder": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.37.tgz", + "integrity": "sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==", + "license": "MIT", + "dependencies": { + "async": "^3.2.6", + "debug": "^4.3.6" + }, + "engines": { + "node": ">= 10.12" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", + "license": "MIT" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dependencies": { + "qs": "^6.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "license": "MIT" + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + } + } +} diff --git a/WebviewTestPage/package.json b/WebviewTestPage/package.json new file mode 100644 index 00000000..12aa4e0c --- /dev/null +++ b/WebviewTestPage/package.json @@ -0,0 +1,15 @@ +{ + "name": "webviewtestpage", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "http-server -p 8080 -c-1" + }, + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "http-server": "^14.1.1" + } +} From 00bb77226b498dea37427614c7cdb04dc54b5400 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 1 Sep 2025 12:36:15 +1200 Subject: [PATCH 12/75] feat: add android webview implementation --- .../Private/UI/AndroidPassportWebView.cs | 333 ++++++++++++++++++ .../Private/UI/AndroidPassportWebView.cs.meta | 11 + 2 files changed, 344 insertions(+) create mode 100644 src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs create mode 100644 src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs.meta diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs new file mode 100644 index 00000000..138c3902 --- /dev/null +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs @@ -0,0 +1,333 @@ +using System; +using UnityEngine; +using UnityEngine.UI; +using Immutable.Passport.Core.Logging; + +#if UNITY_ANDROID && !UNITY_EDITOR +using Immutable.Browser.Gree; +#endif + +namespace Immutable.Passport +{ +#if UNITY_ANDROID && !UNITY_EDITOR + /// + /// Android implementation of IPassportWebView using Gree WebView (Android WebView) + /// Wraps Gree WebViewObject in a clean, platform-agnostic interface + /// Very similar to iOS implementation but uses Android's native WebView + /// + public class AndroidPassportWebView : IPassportWebView + { + private const string TAG = "[AndroidPassportWebView]"; + + // Gree WebView components + private WebViewObject webViewObject; + private GameObject webViewGameObject; + + // Configuration and state + private RawImage targetRawImage; + private MonoBehaviour coroutineRunner; + private PassportWebViewConfig config; + private bool isInitialized = false; + private bool isVisible = false; + private string currentUrl = ""; + + // Events + public event Action OnJavaScriptMessage; + public event Action OnLoadFinished; + public event Action OnLoadStarted; + + // Properties + public bool IsVisible => isVisible; + public string CurrentUrl => currentUrl; + + /// + /// Constructor for Android PassportWebView + /// + /// RawImage component to display WebView content (not used on Android - native overlay) + /// MonoBehaviour to run coroutines (for consistency with other platforms) + public AndroidPassportWebView(RawImage targetRawImage, MonoBehaviour coroutineRunner) + { + this.targetRawImage = targetRawImage ?? throw new ArgumentNullException(nameof(targetRawImage)); + this.coroutineRunner = coroutineRunner ?? throw new ArgumentNullException(nameof(coroutineRunner)); + + PassportLogger.Info($"{TAG} Android WebView wrapper created"); + } + + public void Initialize(PassportWebViewConfig config) + { + if (isInitialized) + { + PassportLogger.Warn($"{TAG} Already initialized, skipping"); + return; + } + + this.config = config ?? new PassportWebViewConfig(); + + try + { + PassportLogger.Info($"{TAG} Initializing Android WebView with Gree WebView..."); + + CreateWebViewObject(); + ConfigureWebView(); + + isInitialized = true; + PassportLogger.Info($"{TAG} Android WebView initialized successfully"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to initialize: {ex.Message}"); + throw; + } + } + + public void LoadUrl(string url) + { + if (!isInitialized) + { + PassportLogger.Error($"{TAG} Cannot load URL - WebView not initialized"); + return; + } + + if (string.IsNullOrEmpty(url)) + { + PassportLogger.Error($"{TAG} Cannot load empty URL"); + return; + } + + try + { + PassportLogger.Info($"{TAG} Loading URL: {url}"); + currentUrl = url; + + // Android implementation: Use LaunchAuthURL to open in external browser + // This follows the same pattern as iOS and the main Passport SDK for Android authentication + webViewObject.LaunchAuthURL(url, "immutablerunner://callback"); + PassportLogger.Info($"{TAG} URL launched in external browser: {url}"); + + // Trigger load started event + OnLoadStarted?.Invoke(); + + // For Android, we consider the load "finished" immediately since it opens externally + OnLoadFinished?.Invoke(); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to load URL: {ex.Message}"); + } + } + + public void Show() + { + if (!isInitialized) + { + PassportLogger.Error($"{TAG} Cannot show - WebView not initialized"); + return; + } + + try + { + PassportLogger.Info($"{TAG} Showing WebView (Android uses external browser)"); + + // Android implementation: WebView is always "shown" since we use external browser + // The actual display happens when LoadUrl calls LaunchAuthURL + isVisible = true; + + PassportLogger.Info($"{TAG} WebView shown successfully"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to show WebView: {ex.Message}"); + } + } + + public void Hide() + { + if (!isInitialized) + { + PassportLogger.Warn($"{TAG} Cannot hide - WebView not initialized"); + return; + } + + try + { + PassportLogger.Info($"{TAG} Hiding WebView (Android external browser will close automatically)"); + + // Android implementation: External browser closes automatically after auth + // No explicit hide needed + isVisible = false; + + PassportLogger.Info($"{TAG} WebView hidden successfully"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to hide WebView: {ex.Message}"); + } + } + + public void ExecuteJavaScript(string js) + { + if (!isInitialized) + { + PassportLogger.Error($"{TAG} Cannot execute JavaScript - WebView not initialized"); + return; + } + + try + { + PassportLogger.Debug($"{TAG} Executing JavaScript: {js.Substring(0, Math.Min(100, js.Length))}..."); + webViewObject.EvaluateJS(js); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to execute JavaScript: {ex.Message}"); + } + } + + public void RegisterJavaScriptMethod(string methodName, Action handler) + { + if (!isInitialized) + { + PassportLogger.Error($"{TAG} Cannot register JavaScript method - WebView not initialized"); + return; + } + + try + { + PassportLogger.Info($"{TAG} Registering JavaScript method: {methodName}"); + + // Android implementation: Since we use external browser (Chrome Custom Tabs), + // JavaScript methods are handled through deep links and auth callbacks + // The login data will be captured via the auth callback when the external browser + // redirects back to the app with immutablerunner://callback + + PassportLogger.Info($"{TAG} JavaScript method '{methodName}' registered (handled via deep link callbacks on Android)"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to register JavaScript method: {ex.Message}"); + } + } + + public void Dispose() + { + try + { + PassportLogger.Info($"{TAG} Disposing Android WebView"); + + // Destroy WebView GameObject + if (webViewGameObject != null) + { + UnityEngine.Object.DestroyImmediate(webViewGameObject); + webViewGameObject = null; + } + + // Clear references + webViewObject = null; + targetRawImage = null; + coroutineRunner = null; + + isInitialized = false; + isVisible = false; + + PassportLogger.Info($"{TAG} Android WebView disposed successfully"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Error during disposal: {ex.Message}"); + } + } + + #region Private Implementation + + private void CreateWebViewObject() + { + PassportLogger.Info($"{TAG} Creating Gree WebViewObject..."); + + // Create GameObject for WebView + webViewGameObject = new GameObject("PassportUI_Android_WebView"); + UnityEngine.Object.DontDestroyOnLoad(webViewGameObject); + + // Add WebViewObject component + webViewObject = webViewGameObject.AddComponent(); + + PassportLogger.Info($"{TAG} Gree WebViewObject created successfully"); + } + + private void ConfigureWebView() + { + PassportLogger.Info($"{TAG} Configuring Gree WebView..."); + + // Initialize WebViewObject with callbacks + webViewObject.Init( + cb: OnWebViewMessage, + httpErr: OnWebViewHttpError, + err: OnWebViewError, + auth: OnWebViewAuth, + log: OnWebViewLog + ); + + // Clear cache if requested + if (config.ClearCacheOnInit) + { + webViewObject.ClearCache(true); + PassportLogger.Info($"{TAG} WebView cache cleared"); + } + + PassportLogger.Info($"{TAG} Gree WebView configured successfully"); + } + + #endregion + + #region WebView Event Handlers + + private void OnWebViewMessage(string message) + { + try + { + PassportLogger.Debug($"{TAG} WebView message: {message}"); + + // Parse message to see if it's a JavaScript method call + if (message.Contains("method") && message.Contains("data")) + { + // This is a JavaScript method call from our registered methods + OnJavaScriptMessage?.Invoke(message); + } + else + { + // Regular WebView message + OnJavaScriptMessage?.Invoke(message); + } + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Error handling WebView message: {ex.Message}"); + } + } + + private void OnWebViewHttpError(string id, string message) + { + PassportLogger.Error($"{TAG} WebView HTTP error [{id}]: {message}"); + } + + private void OnWebViewError(string id, string message) + { + PassportLogger.Error($"{TAG} WebView error [{id}]: {message}"); + } + + private void OnWebViewAuth(string message) + { + PassportLogger.Info($"{TAG} WebView auth message: {message}"); + // Auth messages could be login completion, etc. + OnJavaScriptMessage?.Invoke(message); + } + + private void OnWebViewLog(string message) + { + PassportLogger.Debug($"{TAG} WebView console log: {message}"); + } + + #endregion + } +#endif +} + diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs.meta b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs.meta new file mode 100644 index 00000000..b2dc7c01 --- /dev/null +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ca562827a0f98a7489504f4aa7ffe2dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 552886286ab20646e54d9ac7ec98fa210bee3282 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 1 Sep 2025 12:36:30 +1200 Subject: [PATCH 13/75] feat: add ios webview implementation --- .../Scripts/Private/UI/iOSPassportWebView.cs | 335 ++++++++++++++++++ .../Private/UI/iOSPassportWebView.cs.meta | 11 + 2 files changed, 346 insertions(+) create mode 100644 src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs create mode 100644 src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs.meta diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs new file mode 100644 index 00000000..3c732079 --- /dev/null +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs @@ -0,0 +1,335 @@ +using System; +using UnityEngine; +using UnityEngine.UI; +using Immutable.Passport.Core.Logging; + +#if UNITY_IOS && !UNITY_EDITOR +using Immutable.Browser.Gree; +#endif + +namespace Immutable.Passport +{ +#if UNITY_IOS && !UNITY_EDITOR + /// + /// iOS implementation of IPassportWebView using Gree WebView (WKWebView) + /// Wraps Gree WebViewObject in a clean, platform-agnostic interface + /// + public class iOSPassportWebView : IPassportWebView + { + private const string TAG = "[iOSPassportWebView]"; + + // Gree WebView components + private WebViewObject webViewObject; + private GameObject webViewGameObject; + + // Configuration and state + private RawImage targetRawImage; + private MonoBehaviour coroutineRunner; + private PassportWebViewConfig config; + private bool isInitialized = false; + private bool isVisible = false; + private string currentUrl = ""; + + // Events + public event Action OnJavaScriptMessage; + public event Action OnLoadFinished; + public event Action OnLoadStarted; + + // Properties + public bool IsVisible => isVisible; + public string CurrentUrl => currentUrl; + + /// + /// Constructor for iOS PassportWebView + /// + /// RawImage component to display WebView content (not used on iOS - native overlay) + /// MonoBehaviour to run coroutines (for consistency with other platforms) + public iOSPassportWebView(RawImage targetRawImage, MonoBehaviour coroutineRunner) + { + this.targetRawImage = targetRawImage ?? throw new ArgumentNullException(nameof(targetRawImage)); + this.coroutineRunner = coroutineRunner ?? throw new ArgumentNullException(nameof(coroutineRunner)); + + PassportLogger.Info($"{TAG} iOS WebView wrapper created"); + } + + public void Initialize(PassportWebViewConfig config) + { + if (isInitialized) + { + PassportLogger.Warn($"{TAG} Already initialized, skipping"); + return; + } + + this.config = config ?? new PassportWebViewConfig(); + + try + { + PassportLogger.Info($"{TAG} Initializing iOS WebView with Gree WebView..."); + + CreateWebViewObject(); + ConfigureWebView(); + + isInitialized = true; + PassportLogger.Info($"{TAG} iOS WebView initialized successfully"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to initialize: {ex.Message}"); + throw; + } + } + + public void LoadUrl(string url) + { + if (!isInitialized) + { + PassportLogger.Error($"{TAG} Cannot load URL - WebView not initialized"); + return; + } + + if (string.IsNullOrEmpty(url)) + { + PassportLogger.Error($"{TAG} Cannot load empty URL"); + return; + } + + try + { + PassportLogger.Info($"{TAG} Loading URL: {url}"); + currentUrl = url; + + // iOS implementation: Use LaunchAuthURL to open in external browser (Safari/SFSafariViewController) + // This follows the same pattern as the main Passport SDK for iOS authentication + webViewObject.LaunchAuthURL(url, "immutablerunner://callback"); + PassportLogger.Info($"{TAG} URL launched in external browser: {url}"); + + // Trigger load started event + OnLoadStarted?.Invoke(); + + // For iOS, we consider the load "finished" immediately since it opens externally + OnLoadFinished?.Invoke(); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to load URL: {ex.Message}"); + } + } + + public void Show() + { + if (!isInitialized) + { + PassportLogger.Error($"{TAG} Cannot show - WebView not initialized"); + return; + } + + try + { + PassportLogger.Info($"{TAG} Showing WebView (iOS uses external browser)"); + + // iOS implementation: WebView is always "shown" since we use external browser + // The actual display happens when LoadUrl calls LaunchAuthURL + isVisible = true; + + PassportLogger.Info($"{TAG} WebView shown successfully"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to show WebView: {ex.Message}"); + } + } + + public void Hide() + { + if (!isInitialized) + { + PassportLogger.Warn($"{TAG} Cannot hide - WebView not initialized"); + return; + } + + try + { + PassportLogger.Info($"{TAG} Hiding WebView (iOS external browser will close automatically)"); + + // iOS implementation: External browser closes automatically after auth + // No explicit hide needed + isVisible = false; + + PassportLogger.Info($"{TAG} WebView hidden successfully"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to hide WebView: {ex.Message}"); + } + } + + public void ExecuteJavaScript(string js) + { + if (!isInitialized) + { + PassportLogger.Error($"{TAG} Cannot execute JavaScript - WebView not initialized"); + return; + } + + try + { + PassportLogger.Debug($"{TAG} Executing JavaScript: {js.Substring(0, Math.Min(100, js.Length))}..."); + webViewObject.EvaluateJS(js); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to execute JavaScript: {ex.Message}"); + } + } + + public void RegisterJavaScriptMethod(string methodName, Action handler) + { + if (!isInitialized) + { + PassportLogger.Error($"{TAG} Cannot register JavaScript method - WebView not initialized"); + return; + } + + try + { + PassportLogger.Info($"{TAG} Registering JavaScript method: {methodName}"); + + // iOS implementation: Since we use external browser (Safari/SFSafariViewController), + // JavaScript methods are handled through deep links and auth callbacks + // The login data will be captured via the auth callback when the external browser + // redirects back to the app with immutablerunner://callback + + PassportLogger.Info($"{TAG} JavaScript method '{methodName}' registered (handled via deep link callbacks on iOS)"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to register JavaScript method: {ex.Message}"); + } + } + + public void Dispose() + { + try + { + PassportLogger.Info($"{TAG} Disposing iOS WebView"); + + // Destroy WebView GameObject + if (webViewGameObject != null) + { + UnityEngine.Object.DestroyImmediate(webViewGameObject); + webViewGameObject = null; + } + + // Clear references + webViewObject = null; + targetRawImage = null; + coroutineRunner = null; + + isInitialized = false; + isVisible = false; + + PassportLogger.Info($"{TAG} iOS WebView disposed successfully"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Error during disposal: {ex.Message}"); + } + } + + #region Private Implementation + + private void CreateWebViewObject() + { + PassportLogger.Info($"{TAG} Creating Gree WebViewObject..."); + + // Create GameObject for WebView + webViewGameObject = new GameObject("PassportUI_iOS_WebView"); + UnityEngine.Object.DontDestroyOnLoad(webViewGameObject); + + // Add WebViewObject component + webViewObject = webViewGameObject.AddComponent(); + + PassportLogger.Info($"{TAG} Gree WebViewObject created successfully"); + } + + private void ConfigureWebView() + { + PassportLogger.Info($"{TAG} Configuring Gree WebView..."); + + // Initialize WebViewObject with callbacks + webViewObject.Init( + cb: OnWebViewMessage, + httpErr: OnWebViewHttpError, + err: OnWebViewError, + auth: OnWebViewAuth, + log: OnWebViewLog + ); + + // Configure WebView settings + webViewObject.SetMargins(0, 0, 0, 0); // Full screen + webViewObject.SetVisibility(false); // Start hidden + + // Clear cache if requested + if (config.ClearCacheOnInit) + { + webViewObject.ClearCache(true); + PassportLogger.Info($"{TAG} WebView cache cleared"); + } + + PassportLogger.Info($"{TAG} Gree WebView configured successfully"); + } + + #endregion + + #region WebView Event Handlers + + private void OnWebViewMessage(string message) + { + try + { + PassportLogger.Debug($"{TAG} WebView message: {message}"); + + // Parse message to see if it's a JavaScript method call + if (message.Contains("method") && message.Contains("data")) + { + // This is a JavaScript method call from our registered methods + OnJavaScriptMessage?.Invoke(message); + } + else + { + // Regular WebView message + OnJavaScriptMessage?.Invoke(message); + } + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Error handling WebView message: {ex.Message}"); + } + } + + private void OnWebViewHttpError(string id, string message) + { + PassportLogger.Error($"{TAG} WebView HTTP error [{id}]: {message}"); + } + + private void OnWebViewError(string id, string message) + { + PassportLogger.Error($"{TAG} WebView error [{id}]: {message}"); + } + + private void OnWebViewAuth(string message) + { + PassportLogger.Info($"{TAG} WebView auth message: {message}"); + // Auth messages could be login completion, etc. + OnJavaScriptMessage?.Invoke(message); + } + + private void OnWebViewLog(string message) + { + PassportLogger.Debug($"{TAG} WebView console log: {message}"); + } + + #endregion + } +#endif +} diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs.meta b/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs.meta new file mode 100644 index 00000000..37c5a7bf --- /dev/null +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 40556bd2822651041b083fe251fc73e5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From d942218481344921dc22ea7709c51d88fadf6051 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 1 Sep 2025 12:37:18 +1200 Subject: [PATCH 14/75] feat: update webview class and interface to handle ios and andriod --- .../Runtime/Scripts/Private/UI/WindowsPassportWebView.cs | 4 ++-- .../Passport/Runtime/Scripts/Public/PassportUI.cs | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/WindowsPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/WindowsPassportWebView.cs index fc053c97..da60cf90 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/WindowsPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/WindowsPassportWebView.cs @@ -18,7 +18,7 @@ using System.Runtime.InteropServices; #endif -#if !IMMUTABLE_CUSTOM_BROWSER && UNITY_STANDALONE_WIN +#if !IMMUTABLE_CUSTOM_BROWSER && (UNITY_STANDALONE_WIN || (UNITY_EDITOR && UNITY_EDITOR_WIN)) using VoltstroStudios.UnityWebBrowser; using VoltstroStudios.UnityWebBrowser.Core; using VoltstroStudios.UnityWebBrowser.Core.Engines; @@ -32,7 +32,7 @@ namespace Immutable.Passport { -#if !IMMUTABLE_CUSTOM_BROWSER && UNITY_STANDALONE_WIN +#if !IMMUTABLE_CUSTOM_BROWSER && (UNITY_STANDALONE_WIN || (UNITY_EDITOR && UNITY_EDITOR_WIN)) /// /// Windows implementation of IPassportWebView using Volt Unity Web Browser (UWB) /// Wraps all UWB-specific functionality in a clean, platform-agnostic interface diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index 079af865..1cc8f4d9 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -194,17 +194,15 @@ private void CreateWebView() /// private IPassportWebView CreatePlatformWebView() { -#if !IMMUTABLE_CUSTOM_BROWSER && UNITY_STANDALONE_WIN +#if !IMMUTABLE_CUSTOM_BROWSER && (UNITY_STANDALONE_WIN || (UNITY_EDITOR && UNITY_EDITOR_WIN)) PassportLogger.Info($"{TAG} Creating Windows WebView (UWB)"); return new WindowsPassportWebView(rawImage, this); #elif UNITY_IOS && !UNITY_EDITOR PassportLogger.Info($"{TAG} Creating iOS WebView (WKWebView)"); - // TODO: Implement iOS WebView - throw new NotImplementedException("iOS WebView not yet implemented"); + return new iOSPassportWebView(rawImage, this); #elif UNITY_ANDROID && !UNITY_EDITOR PassportLogger.Info($"{TAG} Creating Android WebView"); - // TODO: Implement Android WebView - throw new NotImplementedException("Android WebView not yet implemented"); + return new AndroidPassportWebView(rawImage, this); #elif UNITY_STANDALONE_OSX PassportLogger.Info($"{TAG} Creating macOS WebView (WKWebView)"); // TODO: Implement macOS WebView From 2c0295de64ddb780bad78bc6415f6a7eaaeff449 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 1 Sep 2025 12:38:07 +1200 Subject: [PATCH 15/75] fix: marketing consent hack --- .../Passport/Runtime/Scripts/Private/PassportImpl.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs b/src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs index b5c91bc2..e717e048 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs @@ -298,6 +298,11 @@ private async UniTask LaunchAuthUrl() if (response != null && response.success == true && response.result != null) { var url = response.result.Replace(" ", "+"); + + // force marketing consent to true for now + // TODO: remove this once we have a way to get the marketing consent from the user + url = url + "&marketingConsent=opted_in"; + #if UNITY_ANDROID && !UNITY_EDITOR loginPKCEUrl = url; SendAuthEvent(_pkceLoginOnly ? PassportAuthEvent.LoginPKCELaunchingCustomTabs : PassportAuthEvent.ConnectImxPKCELaunchingCustomTabs); @@ -774,7 +779,7 @@ public interface PKCECallback { /// - /// Called when the Android Chrome Custom Tabs is hidden. + /// Called when the Android Chrome Custom Tabs is hidden. /// Note that you won't be able to tell whether it was closed by the user or the SDK. /// True if the user has entered everything required (e.g. email address), /// Chrome Custom Tabs have closed, and the SDK is trying to complete the PKCE flow. From c34e3c6c3155354c2c55761b89beb33f1ec7835a Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 1 Sep 2025 13:30:06 +1200 Subject: [PATCH 16/75] fix: build settings --- .../ProjectSettings/EditorBuildSettings.asset | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/sample/ProjectSettings/EditorBuildSettings.asset b/sample/ProjectSettings/EditorBuildSettings.asset index dfbb5fca..fea8f891 100644 --- a/sample/ProjectSettings/EditorBuildSettings.asset +++ b/sample/ProjectSettings/EditorBuildSettings.asset @@ -8,31 +8,34 @@ EditorBuildSettings: - enabled: 0 path: Assets/Scenes/Passport/Initialisation.unity guid: bb0668e0c95b745ce8e2f127d5940ede - - enabled: 0 + - enabled: 1 + path: Assets/Scenes/Passport/InitialisationWithUI.unity + guid: b588e10e8f614e0458aee90c7d02a499 + - enabled: 1 path: Assets/Scenes/Passport/UnauthenticatedScene.unity guid: 2cda990e2423bbf4892e6590ba056729 - - enabled: 0 + - enabled: 1 path: Assets/Scenes/Passport/AuthenticatedScene.unity guid: 48b17d6cb0b0f409a9edf831addcbc0a - - enabled: 0 + - enabled: 1 path: Assets/Scenes/Passport/ZkEvm/ZkEvmSendTransaction.unity guid: a7c5223614c2d4ff7ac5b06a02ef956c - - enabled: 0 + - enabled: 1 path: Assets/Scenes/Passport/ZkEvm/ZkEvmGetBalance.unity guid: 996b0bbb9b6464417b845459e3e8d764 - - enabled: 0 + - enabled: 1 path: Assets/Scenes/Passport/ZkEvm/ZkEvmGetTransactionReceipt.unity guid: ee999224a19ee442d998a452e74dab8c - - enabled: 0 + - enabled: 1 path: Assets/Scenes/Passport/Imx/ImxNftTransfer.unity guid: 2f14d9e7f1e6941d3bc021f86377a3c9 - - enabled: 0 + - enabled: 1 path: Assets/Scenes/Passport/ZkEvm/ZkEvmSignTypedData.unity guid: 7947e157cd8d541138343d5eba099466 - - enabled: 0 + - enabled: 1 path: Assets/Scenes/Passport/Other/SetCallTimeout.unity guid: 73ba07ed56efd1949b722042d50dc444 - - enabled: 0 + - enabled: 1 path: Assets/Scenes/Other/LaunchBrowser.unity guid: 7c72c29391829c742a018e4f2e629313 - enabled: 0 @@ -50,7 +53,7 @@ EditorBuildSettings: - enabled: 0 path: Assets/Scenes/Marketplace/BridgeScene.unity guid: ce7072a24f12e4c91b658c798755b355 - - enabled: 1 + - enabled: 0 path: Assets/Scenes/Passport/WebViewTest.unity guid: c8e2712d59c2d9a479d9177e52dd28c5 m_configObjects: {} From fc2dcd4e9526fefbe6a73cd8e283f16fd5129123 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 1 Sep 2025 13:59:05 +1200 Subject: [PATCH 17/75] fix: compilation flags --- .../Runtime/Scripts/Private/UI/AndroidPassportWebView.cs | 4 ++-- src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs index 138c3902..49230e97 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs @@ -3,13 +3,13 @@ using UnityEngine.UI; using Immutable.Passport.Core.Logging; -#if UNITY_ANDROID && !UNITY_EDITOR +#if UNITY_ANDROID using Immutable.Browser.Gree; #endif namespace Immutable.Passport { -#if UNITY_ANDROID && !UNITY_EDITOR +#if UNITY_ANDROID /// /// Android implementation of IPassportWebView using Gree WebView (Android WebView) /// Wraps Gree WebViewObject in a clean, platform-agnostic interface diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index 1cc8f4d9..349b1eb3 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -197,10 +197,10 @@ private IPassportWebView CreatePlatformWebView() #if !IMMUTABLE_CUSTOM_BROWSER && (UNITY_STANDALONE_WIN || (UNITY_EDITOR && UNITY_EDITOR_WIN)) PassportLogger.Info($"{TAG} Creating Windows WebView (UWB)"); return new WindowsPassportWebView(rawImage, this); -#elif UNITY_IOS && !UNITY_EDITOR +#elif UNITY_IOS || UNITY_EDITOR_OSX PassportLogger.Info($"{TAG} Creating iOS WebView (WKWebView)"); return new iOSPassportWebView(rawImage, this); -#elif UNITY_ANDROID && !UNITY_EDITOR +#elif UNITY_ANDROID PassportLogger.Info($"{TAG} Creating Android WebView"); return new AndroidPassportWebView(rawImage, this); #elif UNITY_STANDALONE_OSX From 04a8e8fdf7f69c05ce94902c8bd8bdae7583292e Mon Sep 17 00:00:00 2001 From: ImmutableJeffrey Date: Thu, 4 Sep 2025 17:43:56 +1000 Subject: [PATCH 18/75] feat: add vuplex webview for android (#3956) --- .../Immutable.Passport.Runtime.Private.asmdef | 3 +- .../Private/UI/AndroidPassportWebView.cs | 2 +- .../Private/UI/AndroidVuplexWebView.cs | 171 ++++++++++++++++++ .../Private/UI/AndroidVuplexWebView.cs.meta | 2 + .../Runtime/Scripts/Public/PassportUI.cs | 2 +- 5 files changed, 177 insertions(+), 3 deletions(-) create mode 100644 src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs create mode 100644 src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs.meta diff --git a/src/Packages/Passport/Runtime/Scripts/Private/Immutable.Passport.Runtime.Private.asmdef b/src/Packages/Passport/Runtime/Scripts/Private/Immutable.Passport.Runtime.Private.asmdef index 289b573b..5ca03f58 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/Immutable.Passport.Runtime.Private.asmdef +++ b/src/Packages/Passport/Runtime/Scripts/Private/Immutable.Passport.Runtime.Private.asmdef @@ -6,7 +6,8 @@ "UniTask", "Immutable.Browser.Core", "Immutable.Browser.Gree", - "Immutable.Passport.Core.Logging" + "Immutable.Passport.Core.Logging", + "Vuplex.WebView" ], "includePlatforms": [ "Android", diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs index 49230e97..662f2397 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs @@ -248,7 +248,7 @@ private void CreateWebViewObject() UnityEngine.Object.DontDestroyOnLoad(webViewGameObject); // Add WebViewObject component - webViewObject = webViewGameObject.AddComponent(); + webViewObject = new WebViewObject(); PassportLogger.Info($"{TAG} Gree WebViewObject created successfully"); } diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs new file mode 100644 index 00000000..2836c113 --- /dev/null +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.UI; +using Immutable.Passport.Core.Logging; +using Vuplex.WebView; + +namespace Immutable.Passport +{ + public class AndroidVuplexWebView : IPassportWebView + { + private const string TAG = "[AndroidWebView]"; + private CanvasWebViewPrefab _webViewPrefab; + private readonly Dictionary> _jsHandlers = new Dictionary>(); + private readonly RawImage _canvasReference; + private bool _isInitialized = false; + + public event Action OnJavaScriptMessage; + public event Action OnLoadFinished; + public event Action OnLoadStarted; + + // Safe access - check initialization + public bool IsVisible => _webViewPrefab?.Visible ?? false; + public string CurrentUrl => _webViewPrefab?.WebView?.Url ?? ""; + + public AndroidVuplexWebView(RawImage canvasReference) + { + _canvasReference = canvasReference ?? throw new ArgumentNullException(nameof(canvasReference)); + } + + public void Initialize(PassportWebViewConfig config) + { + if (_isInitialized) + { + PassportLogger.Warn($"{TAG} Already initialized, skipping"); + return; + } + + try + { + PassportLogger.Info($"{TAG} Initializing Vuplex WebView..."); + + // Start async initialization but don't wait + InitializeAsync(config).Forget(); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to initialize: {ex.Message}"); + throw; + } + } + + private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) + { + try + { + // Create WebView prefab and parent to Canvas + _webViewPrefab = CanvasWebViewPrefab.Instantiate(); + _webViewPrefab.Native2DModeEnabled = true; + + // Must be child of Canvas for Vuplex to work + _webViewPrefab.transform.SetParent(_canvasReference.canvas.transform, false); + + // Set up full-screen layout for Native 2D Mode + var rect = _webViewPrefab.GetComponent(); + rect.anchorMin = Vector2.zero; + rect.anchorMax = Vector2.one; + rect.offsetMin = rect.offsetMax = Vector2.zero; + + // Wait for WebView initialization + await _webViewPrefab.WaitUntilInitialized(); + + // Setup event handlers + _webViewPrefab.WebView.LoadProgressChanged += (s, e) => + { + if (e.Type == ProgressChangeType.Started) + { + OnLoadStarted?.Invoke(); + } + else if (e.Type == ProgressChangeType.Finished) + { + OnLoadFinished?.Invoke(); + } + }; + _webViewPrefab.WebView.MessageEmitted += (s, e) => + { + foreach (var h in _jsHandlers) + { + if (e.Value.StartsWith($"{h.Key}:")) + { + h.Value?.Invoke(e.Value.Substring(h.Key.Length + 1)); + return; + } + } + + OnJavaScriptMessage?.Invoke(e.Value); + }; + _webViewPrefab.WebView.LoadFailed += (s, e) => PassportLogger.Warn($"{TAG} Load failed: {e.NativeErrorCode} for {e.Url}"); + + _isInitialized = true; + PassportLogger.Info($"{TAG} Vuplex WebView initialized successfully"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to initialize async: {ex.Message}"); + throw; + } + } + + public void LoadUrl(string url) + { + if (!_isInitialized || _webViewPrefab?.WebView == null) + { + PassportLogger.Error($"{TAG} Cannot load URL - WebView not initialized"); + return; + } + + _webViewPrefab.WebView.LoadUrl(url); + } + + public void Show() + { + if (_webViewPrefab != null) + { + _webViewPrefab.Visible = true; + } + } + + public void Hide() + { + if (_webViewPrefab != null) + { + _webViewPrefab.Visible = false; + } + } + + public void ExecuteJavaScript(string js) + { + if (!_isInitialized || _webViewPrefab?.WebView == null) + { + PassportLogger.Error($"{TAG} Cannot execute JavaScript - WebView not initialized"); + return; + } + + _webViewPrefab.WebView.ExecuteJavaScript(js); + } + + public void RegisterJavaScriptMethod(string methodName, Action handler) + { + _jsHandlers[methodName] = handler; + + if (_isInitialized && _webViewPrefab?.WebView != null) + { + ExecuteJavaScript($"window.{methodName}=d=>window.vuplex?.postMessage('{methodName}:'+(typeof d==='object'?JSON.stringify(d):d))"); + } + } + + public void Dispose() + { + if (_webViewPrefab != null) + { + _webViewPrefab.Destroy(); + _webViewPrefab = null; + } + + _jsHandlers.Clear(); + _isInitialized = false; + } + } +} \ No newline at end of file diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs.meta b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs.meta new file mode 100644 index 00000000..e22e2750 --- /dev/null +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ee900f6b1f028034f8399f83385c3a44 \ No newline at end of file diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index 349b1eb3..0cf1b64a 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -202,7 +202,7 @@ private IPassportWebView CreatePlatformWebView() return new iOSPassportWebView(rawImage, this); #elif UNITY_ANDROID PassportLogger.Info($"{TAG} Creating Android WebView"); - return new AndroidPassportWebView(rawImage, this); + return new AndroidVuplexWebView(rawImage); #elif UNITY_STANDALONE_OSX PassportLogger.Info($"{TAG} Creating macOS WebView (WKWebView)"); // TODO: Implement macOS WebView From be29c5a60c65490f8bb33fb792066e20ceb2746b Mon Sep 17 00:00:00 2001 From: ImmutableJeffrey Date: Mon, 8 Sep 2025 09:29:19 +1000 Subject: [PATCH 19/75] fix: linting errors --- .../Runtime/Scripts/Private/UI/AndroidPassportWebView.cs | 8 ++++---- .../Runtime/Scripts/Private/UI/WindowsPassportWebView.cs | 4 ++-- .../Runtime/Scripts/Private/UI/iOSPassportWebView.cs | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs index 662f2397..8d0d15a9 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs @@ -237,7 +237,7 @@ public void Dispose() } } - #region Private Implementation + #region Private Implementation private void CreateWebViewObject() { @@ -276,9 +276,9 @@ private void ConfigureWebView() PassportLogger.Info($"{TAG} Gree WebView configured successfully"); } - #endregion + #endregion - #region WebView Event Handlers + #region WebView Event Handlers private void OnWebViewMessage(string message) { @@ -326,7 +326,7 @@ private void OnWebViewLog(string message) PassportLogger.Debug($"{TAG} WebView console log: {message}"); } - #endregion + #endregion } #endif } diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/WindowsPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/WindowsPassportWebView.cs index da60cf90..d8afe9b9 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/WindowsPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/WindowsPassportWebView.cs @@ -355,7 +355,7 @@ public void Dispose() } } - #region Private Implementation + #region Private Implementation private void CreateUWBInstance() { @@ -689,7 +689,7 @@ private bool IsPortAvailable(int port) } } - #endregion + #endregion } #endif } diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs index 3c732079..119aeecf 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs @@ -236,7 +236,7 @@ public void Dispose() } } - #region Private Implementation + #region Private Implementation private void CreateWebViewObject() { @@ -279,9 +279,9 @@ private void ConfigureWebView() PassportLogger.Info($"{TAG} Gree WebView configured successfully"); } - #endregion + #endregion - #region WebView Event Handlers + #region WebView Event Handlers private void OnWebViewMessage(string message) { @@ -329,7 +329,7 @@ private void OnWebViewLog(string message) PassportLogger.Debug($"{TAG} WebView console log: {message}"); } - #endregion + #endregion } #endif } From 8e79a2a1a1a702bb319d0e42ee0a7b01db0431ed Mon Sep 17 00:00:00 2001 From: ImmutableJeffrey Date: Mon, 8 Sep 2025 12:27:35 +1000 Subject: [PATCH 20/75] fix: android pr checks --- .../Private/UI/AndroidVuplexWebView.cs | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs index 2836c113..a90d43cf 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs @@ -1,28 +1,45 @@ +#nullable enable using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.UI; using Immutable.Passport.Core.Logging; + + #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE using Vuplex.WebView; +#endif namespace Immutable.Passport { + /// + /// Android implementation of IPassportWebView using Vuplex WebView + /// Provides embedded WebView functionality within the Unity app (not external browser) + /// This is different from AndroidPassportWebView which uses external browser for auth flows + /// public class AndroidVuplexWebView : IPassportWebView { - private const string TAG = "[AndroidWebView]"; - private CanvasWebViewPrefab _webViewPrefab; + private const string TAG = "[AndroidVuplexWebView]"; + + #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE + private CanvasWebViewPrefab? _webViewPrefab; +#endif private readonly Dictionary> _jsHandlers = new Dictionary>(); private readonly RawImage _canvasReference; private bool _isInitialized = false; - public event Action OnJavaScriptMessage; - public event Action OnLoadFinished; - public event Action OnLoadStarted; + public event Action? OnJavaScriptMessage; + public event Action? OnLoadFinished; + public event Action? OnLoadStarted; // Safe access - check initialization + #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE public bool IsVisible => _webViewPrefab?.Visible ?? false; public string CurrentUrl => _webViewPrefab?.WebView?.Url ?? ""; +#else + public bool IsVisible => false; + public string CurrentUrl => ""; +#endif public AndroidVuplexWebView(RawImage canvasReference) { @@ -37,6 +54,7 @@ public void Initialize(PassportWebViewConfig config) return; } + #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE try { PassportLogger.Info($"{TAG} Initializing Vuplex WebView..."); @@ -49,8 +67,13 @@ public void Initialize(PassportWebViewConfig config) PassportLogger.Error($"{TAG} Failed to initialize: {ex.Message}"); throw; } +#else + PassportLogger.Warn($"{TAG} Vuplex WebView is only supported on Android builds, not in editor"); + _isInitialized = true; +#endif } + #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) { try @@ -107,9 +130,11 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) throw; } } +#endif public void LoadUrl(string url) { + #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE if (!_isInitialized || _webViewPrefab?.WebView == null) { PassportLogger.Error($"{TAG} Cannot load URL - WebView not initialized"); @@ -117,26 +142,34 @@ public void LoadUrl(string url) } _webViewPrefab.WebView.LoadUrl(url); +#else + PassportLogger.Warn($"{TAG} LoadUrl not supported in editor mode"); +#endif } public void Show() { + #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE if (_webViewPrefab != null) { _webViewPrefab.Visible = true; } +#endif } public void Hide() { + #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE if (_webViewPrefab != null) { _webViewPrefab.Visible = false; } +#endif } public void ExecuteJavaScript(string js) { + #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE if (!_isInitialized || _webViewPrefab?.WebView == null) { PassportLogger.Error($"{TAG} Cannot execute JavaScript - WebView not initialized"); @@ -144,28 +177,33 @@ public void ExecuteJavaScript(string js) } _webViewPrefab.WebView.ExecuteJavaScript(js); +#endif } public void RegisterJavaScriptMethod(string methodName, Action handler) { _jsHandlers[methodName] = handler; + #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE if (_isInitialized && _webViewPrefab?.WebView != null) { ExecuteJavaScript($"window.{methodName}=d=>window.vuplex?.postMessage('{methodName}:'+(typeof d==='object'?JSON.stringify(d):d))"); } +#endif } public void Dispose() { + #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE if (_webViewPrefab != null) { _webViewPrefab.Destroy(); _webViewPrefab = null; } +#endif _jsHandlers.Clear(); _isInitialized = false; } } -} \ No newline at end of file +} From cedaa6a17d25396072a3e02e1fa173b01017f988 Mon Sep 17 00:00:00 2001 From: ImmutableJeffrey Date: Mon, 8 Sep 2025 12:53:14 +1000 Subject: [PATCH 21/75] fix: linting errors --- .../Private/UI/AndroidVuplexWebView.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs index a90d43cf..526a4bf8 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs @@ -6,7 +6,7 @@ using UnityEngine.UI; using Immutable.Passport.Core.Logging; - #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE using Vuplex.WebView; #endif @@ -20,8 +20,8 @@ namespace Immutable.Passport public class AndroidVuplexWebView : IPassportWebView { private const string TAG = "[AndroidVuplexWebView]"; - - #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE + +#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE private CanvasWebViewPrefab? _webViewPrefab; #endif private readonly Dictionary> _jsHandlers = new Dictionary>(); @@ -33,7 +33,7 @@ public class AndroidVuplexWebView : IPassportWebView public event Action? OnLoadStarted; // Safe access - check initialization - #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE public bool IsVisible => _webViewPrefab?.Visible ?? false; public string CurrentUrl => _webViewPrefab?.WebView?.Url ?? ""; #else @@ -54,7 +54,7 @@ public void Initialize(PassportWebViewConfig config) return; } - #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE try { PassportLogger.Info($"{TAG} Initializing Vuplex WebView..."); @@ -73,7 +73,7 @@ public void Initialize(PassportWebViewConfig config) #endif } - #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) { try @@ -134,7 +134,7 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) public void LoadUrl(string url) { - #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE if (!_isInitialized || _webViewPrefab?.WebView == null) { PassportLogger.Error($"{TAG} Cannot load URL - WebView not initialized"); @@ -149,7 +149,7 @@ public void LoadUrl(string url) public void Show() { - #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE if (_webViewPrefab != null) { _webViewPrefab.Visible = true; @@ -159,7 +159,7 @@ public void Show() public void Hide() { - #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE if (_webViewPrefab != null) { _webViewPrefab.Visible = false; @@ -169,7 +169,7 @@ public void Hide() public void ExecuteJavaScript(string js) { - #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE if (!_isInitialized || _webViewPrefab?.WebView == null) { PassportLogger.Error($"{TAG} Cannot execute JavaScript - WebView not initialized"); @@ -184,7 +184,7 @@ public void RegisterJavaScriptMethod(string methodName, Action handler) { _jsHandlers[methodName] = handler; - #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE if (_isInitialized && _webViewPrefab?.WebView != null) { ExecuteJavaScript($"window.{methodName}=d=>window.vuplex?.postMessage('{methodName}:'+(typeof d==='object'?JSON.stringify(d):d))"); @@ -194,7 +194,7 @@ public void RegisterJavaScriptMethod(string methodName, Action handler) public void Dispose() { - #if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE if (_webViewPrefab != null) { _webViewPrefab.Destroy(); From 1a6ae2e709344ade393c2b1c54111de1b3ac3dab Mon Sep 17 00:00:00 2001 From: ImmutableJeffrey Date: Mon, 8 Sep 2025 13:38:57 +1000 Subject: [PATCH 22/75] fix: ios build errors --- .../Runtime/Scripts/Private/UI/iOSPassportWebView.cs | 9 +++------ src/Packages/Passport/Runtime/Scripts/Public/Passport.cs | 1 + .../Passport/Runtime/Scripts/Public/PassportUI.cs | 3 --- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs index 119aeecf..64daa8ef 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs @@ -3,13 +3,13 @@ using UnityEngine.UI; using Immutable.Passport.Core.Logging; -#if UNITY_IOS && !UNITY_EDITOR +#if UNITY_IOS || UNITY_EDITOR_OSX using Immutable.Browser.Gree; #endif namespace Immutable.Passport { -#if UNITY_IOS && !UNITY_EDITOR +#if UNITY_IOS || UNITY_EDITOR_OSX /// /// iOS implementation of IPassportWebView using Gree WebView (WKWebView) /// Wraps Gree WebViewObject in a clean, platform-agnostic interface @@ -246,8 +246,7 @@ private void CreateWebViewObject() webViewGameObject = new GameObject("PassportUI_iOS_WebView"); UnityEngine.Object.DontDestroyOnLoad(webViewGameObject); - // Add WebViewObject component - webViewObject = webViewGameObject.AddComponent(); + webViewObject = new WebViewObject(); PassportLogger.Info($"{TAG} Gree WebViewObject created successfully"); } @@ -266,8 +265,6 @@ private void ConfigureWebView() ); // Configure WebView settings - webViewObject.SetMargins(0, 0, 0, 0); // Full screen - webViewObject.SetVisibility(false); // Start hidden // Clear cache if requested if (config.ClearCacheOnInit) diff --git a/src/Packages/Passport/Runtime/Scripts/Public/Passport.cs b/src/Packages/Passport/Runtime/Scripts/Public/Passport.cs index 83fc4e27..0d667317 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/Passport.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/Passport.cs @@ -237,6 +237,7 @@ private async UniTask Initialise( #elif (UNITY_ANDROID && !UNITY_EDITOR_WIN) || (UNITY_IPHONE && !UNITY_EDITOR_WIN) || UNITY_STANDALONE_OSX || UNITY_WEBGL // Initialise default browser client for Android, iOS, and macOS _webBrowserClient = new GreeBrowserClient(); + await UniTask.CompletedTask; #else throw new PassportException("Platform not supported"); #endif diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index 0cf1b64a..f2b2f033 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -83,7 +83,6 @@ public class PassportUI : MonoBehaviour private RawImage rawImage; // Login completion source removed - OAuth handled by external browser private bool isInitialized = false; - private bool pointerEnterTriggered = false; private GameObject bridgeWebViewGameObject; // Input management @@ -459,8 +458,6 @@ public void HideLoginUI(bool logMessage = true) bridgeWebViewGameObject = null; // Clear reference } - // Reset pointer enter flag for next login - pointerEnterTriggered = false; } /// From a7ac7f948811adb104a0190de14b82ba8634700a Mon Sep 17 00:00:00 2001 From: ImmutableJeffrey Date: Mon, 8 Sep 2025 19:08:11 +1000 Subject: [PATCH 23/75] fix: test fail on windows --- sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs b/sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs index ec47df36..262029ff 100644 --- a/sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs +++ b/sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs @@ -60,6 +60,6 @@ public static void NavigateToAuthenticatedScene() public static void NavigateToUnauthenticatedScene() { - UnityEngine.SceneManagement.SceneManager.LoadScene("InitialisationWithUI"); + UnityEngine.SceneManagement.SceneManager.LoadScene("UnauthenticatedScene"); } } \ No newline at end of file From cc384e4555d68e08d20fd88ae4869c814e8693b0 Mon Sep 17 00:00:00 2001 From: ImmutableJeffrey Date: Fri, 12 Sep 2025 10:58:20 +1000 Subject: [PATCH 24/75] chore: remove vuplex available --- .../Private/UI/AndroidVuplexWebView.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs index 526a4bf8..4223d63a 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs @@ -6,7 +6,7 @@ using UnityEngine.UI; using Immutable.Passport.Core.Logging; -#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR using Vuplex.WebView; #endif @@ -21,7 +21,7 @@ public class AndroidVuplexWebView : IPassportWebView { private const string TAG = "[AndroidVuplexWebView]"; -#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR private CanvasWebViewPrefab? _webViewPrefab; #endif private readonly Dictionary> _jsHandlers = new Dictionary>(); @@ -33,7 +33,7 @@ public class AndroidVuplexWebView : IPassportWebView public event Action? OnLoadStarted; // Safe access - check initialization -#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR public bool IsVisible => _webViewPrefab?.Visible ?? false; public string CurrentUrl => _webViewPrefab?.WebView?.Url ?? ""; #else @@ -54,7 +54,7 @@ public void Initialize(PassportWebViewConfig config) return; } -#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR try { PassportLogger.Info($"{TAG} Initializing Vuplex WebView..."); @@ -73,7 +73,7 @@ public void Initialize(PassportWebViewConfig config) #endif } -#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) { try @@ -134,7 +134,7 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) public void LoadUrl(string url) { -#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR if (!_isInitialized || _webViewPrefab?.WebView == null) { PassportLogger.Error($"{TAG} Cannot load URL - WebView not initialized"); @@ -149,7 +149,7 @@ public void LoadUrl(string url) public void Show() { -#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR if (_webViewPrefab != null) { _webViewPrefab.Visible = true; @@ -159,7 +159,7 @@ public void Show() public void Hide() { -#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR if (_webViewPrefab != null) { _webViewPrefab.Visible = false; @@ -169,7 +169,7 @@ public void Hide() public void ExecuteJavaScript(string js) { -#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR if (!_isInitialized || _webViewPrefab?.WebView == null) { PassportLogger.Error($"{TAG} Cannot execute JavaScript - WebView not initialized"); @@ -184,7 +184,7 @@ public void RegisterJavaScriptMethod(string methodName, Action handler) { _jsHandlers[methodName] = handler; -#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR if (_isInitialized && _webViewPrefab?.WebView != null) { ExecuteJavaScript($"window.{methodName}=d=>window.vuplex?.postMessage('{methodName}:'+(typeof d==='object'?JSON.stringify(d):d))"); @@ -194,7 +194,7 @@ public void RegisterJavaScriptMethod(string methodName, Action handler) public void Dispose() { -#if UNITY_ANDROID && !UNITY_EDITOR && VUPLEX_AVAILABLE && VUPLEX_AVAILABLE +#if UNITY_ANDROID && !UNITY_EDITOR if (_webViewPrefab != null) { _webViewPrefab.Destroy(); From 2ca31ecd18b8e6f63e94a88ea518d053717833cc Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 15 Sep 2025 12:57:41 +1200 Subject: [PATCH 25/75] chore: ignore vuplex package --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 6aaa93d8..bee1729b 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,8 @@ sample/mono_crash* # Vuplex sample/Assets/Vuplex* +Vuplex/ +Vuplex.meta __pycache__/ *.pyc From c96f405e7d731c71093be002d6b5f5c96866cc69 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 15 Sep 2025 12:58:28 +1200 Subject: [PATCH 26/75] feat: add vuplex callback to mock login page --- WebviewTestPage/index.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/WebviewTestPage/index.html b/WebviewTestPage/index.html index ddad2395..89b4d03b 100644 --- a/WebviewTestPage/index.html +++ b/WebviewTestPage/index.html @@ -353,6 +353,9 @@

Continue to Passport Sample Application - Sandbox

if (typeof uwb !== 'undefined' && uwb.ExecuteJsMethod) { uwb.ExecuteJsMethod('HandleLoginData', JSON.stringify(loginData)); } + if (window.vuplex && window.vuplex.postMessage) { + window.vuplex.postMessage('HandleLoginData:' + JSON.stringify(loginData)); + } } function handleEmailLogin(event) { @@ -380,6 +383,9 @@

Continue to Passport Sample Application - Sandbox

if (typeof uwb !== 'undefined' && uwb.ExecuteJsMethod) { uwb.ExecuteJsMethod('HandleLoginData', JSON.stringify(loginData)); } + if (window.vuplex && window.vuplex.postMessage) { + window.vuplex.postMessage('HandleLoginData:' + JSON.stringify(loginData)); + } } // Log when page loads From 0f6c011120ad6029c7272f938a8d6e7ef16ccb0c Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 15 Sep 2025 12:59:16 +1200 Subject: [PATCH 27/75] chore: update unity android webview meta file --- .../Scripts/Private/UI/AndroidVuplexWebView.cs.meta | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs.meta b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs.meta index e22e2750..f2b1d18c 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs.meta +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: ee900f6b1f028034f8399f83385c3a44 \ No newline at end of file +guid: ee900f6b1f028034f8399f83385c3a44 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From fca7fe60e0e65cabd67be71530f88a46c81715cc Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 15 Sep 2025 13:02:04 +1200 Subject: [PATCH 28/75] feat: update ios vuplex webview --- .../Scripts/Private/UI/IPassportWebView.cs | 3 +- .../Scripts/Private/UI/iOSPassportWebView.cs | 369 ++++++------------ .../Runtime/Scripts/Public/PassportUI.cs | 10 +- 3 files changed, 132 insertions(+), 250 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs index b96da11d..6aca08bb 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs @@ -6,7 +6,8 @@ namespace Immutable.Passport /// Platform abstraction interface for PassportUI WebView implementations. /// Provides a unified API for different WebView technologies across platforms: /// - Windows: Volt Unity Web Browser (UWB) with Chromium CEF - /// - iOS/macOS: Gree WebView with WKWebView + /// - macOS: Volt Unity Web Browser (UWB) with Chromium CEF + /// - iOS: Vuplex 3D WebView with WKWebView /// - Android: Gree WebView with Android WebView ///
public interface IPassportWebView diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs index 64daa8ef..d1e77a82 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs @@ -1,332 +1,213 @@ +#nullable enable using System; +using System.Collections.Generic; +using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.UI; using Immutable.Passport.Core.Logging; -#if UNITY_IOS || UNITY_EDITOR_OSX -using Immutable.Browser.Gree; +#if UNITY_IOS && !UNITY_EDITOR +using Vuplex.WebView; #endif namespace Immutable.Passport { -#if UNITY_IOS || UNITY_EDITOR_OSX /// - /// iOS implementation of IPassportWebView using Gree WebView (WKWebView) - /// Wraps Gree WebViewObject in a clean, platform-agnostic interface + /// iOS implementation of IPassportWebView using Vuplex WebView + /// Provides embedded WebView functionality within the Unity app (not external browser) + /// This is different from iOSPassportWebView which uses external browser for auth flows /// public class iOSPassportWebView : IPassportWebView { private const string TAG = "[iOSPassportWebView]"; - // Gree WebView components - private WebViewObject webViewObject; - private GameObject webViewGameObject; - - // Configuration and state - private RawImage targetRawImage; - private MonoBehaviour coroutineRunner; - private PassportWebViewConfig config; - private bool isInitialized = false; - private bool isVisible = false; - private string currentUrl = ""; - - // Events - public event Action OnJavaScriptMessage; - public event Action OnLoadFinished; - public event Action OnLoadStarted; - - // Properties - public bool IsVisible => isVisible; - public string CurrentUrl => currentUrl; +#if UNITY_IOS && !UNITY_EDITOR + private CanvasWebViewPrefab? _webViewPrefab; +#endif + private readonly Dictionary> _jsHandlers = new Dictionary>(); + private readonly RawImage _canvasReference; + private bool _isInitialized = false; + + public event Action? OnJavaScriptMessage; + public event Action? OnLoadFinished; + public event Action? OnLoadStarted; + + // Safe access - check initialization +#if UNITY_IOS && !UNITY_EDITOR + public bool IsVisible => _webViewPrefab?.Visible ?? false; + public string CurrentUrl => _webViewPrefab?.WebView?.Url ?? ""; +#else + public bool IsVisible => false; + public string CurrentUrl => ""; +#endif - /// - /// Constructor for iOS PassportWebView - /// - /// RawImage component to display WebView content (not used on iOS - native overlay) - /// MonoBehaviour to run coroutines (for consistency with other platforms) - public iOSPassportWebView(RawImage targetRawImage, MonoBehaviour coroutineRunner) + public iOSPassportWebView(RawImage canvasReference) { - this.targetRawImage = targetRawImage ?? throw new ArgumentNullException(nameof(targetRawImage)); - this.coroutineRunner = coroutineRunner ?? throw new ArgumentNullException(nameof(coroutineRunner)); - - PassportLogger.Info($"{TAG} iOS WebView wrapper created"); + _canvasReference = canvasReference ?? throw new ArgumentNullException(nameof(canvasReference)); } public void Initialize(PassportWebViewConfig config) { - if (isInitialized) + if (_isInitialized) { PassportLogger.Warn($"{TAG} Already initialized, skipping"); return; } - this.config = config ?? new PassportWebViewConfig(); - +#if UNITY_IOS && !UNITY_EDITOR try { - PassportLogger.Info($"{TAG} Initializing iOS WebView with Gree WebView..."); - - CreateWebViewObject(); - ConfigureWebView(); + PassportLogger.Info($"{TAG} Initializing iOS WebView..."); - isInitialized = true; - PassportLogger.Info($"{TAG} iOS WebView initialized successfully"); + // Start async initialization but don't wait + InitializeAsync(config).Forget(); } catch (Exception ex) { PassportLogger.Error($"{TAG} Failed to initialize: {ex.Message}"); throw; } +#else + PassportLogger.Warn($"{TAG} Vuplex WebView is only supported on iOS builds, not in editor"); + _isInitialized = true; +#endif } - public void LoadUrl(string url) +#if UNITY_IOS && !UNITY_EDITOR + private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) { - if (!isInitialized) - { - PassportLogger.Error($"{TAG} Cannot load URL - WebView not initialized"); - return; - } - - if (string.IsNullOrEmpty(url)) - { - PassportLogger.Error($"{TAG} Cannot load empty URL"); - return; - } - try { - PassportLogger.Info($"{TAG} Loading URL: {url}"); - currentUrl = url; + // Create WebView prefab and parent to Canvas + _webViewPrefab = CanvasWebViewPrefab.Instantiate(); + _webViewPrefab.Native2DModeEnabled = false; // Disable Native2DMode to avoid Unity integration issues + + // Set reasonable resolution - much lower to avoid texture size issues + _webViewPrefab.Resolution = 1.0f; // 1px per Unity unit - creates 800x600px texture - // iOS implementation: Use LaunchAuthURL to open in external browser (Safari/SFSafariViewController) - // This follows the same pattern as the main Passport SDK for iOS authentication - webViewObject.LaunchAuthURL(url, "immutablerunner://callback"); - PassportLogger.Info($"{TAG} URL launched in external browser: {url}"); + // Must be child of Canvas for Vuplex to work + _webViewPrefab.transform.SetParent(_canvasReference.canvas.transform, false); - // Trigger load started event - OnLoadStarted?.Invoke(); + // Set reasonable fixed size instead of full-screen to avoid massive textures + var rect = _webViewPrefab.GetComponent(); + rect.anchorMin = new Vector2(0.5f, 0.5f); // Center anchor + rect.anchorMax = new Vector2(0.5f, 0.5f); + rect.sizeDelta = new Vector2(1000, 700); // Wider: 1000x700 for better login UX + rect.anchoredPosition = Vector2.zero; // Center position - // For iOS, we consider the load "finished" immediately since it opens externally - OnLoadFinished?.Invoke(); + // Wait for WebView initialization + await _webViewPrefab.WaitUntilInitialized(); + + // Setup event handlers + _webViewPrefab.WebView.LoadProgressChanged += (s, e) => + { + if (e.Type == ProgressChangeType.Started) + { + OnLoadStarted?.Invoke(); + } + else if (e.Type == ProgressChangeType.Finished) + { + OnLoadFinished?.Invoke(); + } + }; + _webViewPrefab.WebView.MessageEmitted += (s, e) => + { + foreach (var h in _jsHandlers) + { + if (e.Value.StartsWith($"{h.Key}:")) + { + h.Value?.Invoke(e.Value.Substring(h.Key.Length + 1)); + return; + } + } + + OnJavaScriptMessage?.Invoke(e.Value); + }; + _webViewPrefab.WebView.LoadFailed += (s, e) => PassportLogger.Warn($"{TAG} Load failed: {e.NativeErrorCode} for {e.Url}"); + + _isInitialized = true; + PassportLogger.Info($"{TAG} iOS WebView initialized successfully"); } catch (Exception ex) { - PassportLogger.Error($"{TAG} Failed to load URL: {ex.Message}"); + PassportLogger.Error($"{TAG} Failed to initialize iOS WebView: {ex.Message}"); + throw; } } +#endif - public void Show() + public void LoadUrl(string url) { - if (!isInitialized) +#if UNITY_IOS && !UNITY_EDITOR + if (!_isInitialized || _webViewPrefab?.WebView == null) { - PassportLogger.Error($"{TAG} Cannot show - WebView not initialized"); + PassportLogger.Error($"{TAG} Cannot load URL - iOS WebView not initialized"); return; } - try - { - PassportLogger.Info($"{TAG} Showing WebView (iOS uses external browser)"); - - // iOS implementation: WebView is always "shown" since we use external browser - // The actual display happens when LoadUrl calls LaunchAuthURL - isVisible = true; + _webViewPrefab.WebView.LoadUrl(url); +#else + PassportLogger.Warn($"{TAG} LoadUrl not supported in iOS editor mode"); +#endif + } - PassportLogger.Info($"{TAG} WebView shown successfully"); - } - catch (Exception ex) + public void Show() + { +#if UNITY_IOS && !UNITY_EDITOR + if (_webViewPrefab != null) { - PassportLogger.Error($"{TAG} Failed to show WebView: {ex.Message}"); + _webViewPrefab.Visible = true; } +#endif } public void Hide() { - if (!isInitialized) +#if UNITY_IOS && !UNITY_EDITOR + if (_webViewPrefab != null) { - PassportLogger.Warn($"{TAG} Cannot hide - WebView not initialized"); - return; - } - - try - { - PassportLogger.Info($"{TAG} Hiding WebView (iOS external browser will close automatically)"); - - // iOS implementation: External browser closes automatically after auth - // No explicit hide needed - isVisible = false; - - PassportLogger.Info($"{TAG} WebView hidden successfully"); - } - catch (Exception ex) - { - PassportLogger.Error($"{TAG} Failed to hide WebView: {ex.Message}"); + _webViewPrefab.Visible = false; } +#endif } public void ExecuteJavaScript(string js) { - if (!isInitialized) +#if UNITY_IOS && !UNITY_EDITOR + if (!_isInitialized || _webViewPrefab?.WebView == null) { - PassportLogger.Error($"{TAG} Cannot execute JavaScript - WebView not initialized"); + PassportLogger.Error($"{TAG} Cannot execute JavaScript - iOS WebView not initialized"); return; } - try - { - PassportLogger.Debug($"{TAG} Executing JavaScript: {js.Substring(0, Math.Min(100, js.Length))}..."); - webViewObject.EvaluateJS(js); - } - catch (Exception ex) - { - PassportLogger.Error($"{TAG} Failed to execute JavaScript: {ex.Message}"); - } + _webViewPrefab.WebView.ExecuteJavaScript(js); +#endif } public void RegisterJavaScriptMethod(string methodName, Action handler) { - if (!isInitialized) - { - PassportLogger.Error($"{TAG} Cannot register JavaScript method - WebView not initialized"); - return; - } - - try - { - PassportLogger.Info($"{TAG} Registering JavaScript method: {methodName}"); + _jsHandlers[methodName] = handler; - // iOS implementation: Since we use external browser (Safari/SFSafariViewController), - // JavaScript methods are handled through deep links and auth callbacks - // The login data will be captured via the auth callback when the external browser - // redirects back to the app with immutablerunner://callback - - PassportLogger.Info($"{TAG} JavaScript method '{methodName}' registered (handled via deep link callbacks on iOS)"); - } - catch (Exception ex) +#if UNITY_IOS && !UNITY_EDITOR + if (_isInitialized && _webViewPrefab?.WebView != null) { - PassportLogger.Error($"{TAG} Failed to register JavaScript method: {ex.Message}"); + ExecuteJavaScript($"window.{methodName}=d=>window.vuplex?.postMessage('{methodName}:'+(typeof d==='object'?JSON.stringify(d):d))"); } +#endif } public void Dispose() { - try - { - PassportLogger.Info($"{TAG} Disposing iOS WebView"); - - // Destroy WebView GameObject - if (webViewGameObject != null) - { - UnityEngine.Object.DestroyImmediate(webViewGameObject); - webViewGameObject = null; - } - - // Clear references - webViewObject = null; - targetRawImage = null; - coroutineRunner = null; - - isInitialized = false; - isVisible = false; - - PassportLogger.Info($"{TAG} iOS WebView disposed successfully"); - } - catch (Exception ex) - { - PassportLogger.Error($"{TAG} Error during disposal: {ex.Message}"); - } - } - - #region Private Implementation - - private void CreateWebViewObject() - { - PassportLogger.Info($"{TAG} Creating Gree WebViewObject..."); - - // Create GameObject for WebView - webViewGameObject = new GameObject("PassportUI_iOS_WebView"); - UnityEngine.Object.DontDestroyOnLoad(webViewGameObject); - - webViewObject = new WebViewObject(); - - PassportLogger.Info($"{TAG} Gree WebViewObject created successfully"); - } - - private void ConfigureWebView() - { - PassportLogger.Info($"{TAG} Configuring Gree WebView..."); - - // Initialize WebViewObject with callbacks - webViewObject.Init( - cb: OnWebViewMessage, - httpErr: OnWebViewHttpError, - err: OnWebViewError, - auth: OnWebViewAuth, - log: OnWebViewLog - ); - - // Configure WebView settings - - // Clear cache if requested - if (config.ClearCacheOnInit) +#if UNITY_IOS && !UNITY_EDITOR + if (_webViewPrefab != null) { - webViewObject.ClearCache(true); - PassportLogger.Info($"{TAG} WebView cache cleared"); + _webViewPrefab.Destroy(); + _webViewPrefab = null; } +#endif - PassportLogger.Info($"{TAG} Gree WebView configured successfully"); - } - - #endregion - - #region WebView Event Handlers - - private void OnWebViewMessage(string message) - { - try - { - PassportLogger.Debug($"{TAG} WebView message: {message}"); - - // Parse message to see if it's a JavaScript method call - if (message.Contains("method") && message.Contains("data")) - { - // This is a JavaScript method call from our registered methods - OnJavaScriptMessage?.Invoke(message); - } - else - { - // Regular WebView message - OnJavaScriptMessage?.Invoke(message); - } - } - catch (Exception ex) - { - PassportLogger.Error($"{TAG} Error handling WebView message: {ex.Message}"); - } - } - - private void OnWebViewHttpError(string id, string message) - { - PassportLogger.Error($"{TAG} WebView HTTP error [{id}]: {message}"); - } - - private void OnWebViewError(string id, string message) - { - PassportLogger.Error($"{TAG} WebView error [{id}]: {message}"); - } - - private void OnWebViewAuth(string message) - { - PassportLogger.Info($"{TAG} WebView auth message: {message}"); - // Auth messages could be login completion, etc. - OnJavaScriptMessage?.Invoke(message); + _jsHandlers.Clear(); + _isInitialized = false; } - - private void OnWebViewLog(string message) - { - PassportLogger.Debug($"{TAG} WebView console log: {message}"); - } - - #endregion } -#endif } diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index f2b2f033..6fe3c267 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -196,14 +196,14 @@ private IPassportWebView CreatePlatformWebView() #if !IMMUTABLE_CUSTOM_BROWSER && (UNITY_STANDALONE_WIN || (UNITY_EDITOR && UNITY_EDITOR_WIN)) PassportLogger.Info($"{TAG} Creating Windows WebView (UWB)"); return new WindowsPassportWebView(rawImage, this); -#elif UNITY_IOS || UNITY_EDITOR_OSX - PassportLogger.Info($"{TAG} Creating iOS WebView (WKWebView)"); - return new iOSPassportWebView(rawImage, this); +#elif UNITY_IOS + PassportLogger.Info($"{TAG} Creating iOS WebView (Vuplex)"); + return new iOSPassportWebView(rawImage); #elif UNITY_ANDROID - PassportLogger.Info($"{TAG} Creating Android WebView"); + PassportLogger.Info($"{TAG} Creating Android WebView (Vuplex)"); return new AndroidVuplexWebView(rawImage); #elif UNITY_STANDALONE_OSX - PassportLogger.Info($"{TAG} Creating macOS WebView (WKWebView)"); + PassportLogger.Info($"{TAG} Creating macOS WebView (Vuplex)"); // TODO: Implement macOS WebView throw new NotImplementedException("macOS WebView not yet implemented"); #else From 13fc6b56a1afc353cc80976484dbd35f37822785 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 16 Sep 2025 15:52:25 +1200 Subject: [PATCH 29/75] refactor: update login data interface to match embedded prompt page update --- .../Runtime/Scripts/Public/PassportUI.cs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index 6fe3c267..8509e96b 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -18,25 +18,29 @@ namespace Immutable.Passport { /// /// Data structure for login information received from JavaScript + /// Matches the TypeScript LoginData interface from the web page /// [System.Serializable] public class LoginData { - public string provider; + public string directLoginMethod; public string email; - public bool marketing_consent; + public string marketingConsentStatus; public override string ToString() { - return $"LoginData(provider: {provider}, email: {email}, marketing_consent: {marketing_consent})"; + return $"LoginData(directLoginMethod: {directLoginMethod}, email: {email}, marketingConsentStatus: {marketingConsentStatus})"; } } /// - /// PassportUI that follows the working WebViewTest pattern - /// Key difference: Let Unity handle component lifecycle instead of manual initialization + /// Cross-platform WebView UI component for Passport authentication. + /// Automatically selects the appropriate WebView implementation based on the target platform: + /// - Windows: Unity Web Browser (UWB) with Chromium CEF + /// - iOS/Android: Gree unity-webview with external browser integration + /// - macOS: Not yet implemented /// - /// IMPORTANT: When setting up the PassportUI prefab in the editor: + /// SETUP: When configuring the PassportUI prefab in the editor: /// - Set RawImage component's width and height to 0 in the RectTransform /// - This ensures the UI is completely hidden before initialization /// @@ -256,11 +260,11 @@ private async void HandleLoginData(string jsonData) PassportLogger.Info($"{TAG} Parsed login data: {loginData}"); // For now, just log the data - in the future this could trigger OAuth flow - PassportLogger.Info($"{TAG} Login attempt - Provider: {loginData.provider}, Email: {loginData.email}, Marketing Consent: {loginData.marketing_consent}"); + PassportLogger.Info($"{TAG} Login attempt - Method: {loginData.directLoginMethod}, Email: {loginData.email}, Marketing Consent: {loginData.marketingConsentStatus}"); // Login(bool useCachedSession = false, DirectLoginOptions directLoginOptions = null) DirectLoginMethod loginMethod; - switch (loginData.provider) + switch (loginData.directLoginMethod) { case "email": loginMethod = DirectLoginMethod.Email; @@ -275,7 +279,7 @@ private async void HandleLoginData(string jsonData) loginMethod = DirectLoginMethod.Facebook; break; default: - PassportLogger.Error($"{TAG} Invalid login provider: {loginData.provider}"); + PassportLogger.Error($"{TAG} Invalid login method: {loginData.directLoginMethod}"); return; } var loginOptions = new DirectLoginOptions(loginMethod); @@ -285,7 +289,7 @@ private async void HandleLoginData(string jsonData) } // Perform the login and handle the result - PassportLogger.Info($"{TAG} Starting login with provider: {loginData.provider}"); + PassportLogger.Info($"{TAG} Starting login with method: {loginData.directLoginMethod}"); bool loginSuccess = await _passportInstance.Login(false, loginOptions); if (loginSuccess) @@ -408,7 +412,7 @@ public async UniTask ShowLoginUI() // TESTING: Load local test page instead of auth URL PassportLogger.Info($"{TAG} 🧪 Loading local test page for development"); - string testPageUrl = "http://localhost:8080"; + string testPageUrl = "http://localhost:3001"; PassportLogger.Info($"{TAG} Test page URL: {testPageUrl}"); // Show the WebView From f275a1fd0a23a31e470c85a2984a2c39dd933a21 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 16 Sep 2025 16:15:05 +1200 Subject: [PATCH 30/75] feat: add macos vuplex webview --- .../Private/UI/MacOSPassportWebView.cs | 227 ++++++++++++++++++ .../Private/UI/MacOSPassportWebView.cs.meta | 12 + .../Runtime/Scripts/Public/PassportUI.cs | 9 +- 3 files changed, 243 insertions(+), 5 deletions(-) create mode 100644 src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs create mode 100644 src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs.meta diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs new file mode 100644 index 00000000..2e95b841 --- /dev/null +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs @@ -0,0 +1,227 @@ +#nullable enable +using System; +using System.Collections.Generic; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.UI; +using Immutable.Passport.Core.Logging; + +#if UNITY_STANDALONE_OSX && !UNITY_EDITOR +using Vuplex.WebView; +#endif + +namespace Immutable.Passport +{ + /// + /// MacOS implementation of IPassportWebView using Vuplex WebView + /// Provides embedded WebView functionality within the Unity app + /// Similar to iOS implementation but optimized for MacOS desktop environment + /// + public class MacOSPassportWebView : IPassportWebView + { + private const string TAG = "[MacOSPassportWebView]"; + +#if UNITY_STANDALONE_OSX && !UNITY_EDITOR + private CanvasWebViewPrefab? _webViewPrefab; +#endif + private readonly Dictionary> _jsHandlers = new Dictionary>(); + private readonly RawImage _canvasReference; + private bool _isInitialized = false; + + public event Action? OnJavaScriptMessage; + public event Action? OnLoadFinished; + public event Action? OnLoadStarted; + + // Safe access - check initialization +#if UNITY_STANDALONE_OSX && !UNITY_EDITOR + public bool IsVisible => _webViewPrefab?.Visible ?? false; + public string CurrentUrl => _webViewPrefab?.WebView?.Url ?? ""; +#else + public bool IsVisible => false; + public string CurrentUrl => ""; +#endif + + public MacOSPassportWebView(RawImage canvasReference) + { + _canvasReference = canvasReference ?? throw new ArgumentNullException(nameof(canvasReference)); + } + + public void Initialize(PassportWebViewConfig config) + { + if (_isInitialized) + { + PassportLogger.Warn($"{TAG} Already initialized, skipping"); + return; + } + +#if UNITY_STANDALONE_OSX && !UNITY_EDITOR + try + { + PassportLogger.Info($"{TAG} Initializing MacOS WebView..."); + + // Start async initialization but don't wait + InitializeAsync(config).Forget(); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to initialize: {ex.Message}"); + throw; + } +#else + PassportLogger.Warn($"{TAG} Vuplex WebView is only supported on MacOS builds, not in editor"); + _isInitialized = true; +#endif + } + +#if UNITY_STANDALONE_OSX && !UNITY_EDITOR + private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) + { + try + { + // Create WebView prefab and parent to Canvas + _webViewPrefab = CanvasWebViewPrefab.Instantiate(); + _webViewPrefab.Native2DModeEnabled = false; // Use standard mode for better desktop compatibility + + // Set higher resolution for desktop - MacOS can handle larger textures + _webViewPrefab.Resolution = 1.5f; // 1.5px per Unity unit for crisp rendering + + // Must be child of Canvas for Vuplex to work + _webViewPrefab.transform.SetParent(_canvasReference.canvas.transform, false); + + // Set desktop-appropriate size - larger than mobile but not full-screen + var rect = _webViewPrefab.GetComponent(); + rect.anchorMin = new Vector2(0.5f, 0.5f); // Center anchor + rect.anchorMax = new Vector2(0.5f, 0.5f); + rect.sizeDelta = new Vector2(1200, 800); // Desktop size: 1200x800 for comfortable login UX + rect.anchoredPosition = Vector2.zero; // Center position + + // Wait for WebView initialization + await _webViewPrefab.WaitUntilInitialized(); + + // Setup event handlers + _webViewPrefab.WebView.LoadProgressChanged += (s, e) => + { + if (e.Type == ProgressChangeType.Started) + { + OnLoadStarted?.Invoke(); + } + else if (e.Type == ProgressChangeType.Finished) + { + OnLoadFinished?.Invoke(); + } + }; + _webViewPrefab.WebView.MessageEmitted += (s, e) => + { + foreach (var h in _jsHandlers) + { + if (e.Value.StartsWith($"{h.Key}:")) + { + h.Value?.Invoke(e.Value.Substring(h.Key.Length + 1)); + return; + } + } + + OnJavaScriptMessage?.Invoke(e.Value); + }; + _webViewPrefab.WebView.LoadFailed += (s, e) => PassportLogger.Warn($"{TAG} Load failed: {e.NativeErrorCode} for {e.Url}"); + + _isInitialized = true; + PassportLogger.Info($"{TAG} MacOS WebView initialized successfully"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to initialize MacOS WebView: {ex.Message}"); + throw; + } + } +#endif + + public void LoadUrl(string url) + { +#if UNITY_STANDALONE_OSX && !UNITY_EDITOR + if (!_isInitialized || _webViewPrefab?.WebView == null) + { + PassportLogger.Error($"{TAG} Cannot load URL - MacOS WebView not initialized"); + return; + } + + PassportLogger.Info($"{TAG} Loading URL: {url}"); + _webViewPrefab.WebView.LoadUrl(url); +#else + PassportLogger.Warn($"{TAG} LoadUrl not supported in MacOS editor mode"); +#endif + } + + public void Show() + { +#if UNITY_STANDALONE_OSX && !UNITY_EDITOR + if (_webViewPrefab != null) + { + _webViewPrefab.Visible = true; + PassportLogger.Info($"{TAG} WebView shown"); + } +#else + PassportLogger.Info($"{TAG} Show() called (editor mode)"); +#endif + } + + public void Hide() + { +#if UNITY_STANDALONE_OSX && !UNITY_EDITOR + if (_webViewPrefab != null) + { + _webViewPrefab.Visible = false; + PassportLogger.Info($"{TAG} WebView hidden"); + } +#else + PassportLogger.Info($"{TAG} Hide() called (editor mode)"); +#endif + } + + public void ExecuteJavaScript(string js) + { +#if UNITY_STANDALONE_OSX && !UNITY_EDITOR + if (!_isInitialized || _webViewPrefab?.WebView == null) + { + PassportLogger.Error($"{TAG} Cannot execute JavaScript - MacOS WebView not initialized"); + return; + } + + _webViewPrefab.WebView.ExecuteJavaScript(js); +#else + PassportLogger.Warn($"{TAG} ExecuteJavaScript not supported in MacOS editor mode"); +#endif + } + + public void RegisterJavaScriptMethod(string methodName, Action handler) + { + _jsHandlers[methodName] = handler; + PassportLogger.Info($"{TAG} JavaScript method '{methodName}' registered"); + +#if UNITY_STANDALONE_OSX && !UNITY_EDITOR + if (_isInitialized && _webViewPrefab?.WebView != null) + { + // Register the method with Vuplex WebView using window.vuplex.postMessage + string jsCode = $"window.{methodName}=d=>window.vuplex?.postMessage('{methodName}:'+(typeof d==='object'?JSON.stringify(d):d))"; + ExecuteJavaScript(jsCode); + PassportLogger.Info($"{TAG} JavaScript method '{methodName}' registered with Vuplex"); + } +#endif + } + + public void Dispose() + { +#if UNITY_STANDALONE_OSX && !UNITY_EDITOR + if (_webViewPrefab != null) + { + PassportLogger.Info($"{TAG} Disposing MacOS WebView"); + _webViewPrefab.Destroy(); + _webViewPrefab = null; + } +#endif + + _jsHandlers.Clear(); + _isInitialized = false; + } + } +} diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs.meta b/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs.meta new file mode 100644 index 00000000..8781c0e6 --- /dev/null +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f8e4d9c2b1a5e7f3d6c8b4a9e2f7c1d5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: + diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index 8509e96b..6fe761a0 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -37,8 +37,8 @@ public override string ToString() /// Cross-platform WebView UI component for Passport authentication. /// Automatically selects the appropriate WebView implementation based on the target platform: /// - Windows: Unity Web Browser (UWB) with Chromium CEF - /// - iOS/Android: Gree unity-webview with external browser integration - /// - macOS: Not yet implemented + /// - iOS/Android: Vuplex WebView with embedded browser + /// - macOS: Vuplex WebView with embedded browser /// /// SETUP: When configuring the PassportUI prefab in the editor: /// - Set RawImage component's width and height to 0 in the RectTransform @@ -207,9 +207,8 @@ private IPassportWebView CreatePlatformWebView() PassportLogger.Info($"{TAG} Creating Android WebView (Vuplex)"); return new AndroidVuplexWebView(rawImage); #elif UNITY_STANDALONE_OSX - PassportLogger.Info($"{TAG} Creating macOS WebView (Vuplex)"); - // TODO: Implement macOS WebView - throw new NotImplementedException("macOS WebView not yet implemented"); + PassportLogger.Info($"{TAG} Creating MacOS WebView (Vuplex)"); + return new MacOSPassportWebView(rawImage); #else PassportLogger.Error($"{TAG} WebView not supported on this platform"); return null; From b4a2f5befe79040192b2ac021d214602ed8312bc Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 16 Sep 2025 16:52:15 +1200 Subject: [PATCH 31/75] feat: expose webview url as serialised field --- .../Private/UI/MacOSPassportWebView.cs | 2 +- .../Runtime/Scripts/Public/PassportUI.cs | 45 ++++++++++--------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs index 2e95b841..0af49488 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs @@ -81,7 +81,7 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) // Create WebView prefab and parent to Canvas _webViewPrefab = CanvasWebViewPrefab.Instantiate(); _webViewPrefab.Native2DModeEnabled = false; // Use standard mode for better desktop compatibility - + // Set higher resolution for desktop - MacOS can handle larger textures _webViewPrefab.Resolution = 1.5f; // 1.5px per Unity unit for crisp rendering diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index 6fe761a0..d142b283 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -73,13 +73,29 @@ public class PassportUI : MonoBehaviour [Tooltip("Unity Event triggered when login fails (with error message)")] public UnityEvent OnLoginFailure; - [Header("Debug Settings")] + [Header("Debug Settings (Windows WebView Only)")] + [Tooltip("Enable remote debugging for the Windows WebView (Volt Unity Web Browser). Not used on other platforms.")] public bool enableRemoteDebugging = false; + [Tooltip("Port for remote debugging on Windows WebView (Volt Unity Web Browser). Not used on other platforms.")] public uint remoteDebuggingPort = 9222; - [Header("Cache Settings")] + [Header("Cache Settings (Windows WebView Only)")] + [Tooltip("Clear WebView cache on login for Windows WebView (Volt Unity Web Browser). Not used on other platforms.")] public bool clearCacheOnLogin = true; + [Header("WebView Settings")] + [Tooltip("URL to load in the WebView for authentication.")] + [SerializeField] private string webViewUrl = "http://localhost:3001"; + + /// + /// Gets or sets the URL to load in the WebView for authentication + /// + public string WebViewUrl + { + get => webViewUrl; + set => webViewUrl = value; + } + // Cross-platform WebView abstraction private IPassportWebView webView; @@ -293,7 +309,7 @@ private async void HandleLoginData(string jsonData) if (loginSuccess) { - PassportLogger.Info($"{TAG} ✅ Login successful!"); + PassportLogger.Info($"{TAG} Login successful!"); // Trigger events OnLoginSuccess?.Invoke(); @@ -305,7 +321,7 @@ private async void HandleLoginData(string jsonData) else { string errorMessage = "Login failed - authentication was not completed"; - PassportLogger.Error($"{TAG} ❌ {errorMessage}"); + PassportLogger.Error($"{TAG} {errorMessage}"); // Trigger failure events OnLoginFailure?.Invoke(errorMessage); @@ -382,7 +398,7 @@ public async UniTask ShowLoginUI() PassportLogger.Info($"{TAG} Passport bridge ready - waiting for login form submission"); // CRITICAL: Initialize deep link system for OAuth callback handling - PassportLogger.Info($"{TAG} 🔗 Initializing deep link system for OAuth callbacks"); + PassportLogger.Info($"{TAG} Initializing deep link system for OAuth callbacks"); #if UNITY_STANDALONE_WIN || (UNITY_ANDROID && UNITY_EDITOR_WIN) || (UNITY_IPHONE && UNITY_EDITOR_WIN) try { @@ -396,7 +412,7 @@ public async UniTask ShowLoginUI() // Initialize deep link system with the redirect URI and PassportImpl's callback var redirectUri = "immutablerunner://callback"; WindowsDeepLink.Initialise(redirectUri, passportImpl.OnDeepLinkActivated); - PassportLogger.Info($"{TAG} ✅ Deep link system initialized for protocol: {redirectUri}"); + PassportLogger.Info($"{TAG} Deep link system initialized for protocol: {redirectUri}"); } else { @@ -409,22 +425,11 @@ public async UniTask ShowLoginUI() } #endif - // TESTING: Load local test page instead of auth URL - PassportLogger.Info($"{TAG} 🧪 Loading local test page for development"); - string testPageUrl = "http://localhost:3001"; - PassportLogger.Info($"{TAG} Test page URL: {testPageUrl}"); - - // Show the WebView webView.Show(); PassportLogger.Info($"{TAG} WebView shown"); - // Navigate to test page - webView.LoadUrl(testPageUrl); - PassportLogger.Info($"{TAG} Navigated to test page: {testPageUrl}"); - - // Test page loaded for development - PassportLogger.Info($"{TAG} 🧪 Test page loaded in WebView for development and testing."); - PassportLogger.Info($"{TAG} Login data will be captured and logged from the test page."); + webView.LoadUrl(webViewUrl); + PassportLogger.Info($"{TAG} Navigated to configured URL: {webViewUrl}"); // Return true since we successfully started the OAuth flow // The actual authentication completion is handled by the deep link system @@ -457,7 +462,7 @@ public void HideLoginUI(bool logMessage = true) if (bridgeWebViewGameObject != null && !bridgeWebViewGameObject.activeSelf) { bridgeWebViewGameObject.SetActive(true); - PassportLogger.Info($"{TAG} ✅ Re-enabled bridge WebView {bridgeWebViewGameObject.name} - UI WebView is now hidden"); + PassportLogger.Info($"{TAG} Re-enabled bridge WebView {bridgeWebViewGameObject.name} - UI WebView is now hidden"); bridgeWebViewGameObject = null; // Clear reference } From 9afcd4bf3e0a697fd01906d2b80a7143076c3582 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 16 Sep 2025 17:54:09 +1200 Subject: [PATCH 32/75] refactor: better direct login method checking --- .../Runtime/Scripts/Public/PassportUI.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index d142b283..e985634a 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -274,6 +274,13 @@ private async void HandleLoginData(string jsonData) LoginData loginData = JsonUtility.FromJson(jsonData); PassportLogger.Info($"{TAG} Parsed login data: {loginData}"); + // Validate required login data + if (string.IsNullOrEmpty(loginData.directLoginMethod)) + { + PassportLogger.Error($"{TAG} Login method is required but was null or empty"); + return; + } + // For now, just log the data - in the future this could trigger OAuth flow PassportLogger.Info($"{TAG} Login attempt - Method: {loginData.directLoginMethod}, Email: {loginData.email}, Marketing Consent: {loginData.marketingConsentStatus}"); @@ -298,8 +305,16 @@ private async void HandleLoginData(string jsonData) return; } var loginOptions = new DirectLoginOptions(loginMethod); - if (loginData.email != null) + + // Validate email is provided for email login method + if (loginMethod == DirectLoginMethod.Email) { + if (string.IsNullOrEmpty(loginData.email)) + { + PassportLogger.Error($"{TAG} Email is required for email login method but was null or empty"); + return; + } + loginOptions.email = loginData.email; } From 3709c09865c7e37f037b0313436f9a6cf225413e Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 16 Sep 2025 19:11:34 +1200 Subject: [PATCH 33/75] refactor: update vuplex webviews to follow recommended message handling --- .../Private/UI/AndroidVuplexWebView.cs | 38 ++++++++++------- .../Scripts/Private/UI/IPassportWebView.cs | 10 +++++ .../Private/UI/MacOSPassportWebView.cs | 42 ++++++++++--------- .../Scripts/Private/UI/iOSPassportWebView.cs | 40 ++++++++++-------- 4 files changed, 77 insertions(+), 53 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs index 4223d63a..1f6ffe31 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs @@ -95,31 +95,40 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) await _webViewPrefab.WaitUntilInitialized(); // Setup event handlers - _webViewPrefab.WebView.LoadProgressChanged += (s, e) => + _webViewPrefab.WebView.LoadProgressChanged += (sender, progressArgs) => { - if (e.Type == ProgressChangeType.Started) + if (progressArgs.Type == ProgressChangeType.Started) { OnLoadStarted?.Invoke(); } - else if (e.Type == ProgressChangeType.Finished) + else if (progressArgs.Type == ProgressChangeType.Finished) { OnLoadFinished?.Invoke(); } }; - _webViewPrefab.WebView.MessageEmitted += (s, e) => + _webViewPrefab.WebView.MessageEmitted += (sender, messageArgs) => { - foreach (var h in _jsHandlers) + try { - if (e.Value.StartsWith($"{h.Key}:")) + // Parse the JSON message from window.vuplex.postMessage() + var message = JsonUtility.FromJson(messageArgs.Value); + + if (_jsHandlers.ContainsKey(message.method)) { - h.Value?.Invoke(e.Value.Substring(h.Key.Length + 1)); + _jsHandlers[message.method]?.Invoke(message.data); return; } - } - OnJavaScriptMessage?.Invoke(e.Value); + PassportLogger.Warn($"{TAG} No handler registered for method: {message.method}"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to parse Vuplex message: {ex.Message}, Raw message: {messageArgs.Value}"); + // Fallback to raw message for backwards compatibility + OnJavaScriptMessage?.Invoke(messageArgs.Value); + } }; - _webViewPrefab.WebView.LoadFailed += (s, e) => PassportLogger.Warn($"{TAG} Load failed: {e.NativeErrorCode} for {e.Url}"); + _webViewPrefab.WebView.LoadFailed += (sender, failedArgs) => PassportLogger.Warn($"{TAG} Load failed: {failedArgs.NativeErrorCode} for {failedArgs.Url}"); _isInitialized = true; PassportLogger.Info($"{TAG} Vuplex WebView initialized successfully"); @@ -183,13 +192,10 @@ public void ExecuteJavaScript(string js) public void RegisterJavaScriptMethod(string methodName, Action handler) { _jsHandlers[methodName] = handler; + PassportLogger.Info($"{TAG} JavaScript method '{methodName}' registered for Vuplex message handling"); -#if UNITY_ANDROID && !UNITY_EDITOR - if (_isInitialized && _webViewPrefab?.WebView != null) - { - ExecuteJavaScript($"window.{methodName}=d=>window.vuplex?.postMessage('{methodName}:'+(typeof d==='object'?JSON.stringify(d):d))"); - } -#endif + // Note: No JavaScript injection needed with Vuplex - web page should call: + // window.vuplex.postMessage({method: 'methodName', data: 'jsonData'}) } public void Dispose() diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs index 6aca08bb..353516df 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs @@ -2,6 +2,16 @@ namespace Immutable.Passport { + /// + /// Message structure for Vuplex WebView communication + /// Matches the JSON format: {method: "MethodName", data: "..."} + /// + [System.Serializable] + public class VuplexMessage + { + public string method; + public string data; + } /// /// Platform abstraction interface for PassportUI WebView implementations. /// Provides a unified API for different WebView technologies across platforms: diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs index 0af49488..d5d15485 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs @@ -99,31 +99,40 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) await _webViewPrefab.WaitUntilInitialized(); // Setup event handlers - _webViewPrefab.WebView.LoadProgressChanged += (s, e) => + _webViewPrefab.WebView.LoadProgressChanged += (sender, progressArgs) => { - if (e.Type == ProgressChangeType.Started) + if (progressArgs.Type == ProgressChangeType.Started) { OnLoadStarted?.Invoke(); } - else if (e.Type == ProgressChangeType.Finished) + else if (progressArgs.Type == ProgressChangeType.Finished) { OnLoadFinished?.Invoke(); } }; - _webViewPrefab.WebView.MessageEmitted += (s, e) => + _webViewPrefab.WebView.MessageEmitted += (sender, messageArgs) => { - foreach (var h in _jsHandlers) + try { - if (e.Value.StartsWith($"{h.Key}:")) + // Parse the JSON message from window.vuplex.postMessage() + var message = JsonUtility.FromJson(messageArgs.Value); + + if (_jsHandlers.ContainsKey(message.method)) { - h.Value?.Invoke(e.Value.Substring(h.Key.Length + 1)); + _jsHandlers[message.method]?.Invoke(message.data); return; } - } - OnJavaScriptMessage?.Invoke(e.Value); + PassportLogger.Warn($"{TAG} No handler registered for method: {message.method}"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to parse Vuplex message: {ex.Message}, Raw message: {messageArgs.Value}"); + // Fallback to raw message for backwards compatibility + OnJavaScriptMessage?.Invoke(messageArgs.Value); + } }; - _webViewPrefab.WebView.LoadFailed += (s, e) => PassportLogger.Warn($"{TAG} Load failed: {e.NativeErrorCode} for {e.Url}"); + _webViewPrefab.WebView.LoadFailed += (sender, failedArgs) => PassportLogger.Warn($"{TAG} Load failed: {failedArgs.NativeErrorCode} for {failedArgs.Url}"); _isInitialized = true; PassportLogger.Info($"{TAG} MacOS WebView initialized successfully"); @@ -196,17 +205,10 @@ public void ExecuteJavaScript(string js) public void RegisterJavaScriptMethod(string methodName, Action handler) { _jsHandlers[methodName] = handler; - PassportLogger.Info($"{TAG} JavaScript method '{methodName}' registered"); + PassportLogger.Info($"{TAG} JavaScript method '{methodName}' registered for Vuplex message handling"); -#if UNITY_STANDALONE_OSX && !UNITY_EDITOR - if (_isInitialized && _webViewPrefab?.WebView != null) - { - // Register the method with Vuplex WebView using window.vuplex.postMessage - string jsCode = $"window.{methodName}=d=>window.vuplex?.postMessage('{methodName}:'+(typeof d==='object'?JSON.stringify(d):d))"; - ExecuteJavaScript(jsCode); - PassportLogger.Info($"{TAG} JavaScript method '{methodName}' registered with Vuplex"); - } -#endif + // Note: No JavaScript injection needed with Vuplex - web page should call: + // window.vuplex.postMessage({method: 'methodName', data: 'jsonData'}) } public void Dispose() diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs index d1e77a82..036e5f31 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs @@ -81,7 +81,7 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) // Create WebView prefab and parent to Canvas _webViewPrefab = CanvasWebViewPrefab.Instantiate(); _webViewPrefab.Native2DModeEnabled = false; // Disable Native2DMode to avoid Unity integration issues - + // Set reasonable resolution - much lower to avoid texture size issues _webViewPrefab.Resolution = 1.0f; // 1px per Unity unit - creates 800x600px texture @@ -99,31 +99,40 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) await _webViewPrefab.WaitUntilInitialized(); // Setup event handlers - _webViewPrefab.WebView.LoadProgressChanged += (s, e) => + _webViewPrefab.WebView.LoadProgressChanged += (sender, progressArgs) => { - if (e.Type == ProgressChangeType.Started) + if (progressArgs.Type == ProgressChangeType.Started) { OnLoadStarted?.Invoke(); } - else if (e.Type == ProgressChangeType.Finished) + else if (progressArgs.Type == ProgressChangeType.Finished) { OnLoadFinished?.Invoke(); } }; - _webViewPrefab.WebView.MessageEmitted += (s, e) => + _webViewPrefab.WebView.MessageEmitted += (sender, messageArgs) => { - foreach (var h in _jsHandlers) + try { - if (e.Value.StartsWith($"{h.Key}:")) + // Parse the JSON message from window.vuplex.postMessage() + var message = JsonUtility.FromJson(messageArgs.Value); + + if (_jsHandlers.ContainsKey(message.method)) { - h.Value?.Invoke(e.Value.Substring(h.Key.Length + 1)); + _jsHandlers[message.method]?.Invoke(message.data); return; } - } - OnJavaScriptMessage?.Invoke(e.Value); + PassportLogger.Warn($"{TAG} No handler registered for method: {message.method}"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to parse Vuplex message: {ex.Message}, Raw message: {messageArgs.Value}"); + // Fallback to raw message for backwards compatibility + OnJavaScriptMessage?.Invoke(messageArgs.Value); + } }; - _webViewPrefab.WebView.LoadFailed += (s, e) => PassportLogger.Warn($"{TAG} Load failed: {e.NativeErrorCode} for {e.Url}"); + _webViewPrefab.WebView.LoadFailed += (sender, failedArgs) => PassportLogger.Warn($"{TAG} Load failed: {failedArgs.NativeErrorCode} for {failedArgs.Url}"); _isInitialized = true; PassportLogger.Info($"{TAG} iOS WebView initialized successfully"); @@ -187,13 +196,10 @@ public void ExecuteJavaScript(string js) public void RegisterJavaScriptMethod(string methodName, Action handler) { _jsHandlers[methodName] = handler; + PassportLogger.Info($"{TAG} JavaScript method '{methodName}' registered for Vuplex message handling"); -#if UNITY_IOS && !UNITY_EDITOR - if (_isInitialized && _webViewPrefab?.WebView != null) - { - ExecuteJavaScript($"window.{methodName}=d=>window.vuplex?.postMessage('{methodName}:'+(typeof d==='object'?JSON.stringify(d):d))"); - } -#endif + // Note: No JavaScript injection needed with Vuplex - web page should call: + // window.vuplex.postMessage({method: 'methodName', data: 'jsonData'}) } public void Dispose() From 9e37ab91f671578b0f89fc4c8b4f9f3ed21c3e7d Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 16 Sep 2025 19:19:09 +1200 Subject: [PATCH 34/75] feat: handle error messages from login app --- .../Scripts/Private/UI/IPassportWebView.cs | 16 ++++++++ .../Runtime/Scripts/Public/PassportUI.cs | 39 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs index 353516df..ae28ca4c 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs @@ -12,6 +12,22 @@ public class VuplexMessage public string method; public string data; } + + /// + /// Error data structure for JavaScript error messages + /// Matches the TypeScript Error serialization: {message: string, name: string} + /// + [System.Serializable] + public class ErrorData + { + public string message; + public string name; + + public override string ToString() + { + return $"ErrorData(name: {name}, message: {message})"; + } + } /// /// Platform abstraction interface for PassportUI WebView implementations. /// Provides a unified API for different WebView technologies across platforms: diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index e985634a..cd8f3063 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -197,6 +197,7 @@ private void CreateWebView() // Register JavaScript methods webView.RegisterJavaScriptMethod("HandleLoginData", HandleLoginData); + webView.RegisterJavaScriptMethod("HandleLoginError", HandleLoginError); isInitialized = true; PassportLogger.Info($"{TAG} Cross-platform WebView created successfully"); @@ -369,6 +370,44 @@ private async void HandleLoginData(string jsonData) } } + /// + /// Handles error messages received from JavaScript + /// Called when the login page encounters an error and sends error information + /// + /// JSON string containing error data + private void HandleLoginError(string jsonData) + { + try + { + PassportLogger.Info($"{TAG} Received error data from JavaScript: {jsonData}"); + + // Parse the JSON error data + ErrorData errorData = JsonUtility.FromJson(jsonData); + PassportLogger.Error($"{TAG} Login page error: {errorData}"); + + // Create user-friendly error message + string errorMessage = !string.IsNullOrEmpty(errorData.message) + ? $"Login failed: {errorData.message}" + : "Login failed due to an unknown error"; + + // Trigger failure events + OnLoginFailure?.Invoke(errorMessage); + OnLoginFailureStatic?.Invoke(errorMessage); + + // Hide the WebView after error + HideLoginUI(); + } + catch (Exception ex) + { + string errorMessage = $"Failed to handle error message from JavaScript: {ex.Message}"; + PassportLogger.Error($"{TAG} {errorMessage}"); + + // Trigger failure events even if we can't parse the error + OnLoginFailure?.Invoke("Login failed due to an error processing error message"); + OnLoginFailureStatic?.Invoke("Login failed due to an error processing error message"); + } + } + private void SetupLoginButton() { if (loginButton != null) From 6bd6f37cdd3fcbe3906d67c0b6822a8c4ca9ff8b Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Wed, 17 Sep 2025 19:15:47 +1200 Subject: [PATCH 35/75] feat: add close webview handler --- .../Passport/Runtime/Scripts/Public/PassportUI.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index cd8f3063..e5d54476 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -85,7 +85,7 @@ public class PassportUI : MonoBehaviour [Header("WebView Settings")] [Tooltip("URL to load in the WebView for authentication.")] - [SerializeField] private string webViewUrl = "http://localhost:3001"; + [SerializeField] private string webViewUrl = "https://auth.immutable.com/im-embedded-login-prompt?isWebView=true&client_id=YOUR_CLIENT_ID"; /// /// Gets or sets the URL to load in the WebView for authentication @@ -198,6 +198,7 @@ private void CreateWebView() // Register JavaScript methods webView.RegisterJavaScriptMethod("HandleLoginData", HandleLoginData); webView.RegisterJavaScriptMethod("HandleLoginError", HandleLoginError); + webView.RegisterJavaScriptMethod("CloseWebView", (data) => HideLoginUI()); isInitialized = true; PassportLogger.Info($"{TAG} Cross-platform WebView created successfully"); @@ -511,15 +512,6 @@ public void HideLoginUI(bool logMessage = true) PassportLogger.Info($"{TAG} Login UI hidden"); } } - - // CRITICAL: Re-enable the bridge WebView GameObject now that UI is hidden - if (bridgeWebViewGameObject != null && !bridgeWebViewGameObject.activeSelf) - { - bridgeWebViewGameObject.SetActive(true); - PassportLogger.Info($"{TAG} Re-enabled bridge WebView {bridgeWebViewGameObject.name} - UI WebView is now hidden"); - bridgeWebViewGameObject = null; // Clear reference - } - } /// From 792e24fa0d6a074772ece5d6852b2587ace98d5d Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Thu, 18 Sep 2025 18:09:52 +1200 Subject: [PATCH 36/75] feat: use correct close webview method name --- src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index e5d54476..f10c5281 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -198,7 +198,7 @@ private void CreateWebView() // Register JavaScript methods webView.RegisterJavaScriptMethod("HandleLoginData", HandleLoginData); webView.RegisterJavaScriptMethod("HandleLoginError", HandleLoginError); - webView.RegisterJavaScriptMethod("CloseWebView", (data) => HideLoginUI()); + webView.RegisterJavaScriptMethod("HandleClose", (data) => HideLoginUI()); isInitialized = true; PassportLogger.Info($"{TAG} Cross-platform WebView created successfully"); From 24f86f6b7cdfe47a94f125e35be17493d2d01a29 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Thu, 18 Sep 2025 21:57:08 +1200 Subject: [PATCH 37/75] fix: queue webview load when webview is not initialised --- .../Private/UI/MacOSPassportWebView.cs | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs index d5d15485..9e922caf 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs @@ -6,7 +6,7 @@ using UnityEngine.UI; using Immutable.Passport.Core.Logging; -#if UNITY_STANDALONE_OSX && !UNITY_EDITOR +#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX using Vuplex.WebView; #endif @@ -21,19 +21,20 @@ public class MacOSPassportWebView : IPassportWebView { private const string TAG = "[MacOSPassportWebView]"; -#if UNITY_STANDALONE_OSX && !UNITY_EDITOR +#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX private CanvasWebViewPrefab? _webViewPrefab; #endif private readonly Dictionary> _jsHandlers = new Dictionary>(); private readonly RawImage _canvasReference; private bool _isInitialized = false; + private string? _queuedUrl = null; // Queue URL if LoadUrl called before initialization public event Action? OnJavaScriptMessage; public event Action? OnLoadFinished; public event Action? OnLoadStarted; // Safe access - check initialization -#if UNITY_STANDALONE_OSX && !UNITY_EDITOR +#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX public bool IsVisible => _webViewPrefab?.Visible ?? false; public string CurrentUrl => _webViewPrefab?.WebView?.Url ?? ""; #else @@ -54,7 +55,7 @@ public void Initialize(PassportWebViewConfig config) return; } -#if UNITY_STANDALONE_OSX && !UNITY_EDITOR +#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX try { PassportLogger.Info($"{TAG} Initializing MacOS WebView..."); @@ -73,13 +74,15 @@ public void Initialize(PassportWebViewConfig config) #endif } -#if UNITY_STANDALONE_OSX && !UNITY_EDITOR +#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) { try { + PassportLogger.Info($"{TAG} Starting Vuplex CanvasWebViewPrefab instantiation..."); // Create WebView prefab and parent to Canvas _webViewPrefab = CanvasWebViewPrefab.Instantiate(); + PassportLogger.Info($"{TAG} CanvasWebViewPrefab created successfully"); _webViewPrefab.Native2DModeEnabled = false; // Use standard mode for better desktop compatibility // Set higher resolution for desktop - MacOS can handle larger textures @@ -97,6 +100,7 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) // Wait for WebView initialization await _webViewPrefab.WaitUntilInitialized(); + PassportLogger.Info($"{TAG} Vuplex WebView initialization completed"); // Setup event handlers _webViewPrefab.WebView.LoadProgressChanged += (sender, progressArgs) => @@ -136,6 +140,15 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) _isInitialized = true; PassportLogger.Info($"{TAG} MacOS WebView initialized successfully"); + + // Load queued URL if one was requested before initialization completed + if (!string.IsNullOrEmpty(_queuedUrl)) + { + PassportLogger.Info($"{TAG} Loading queued URL: {_queuedUrl}"); + var urlToLoad = _queuedUrl; + _queuedUrl = null; // Clear the queue + _webViewPrefab.WebView.LoadUrl(urlToLoad); + } } catch (Exception ex) { @@ -147,10 +160,11 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) public void LoadUrl(string url) { -#if UNITY_STANDALONE_OSX && !UNITY_EDITOR +#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX if (!_isInitialized || _webViewPrefab?.WebView == null) { - PassportLogger.Error($"{TAG} Cannot load URL - MacOS WebView not initialized"); + PassportLogger.Info($"{TAG} WebView not ready, queueing URL: {url}"); + _queuedUrl = url; // Queue the URL for later loading return; } @@ -163,7 +177,7 @@ public void LoadUrl(string url) public void Show() { -#if UNITY_STANDALONE_OSX && !UNITY_EDITOR +#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX if (_webViewPrefab != null) { _webViewPrefab.Visible = true; @@ -176,7 +190,7 @@ public void Show() public void Hide() { -#if UNITY_STANDALONE_OSX && !UNITY_EDITOR +#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX if (_webViewPrefab != null) { _webViewPrefab.Visible = false; @@ -189,7 +203,7 @@ public void Hide() public void ExecuteJavaScript(string js) { -#if UNITY_STANDALONE_OSX && !UNITY_EDITOR +#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX if (!_isInitialized || _webViewPrefab?.WebView == null) { PassportLogger.Error($"{TAG} Cannot execute JavaScript - MacOS WebView not initialized"); @@ -213,7 +227,7 @@ public void RegisterJavaScriptMethod(string methodName, Action handler) public void Dispose() { -#if UNITY_STANDALONE_OSX && !UNITY_EDITOR +#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX if (_webViewPrefab != null) { PassportLogger.Info($"{TAG} Disposing MacOS WebView"); From f8332edf3a40f32ad930f901c9e6d881605c3d24 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Fri, 19 Sep 2025 18:35:45 +1200 Subject: [PATCH 38/75] refactor: reorganise webview implementations --- .../Private/UI/AndroidPassportWebView.cs | 333 ------------------ .../Private/UI/AndroidPassportWebView.cs.meta | 11 - .../Runtime/Scripts/Private/UI/WebViews.meta | 8 + .../UI/{ => WebViews}/AndroidVuplexWebView.cs | 28 +- .../AndroidVuplexWebView.cs.meta | 0 .../UI/{ => WebViews}/MacOSPassportWebView.cs | 10 +- .../MacOSPassportWebView.cs.meta | 0 .../{ => WebViews}/WindowsPassportWebView.cs | 49 ++- .../WindowsPassportWebView.cs.meta | 0 .../UI/{ => WebViews}/iOSPassportWebView.cs | 14 +- .../{ => WebViews}/iOSPassportWebView.cs.meta | 0 11 files changed, 88 insertions(+), 365 deletions(-) delete mode 100644 src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs delete mode 100644 src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs.meta create mode 100644 src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews.meta rename src/Packages/Passport/Runtime/Scripts/Private/UI/{ => WebViews}/AndroidVuplexWebView.cs (86%) rename src/Packages/Passport/Runtime/Scripts/Private/UI/{ => WebViews}/AndroidVuplexWebView.cs.meta (100%) rename src/Packages/Passport/Runtime/Scripts/Private/UI/{ => WebViews}/MacOSPassportWebView.cs (95%) rename src/Packages/Passport/Runtime/Scripts/Private/UI/{ => WebViews}/MacOSPassportWebView.cs.meta (100%) rename src/Packages/Passport/Runtime/Scripts/Private/UI/{ => WebViews}/WindowsPassportWebView.cs (91%) rename src/Packages/Passport/Runtime/Scripts/Private/UI/{ => WebViews}/WindowsPassportWebView.cs.meta (100%) rename src/Packages/Passport/Runtime/Scripts/Private/UI/{ => WebViews}/iOSPassportWebView.cs (93%) rename src/Packages/Passport/Runtime/Scripts/Private/UI/{ => WebViews}/iOSPassportWebView.cs.meta (100%) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs deleted file mode 100644 index 8d0d15a9..00000000 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs +++ /dev/null @@ -1,333 +0,0 @@ -using System; -using UnityEngine; -using UnityEngine.UI; -using Immutable.Passport.Core.Logging; - -#if UNITY_ANDROID -using Immutable.Browser.Gree; -#endif - -namespace Immutable.Passport -{ -#if UNITY_ANDROID - /// - /// Android implementation of IPassportWebView using Gree WebView (Android WebView) - /// Wraps Gree WebViewObject in a clean, platform-agnostic interface - /// Very similar to iOS implementation but uses Android's native WebView - /// - public class AndroidPassportWebView : IPassportWebView - { - private const string TAG = "[AndroidPassportWebView]"; - - // Gree WebView components - private WebViewObject webViewObject; - private GameObject webViewGameObject; - - // Configuration and state - private RawImage targetRawImage; - private MonoBehaviour coroutineRunner; - private PassportWebViewConfig config; - private bool isInitialized = false; - private bool isVisible = false; - private string currentUrl = ""; - - // Events - public event Action OnJavaScriptMessage; - public event Action OnLoadFinished; - public event Action OnLoadStarted; - - // Properties - public bool IsVisible => isVisible; - public string CurrentUrl => currentUrl; - - /// - /// Constructor for Android PassportWebView - /// - /// RawImage component to display WebView content (not used on Android - native overlay) - /// MonoBehaviour to run coroutines (for consistency with other platforms) - public AndroidPassportWebView(RawImage targetRawImage, MonoBehaviour coroutineRunner) - { - this.targetRawImage = targetRawImage ?? throw new ArgumentNullException(nameof(targetRawImage)); - this.coroutineRunner = coroutineRunner ?? throw new ArgumentNullException(nameof(coroutineRunner)); - - PassportLogger.Info($"{TAG} Android WebView wrapper created"); - } - - public void Initialize(PassportWebViewConfig config) - { - if (isInitialized) - { - PassportLogger.Warn($"{TAG} Already initialized, skipping"); - return; - } - - this.config = config ?? new PassportWebViewConfig(); - - try - { - PassportLogger.Info($"{TAG} Initializing Android WebView with Gree WebView..."); - - CreateWebViewObject(); - ConfigureWebView(); - - isInitialized = true; - PassportLogger.Info($"{TAG} Android WebView initialized successfully"); - } - catch (Exception ex) - { - PassportLogger.Error($"{TAG} Failed to initialize: {ex.Message}"); - throw; - } - } - - public void LoadUrl(string url) - { - if (!isInitialized) - { - PassportLogger.Error($"{TAG} Cannot load URL - WebView not initialized"); - return; - } - - if (string.IsNullOrEmpty(url)) - { - PassportLogger.Error($"{TAG} Cannot load empty URL"); - return; - } - - try - { - PassportLogger.Info($"{TAG} Loading URL: {url}"); - currentUrl = url; - - // Android implementation: Use LaunchAuthURL to open in external browser - // This follows the same pattern as iOS and the main Passport SDK for Android authentication - webViewObject.LaunchAuthURL(url, "immutablerunner://callback"); - PassportLogger.Info($"{TAG} URL launched in external browser: {url}"); - - // Trigger load started event - OnLoadStarted?.Invoke(); - - // For Android, we consider the load "finished" immediately since it opens externally - OnLoadFinished?.Invoke(); - } - catch (Exception ex) - { - PassportLogger.Error($"{TAG} Failed to load URL: {ex.Message}"); - } - } - - public void Show() - { - if (!isInitialized) - { - PassportLogger.Error($"{TAG} Cannot show - WebView not initialized"); - return; - } - - try - { - PassportLogger.Info($"{TAG} Showing WebView (Android uses external browser)"); - - // Android implementation: WebView is always "shown" since we use external browser - // The actual display happens when LoadUrl calls LaunchAuthURL - isVisible = true; - - PassportLogger.Info($"{TAG} WebView shown successfully"); - } - catch (Exception ex) - { - PassportLogger.Error($"{TAG} Failed to show WebView: {ex.Message}"); - } - } - - public void Hide() - { - if (!isInitialized) - { - PassportLogger.Warn($"{TAG} Cannot hide - WebView not initialized"); - return; - } - - try - { - PassportLogger.Info($"{TAG} Hiding WebView (Android external browser will close automatically)"); - - // Android implementation: External browser closes automatically after auth - // No explicit hide needed - isVisible = false; - - PassportLogger.Info($"{TAG} WebView hidden successfully"); - } - catch (Exception ex) - { - PassportLogger.Error($"{TAG} Failed to hide WebView: {ex.Message}"); - } - } - - public void ExecuteJavaScript(string js) - { - if (!isInitialized) - { - PassportLogger.Error($"{TAG} Cannot execute JavaScript - WebView not initialized"); - return; - } - - try - { - PassportLogger.Debug($"{TAG} Executing JavaScript: {js.Substring(0, Math.Min(100, js.Length))}..."); - webViewObject.EvaluateJS(js); - } - catch (Exception ex) - { - PassportLogger.Error($"{TAG} Failed to execute JavaScript: {ex.Message}"); - } - } - - public void RegisterJavaScriptMethod(string methodName, Action handler) - { - if (!isInitialized) - { - PassportLogger.Error($"{TAG} Cannot register JavaScript method - WebView not initialized"); - return; - } - - try - { - PassportLogger.Info($"{TAG} Registering JavaScript method: {methodName}"); - - // Android implementation: Since we use external browser (Chrome Custom Tabs), - // JavaScript methods are handled through deep links and auth callbacks - // The login data will be captured via the auth callback when the external browser - // redirects back to the app with immutablerunner://callback - - PassportLogger.Info($"{TAG} JavaScript method '{methodName}' registered (handled via deep link callbacks on Android)"); - } - catch (Exception ex) - { - PassportLogger.Error($"{TAG} Failed to register JavaScript method: {ex.Message}"); - } - } - - public void Dispose() - { - try - { - PassportLogger.Info($"{TAG} Disposing Android WebView"); - - // Destroy WebView GameObject - if (webViewGameObject != null) - { - UnityEngine.Object.DestroyImmediate(webViewGameObject); - webViewGameObject = null; - } - - // Clear references - webViewObject = null; - targetRawImage = null; - coroutineRunner = null; - - isInitialized = false; - isVisible = false; - - PassportLogger.Info($"{TAG} Android WebView disposed successfully"); - } - catch (Exception ex) - { - PassportLogger.Error($"{TAG} Error during disposal: {ex.Message}"); - } - } - - #region Private Implementation - - private void CreateWebViewObject() - { - PassportLogger.Info($"{TAG} Creating Gree WebViewObject..."); - - // Create GameObject for WebView - webViewGameObject = new GameObject("PassportUI_Android_WebView"); - UnityEngine.Object.DontDestroyOnLoad(webViewGameObject); - - // Add WebViewObject component - webViewObject = new WebViewObject(); - - PassportLogger.Info($"{TAG} Gree WebViewObject created successfully"); - } - - private void ConfigureWebView() - { - PassportLogger.Info($"{TAG} Configuring Gree WebView..."); - - // Initialize WebViewObject with callbacks - webViewObject.Init( - cb: OnWebViewMessage, - httpErr: OnWebViewHttpError, - err: OnWebViewError, - auth: OnWebViewAuth, - log: OnWebViewLog - ); - - // Clear cache if requested - if (config.ClearCacheOnInit) - { - webViewObject.ClearCache(true); - PassportLogger.Info($"{TAG} WebView cache cleared"); - } - - PassportLogger.Info($"{TAG} Gree WebView configured successfully"); - } - - #endregion - - #region WebView Event Handlers - - private void OnWebViewMessage(string message) - { - try - { - PassportLogger.Debug($"{TAG} WebView message: {message}"); - - // Parse message to see if it's a JavaScript method call - if (message.Contains("method") && message.Contains("data")) - { - // This is a JavaScript method call from our registered methods - OnJavaScriptMessage?.Invoke(message); - } - else - { - // Regular WebView message - OnJavaScriptMessage?.Invoke(message); - } - } - catch (Exception ex) - { - PassportLogger.Error($"{TAG} Error handling WebView message: {ex.Message}"); - } - } - - private void OnWebViewHttpError(string id, string message) - { - PassportLogger.Error($"{TAG} WebView HTTP error [{id}]: {message}"); - } - - private void OnWebViewError(string id, string message) - { - PassportLogger.Error($"{TAG} WebView error [{id}]: {message}"); - } - - private void OnWebViewAuth(string message) - { - PassportLogger.Info($"{TAG} WebView auth message: {message}"); - // Auth messages could be login completion, etc. - OnJavaScriptMessage?.Invoke(message); - } - - private void OnWebViewLog(string message) - { - PassportLogger.Debug($"{TAG} WebView console log: {message}"); - } - - #endregion - } -#endif -} - diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs.meta b/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs.meta deleted file mode 100644 index b2dc7c01..00000000 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidPassportWebView.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ca562827a0f98a7489504f4aa7ffe2dc -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews.meta b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews.meta new file mode 100644 index 00000000..10c217fc --- /dev/null +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d990766b2963fd843a7bf5ac205ef02d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/AndroidVuplexWebView.cs similarity index 86% rename from src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs rename to src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/AndroidVuplexWebView.cs index 1f6ffe31..e523781d 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/AndroidVuplexWebView.cs @@ -14,8 +14,8 @@ namespace Immutable.Passport { /// /// Android implementation of IPassportWebView using Vuplex WebView - /// Provides embedded WebView functionality within the Unity app (not external browser) - /// This is different from AndroidPassportWebView which uses external browser for auth flows + /// Provides embedded WebView functionality within the Unity app + /// Consistent with iOS and macOS Vuplex implementations /// public class AndroidVuplexWebView : IPassportWebView { @@ -85,11 +85,27 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) // Must be child of Canvas for Vuplex to work _webViewPrefab.transform.SetParent(_canvasReference.canvas.transform, false); - // Set up full-screen layout for Native 2D Mode + // Set WebView size based on configuration var rect = _webViewPrefab.GetComponent(); - rect.anchorMin = Vector2.zero; - rect.anchorMax = Vector2.one; - rect.offsetMin = rect.offsetMax = Vector2.zero; + + // Use configured dimensions or fallback to full-screen for Native 2D Mode + if (config.Width > 0 && config.Height > 0) + { + // Center the WebView with specific dimensions + rect.anchorMin = new Vector2(0.5f, 0.5f); + rect.anchorMax = new Vector2(0.5f, 0.5f); + rect.sizeDelta = new Vector2(config.Width, config.Height); + rect.anchoredPosition = Vector2.zero; + PassportLogger.Info($"{TAG} Using configured dimensions: {config.Width}x{config.Height}"); + } + else + { + // Full-screen fallback for Native 2D Mode + rect.anchorMin = Vector2.zero; + rect.anchorMax = Vector2.one; + rect.offsetMin = rect.offsetMax = Vector2.zero; + PassportLogger.Info($"{TAG} Using full-screen dimensions for Native 2D Mode"); + } // Wait for WebView initialization await _webViewPrefab.WaitUntilInitialized(); diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs.meta b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/AndroidVuplexWebView.cs.meta similarity index 100% rename from src/Packages/Passport/Runtime/Scripts/Private/UI/AndroidVuplexWebView.cs.meta rename to src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/AndroidVuplexWebView.cs.meta diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/MacOSPassportWebView.cs similarity index 95% rename from src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs rename to src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/MacOSPassportWebView.cs index 9e922caf..11928d3f 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/MacOSPassportWebView.cs @@ -91,13 +91,19 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) // Must be child of Canvas for Vuplex to work _webViewPrefab.transform.SetParent(_canvasReference.canvas.transform, false); - // Set desktop-appropriate size - larger than mobile but not full-screen + // Set WebView size based on configuration var rect = _webViewPrefab.GetComponent(); rect.anchorMin = new Vector2(0.5f, 0.5f); // Center anchor rect.anchorMax = new Vector2(0.5f, 0.5f); - rect.sizeDelta = new Vector2(1200, 800); // Desktop size: 1200x800 for comfortable login UX + + // Use configured dimensions or fallback to desktop-appropriate defaults + float width = config.Width > 0 ? config.Width : 1200; + float height = config.Height > 0 ? config.Height : 800; + rect.sizeDelta = new Vector2(width, height); rect.anchoredPosition = Vector2.zero; // Center position + PassportLogger.Info($"{TAG} Using WebView dimensions: {width}x{height}"); + // Wait for WebView initialization await _webViewPrefab.WaitUntilInitialized(); PassportLogger.Info($"{TAG} Vuplex WebView initialization completed"); diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs.meta b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/MacOSPassportWebView.cs.meta similarity index 100% rename from src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs.meta rename to src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/MacOSPassportWebView.cs.meta diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/WindowsPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/WindowsPassportWebView.cs similarity index 91% rename from src/Packages/Passport/Runtime/Scripts/Private/UI/WindowsPassportWebView.cs rename to src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/WindowsPassportWebView.cs index d8afe9b9..a52c6686 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/WindowsPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/WindowsPassportWebView.cs @@ -187,17 +187,32 @@ public void Show() uwbGameObject.SetActive(true); } - // Enable the RawImage and restore size + // Enable the RawImage and set size to match WebView if (targetRawImage != null) { targetRawImage.enabled = true; var rectTransform = targetRawImage.GetComponent(); if (rectTransform != null) { - rectTransform.anchorMin = Vector2.zero; - rectTransform.anchorMax = Vector2.one; - rectTransform.offsetMin = Vector2.zero; - rectTransform.offsetMax = Vector2.zero; + // Match the WebView dimensions that were configured + if (config.Width > 0 && config.Height > 0) + { + // Center the RawImage with specific dimensions to match WebView + rectTransform.anchorMin = new Vector2(0.5f, 0.5f); + rectTransform.anchorMax = new Vector2(0.5f, 0.5f); + rectTransform.sizeDelta = new Vector2(config.Width, config.Height); + rectTransform.anchoredPosition = Vector2.zero; + PassportLogger.Info($"{TAG} RawImage sized to match WebView: {config.Width}x{config.Height}"); + } + else + { + // Full-screen fallback to match WebView + rectTransform.anchorMin = Vector2.zero; + rectTransform.anchorMax = Vector2.one; + rectTransform.offsetMin = Vector2.zero; + rectTransform.offsetMax = Vector2.zero; + PassportLogger.Info($"{TAG} RawImage sized to full-screen to match WebView"); + } } } @@ -381,10 +396,26 @@ private void CreateUWBInstance() // Add RectTransform for UI positioning RectTransform rectTransform = uwbGameObject.AddComponent(); - rectTransform.anchorMin = Vector2.zero; - rectTransform.anchorMax = Vector2.one; - rectTransform.offsetMin = Vector2.zero; - rectTransform.offsetMax = Vector2.zero; + + // Use configured dimensions or fallback to full-screen + if (config.Width > 0 && config.Height > 0) + { + // Center the WebView with specific dimensions + rectTransform.anchorMin = new Vector2(0.5f, 0.5f); + rectTransform.anchorMax = new Vector2(0.5f, 0.5f); + rectTransform.sizeDelta = new Vector2(config.Width, config.Height); + rectTransform.anchoredPosition = Vector2.zero; + PassportLogger.Info($"{TAG} Using configured dimensions: {config.Width}x{config.Height}"); + } + else + { + // Full-screen fallback + rectTransform.anchorMin = Vector2.zero; + rectTransform.anchorMax = Vector2.one; + rectTransform.offsetMin = Vector2.zero; + rectTransform.offsetMax = Vector2.zero; + PassportLogger.Info($"{TAG} Using full-screen dimensions"); + } // Add WebBrowserUIFull component webBrowserUI = uwbGameObject.AddComponent(); diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/WindowsPassportWebView.cs.meta b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/WindowsPassportWebView.cs.meta similarity index 100% rename from src/Packages/Passport/Runtime/Scripts/Private/UI/WindowsPassportWebView.cs.meta rename to src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/WindowsPassportWebView.cs.meta diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/iOSPassportWebView.cs similarity index 93% rename from src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs rename to src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/iOSPassportWebView.cs index 036e5f31..839cd8c2 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/iOSPassportWebView.cs @@ -14,8 +14,8 @@ namespace Immutable.Passport { /// /// iOS implementation of IPassportWebView using Vuplex WebView - /// Provides embedded WebView functionality within the Unity app (not external browser) - /// This is different from iOSPassportWebView which uses external browser for auth flows + /// Provides embedded WebView functionality within the Unity app + /// Consistent with Android and macOS Vuplex implementations /// public class iOSPassportWebView : IPassportWebView { @@ -88,13 +88,19 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) // Must be child of Canvas for Vuplex to work _webViewPrefab.transform.SetParent(_canvasReference.canvas.transform, false); - // Set reasonable fixed size instead of full-screen to avoid massive textures + // Set WebView size based on configuration var rect = _webViewPrefab.GetComponent(); rect.anchorMin = new Vector2(0.5f, 0.5f); // Center anchor rect.anchorMax = new Vector2(0.5f, 0.5f); - rect.sizeDelta = new Vector2(1000, 700); // Wider: 1000x700 for better login UX + + // Use configured dimensions or fallback to reasonable defaults + float width = config.Width > 0 ? config.Width : 1000; + float height = config.Height > 0 ? config.Height : 700; + rect.sizeDelta = new Vector2(width, height); rect.anchoredPosition = Vector2.zero; // Center position + PassportLogger.Info($"{TAG} Using WebView dimensions: {width}x{height}"); + // Wait for WebView initialization await _webViewPrefab.WaitUntilInitialized(); diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs.meta b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/iOSPassportWebView.cs.meta similarity index 100% rename from src/Packages/Passport/Runtime/Scripts/Private/UI/iOSPassportWebView.cs.meta rename to src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/iOSPassportWebView.cs.meta From 2b3a32da75ac44240f7bed3ff44f703c1bc6ac4e Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Fri, 19 Sep 2025 19:01:14 +1200 Subject: [PATCH 39/75] feat: add passport config fields to ui component, add optional passport instance to ui --- .../Scripts/Private/UI/IPassportWebView.cs | 10 ++ .../Runtime/Scripts/Public/PassportUI.cs | 149 ++++++++++++++++-- 2 files changed, 149 insertions(+), 10 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs index ae28ca4c..d29a7f79 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/IPassportWebView.cs @@ -130,6 +130,16 @@ public class PassportWebViewConfig /// public string InitialUrl { get; set; } = "about:blank"; + /// + /// WebView width in pixels (0 = use RawImage width) + /// + public int Width { get; set; } = 1920; + + /// + /// WebView height in pixels (0 = use RawImage height) + /// + public int Height { get; set; } = 1080; + /// /// Custom User-Agent string (optional) /// diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index f10c5281..978d53bc 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -40,6 +40,11 @@ public override string ToString() /// - iOS/Android: Vuplex WebView with embedded browser /// - macOS: Vuplex WebView with embedded browser /// + /// INITIALIZATION OPTIONS: + /// 1. Simple: Call InitializeWithPassport() - PassportUI handles both Passport.Init() and UI setup + /// 2. Hybrid: Call InitializeWithPassport(passport) - Use existing Passport instance with UI + /// 3. Traditional: Call Passport.Init() yourself, then call Init(passport) - for advanced control + /// /// SETUP: When configuring the PassportUI prefab in the editor: /// - Set RawImage component's width and height to 0 in the RectTransform /// - This ensures the UI is completely hidden before initialization @@ -83,17 +88,69 @@ public class PassportUI : MonoBehaviour [Tooltip("Clear WebView cache on login for Windows WebView (Volt Unity Web Browser). Not used on other platforms.")] public bool clearCacheOnLogin = true; + [Header("Passport Configuration")] + [Tooltip("Passport client ID from the Immutable Developer Hub")] + [SerializeField] private string clientId = ""; + + [Tooltip("Passport environment (sandbox or production)")] + [SerializeField] private string environment = "sandbox"; + + [Tooltip("OAuth redirect URI for authentication callbacks")] + [SerializeField] private string redirectUri = "immutablerunner://callback"; + + [Tooltip("OAuth logout redirect URI for logout callbacks")] + [SerializeField] private string logoutRedirectUri = "immutablerunner://logout"; + [Header("WebView Settings")] - [Tooltip("URL to load in the WebView for authentication.")] - [SerializeField] private string webViewUrl = "https://auth.immutable.com/im-embedded-login-prompt?isWebView=true&client_id=YOUR_CLIENT_ID"; + [Tooltip("Base URL for the WebView authentication page. The client_id parameter will be automatically appended from the Passport Configuration above.")] + [SerializeField] private string webViewBaseUrl = "https://auth.immutable.com/im-embedded-login-prompt"; + + [Tooltip("Width of the WebView in pixels. Set to 0 to use the RawImage's current width.")] + [SerializeField] private int webViewWidth = 1920; + + [Tooltip("Height of the WebView in pixels. Set to 0 to use the RawImage's current height.")] + [SerializeField] private int webViewHeight = 1080; /// - /// Gets or sets the URL to load in the WebView for authentication + /// Gets the complete WebView URL with the client ID automatically appended /// public string WebViewUrl { - get => webViewUrl; - set => webViewUrl = value; + get + { + if (string.IsNullOrEmpty(clientId)) + { + return webViewBaseUrl; + } + return $"{webViewBaseUrl}?isWebView=true&client_id={clientId}"; + } + } + + /// + /// Gets or sets the base WebView URL (without client_id parameter) + /// + public string WebViewBaseUrl + { + get => webViewBaseUrl; + set => webViewBaseUrl = value; + } + + /// + /// Gets or sets the WebView width in pixels + /// + public int WebViewWidth + { + get => webViewWidth; + set => webViewWidth = value; + } + + /// + /// Gets or sets the WebView height in pixels + /// + public int WebViewHeight + { + get => webViewHeight; + set => webViewHeight = value; } // Cross-platform WebView abstraction @@ -109,9 +166,79 @@ public string WebViewUrl private Coroutine inputActivationCoroutine; /// - /// Initialize PassportUI with the main Passport instance - /// Following the working WebViewTest pattern + /// Initialize Passport and PassportUI in one call using the configured settings. + /// This is the simple initialization method that handles both Passport.Init() and UI setup. + /// Uses the serialized fields (clientId, environment, redirectUri, logoutRedirectUri) from the Unity Inspector. + /// + /// UniTask that completes when initialization is finished + public async UniTask InitializeWithPassport() + { + // Validate configuration + if (string.IsNullOrEmpty(clientId)) + { + PassportLogger.Error($"{TAG} Client ID is required but not set in the Inspector"); + return; + } + + try + { + PassportLogger.Info($"{TAG} Initializing Passport with client ID: {clientId}"); + + // Initialize Passport using the configured settings + var passport = await Passport.Init(clientId, environment, redirectUri, logoutRedirectUri); + + // Initialize the UI with the created Passport instance + Init(passport); + + PassportLogger.Info($"{TAG} Passport and PassportUI initialized successfully"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to initialize Passport: {ex.Message}"); + throw; + } + } + + /// + /// Initialize PassportUI with an existing Passport instance in one call. + /// Use this method when you already have a Passport instance from elsewhere in your code + /// and just want to set up the UI component. + /// + /// The existing initialized Passport instance + /// UniTask that completes when UI initialization is finished + public async UniTask InitializeWithPassport(Passport passportInstance) + { + if (passportInstance == null) + { + PassportLogger.Error($"{TAG} Passport instance cannot be null"); + return; + } + + try + { + PassportLogger.Info($"{TAG} Initializing PassportUI with existing Passport instance"); + + // Initialize the UI with the provided Passport instance + Init(passportInstance); + + // Wait a frame to ensure initialization is complete + await UniTask.Yield(); + + PassportLogger.Info($"{TAG} PassportUI initialized successfully with existing Passport instance"); + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Failed to initialize PassportUI with existing Passport: {ex.Message}"); + throw; + } + } + + /// + /// Initialize PassportUI with an existing Passport instance. + /// Use this method if you want to control Passport.Init() yourself. + /// Following the working WebViewTest pattern. /// + /// The initialized Passport instance public void Init(Passport passportInstance) { if (passportInstance == null) @@ -185,7 +312,9 @@ private void CreateWebView() EnableRemoteDebugging = enableRemoteDebugging, RemoteDebuggingPort = remoteDebuggingPort, ClearCacheOnInit = clearCacheOnLogin, - InitialUrl = "about:blank" + InitialUrl = "about:blank", + Width = webViewWidth > 0 ? webViewWidth : (int)rawImage.rectTransform.rect.width, + Height = webViewHeight > 0 ? webViewHeight : (int)rawImage.rectTransform.rect.height }; // Initialize WebView @@ -483,8 +612,8 @@ public async UniTask ShowLoginUI() webView.Show(); PassportLogger.Info($"{TAG} WebView shown"); - webView.LoadUrl(webViewUrl); - PassportLogger.Info($"{TAG} Navigated to configured URL: {webViewUrl}"); + webView.LoadUrl(WebViewUrl); + PassportLogger.Info($"{TAG} Navigated to configured URL: {WebViewUrl}"); // Return true since we successfully started the OAuth flow // The actual authentication completion is handled by the deep link system From 65a14f99678252a04165fe6f7efe6b6fa3b75fe0 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Fri, 19 Sep 2025 19:03:20 +1200 Subject: [PATCH 40/75] refactor: use webview and embedded login prompt for authentication --- .../Passport/InitialisationWithUI.unity | 7 ++ .../Passport/AuthenticatedSceneManager.cs | 3 +- .../Scripts/Passport/Logout/LogoutScript.cs | 3 +- .../PassportInitialisationScript.cs | 4 +- .../PassportInitialisationWithUIScript.cs | 79 ++++++++++--------- 5 files changed, 55 insertions(+), 41 deletions(-) diff --git a/sample/Assets/Scenes/Passport/InitialisationWithUI.unity b/sample/Assets/Scenes/Passport/InitialisationWithUI.unity index 7dece912..36146296 100644 --- a/sample/Assets/Scenes/Passport/InitialisationWithUI.unity +++ b/sample/Assets/Scenes/Passport/InitialisationWithUI.unity @@ -337,9 +337,16 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: loginButton: {fileID: 1274086209} + OnLoginSuccess: + m_PersistentCalls: + m_Calls: [] + OnLoginFailure: + m_PersistentCalls: + m_Calls: [] enableRemoteDebugging: 1 remoteDebuggingPort: 9222 clearCacheOnLogin: 1 + webViewUrl: https://auth.immutable.com/im-embedded-login-prompt?client_id=IllW5pJ54DShXtaSXzaAlghm40uQjptd --- !u!1 &96192975 GameObject: m_ObjectHideFlags: 0 diff --git a/sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs b/sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs index 262029ff..63a3edc6 100644 --- a/sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs +++ b/sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs @@ -60,6 +60,7 @@ public static void NavigateToAuthenticatedScene() public static void NavigateToUnauthenticatedScene() { - UnityEngine.SceneManagement.SceneManager.LoadScene("UnauthenticatedScene"); + // Navigate to the main initialization scene with PassportUI instead + UnityEngine.SceneManagement.SceneManager.LoadScene("InitialisationWithUI"); } } \ No newline at end of file diff --git a/sample/Assets/Scripts/Passport/Logout/LogoutScript.cs b/sample/Assets/Scripts/Passport/Logout/LogoutScript.cs index 8aba609a..b098a768 100644 --- a/sample/Assets/Scripts/Passport/Logout/LogoutScript.cs +++ b/sample/Assets/Scripts/Passport/Logout/LogoutScript.cs @@ -25,7 +25,8 @@ private async UniTaskVoid LogoutAsync() await Passport.Instance.Logout(); SampleAppManager.IsConnectedToImx = false; SampleAppManager.IsConnectedToZkEvm = false; - AuthenticatedSceneManager.NavigateToUnauthenticatedScene(); + // Navigate back to the main initialization scene with PassportUI + SceneManager.LoadScene("InitialisationWithUI"); } catch (System.Exception ex) { diff --git a/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs b/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs index cd22015a..3cb9224a 100644 --- a/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs +++ b/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs @@ -54,8 +54,8 @@ private async void InitialisePassport() var passport = await Passport.Init(clientId, environment, redirectUri, logoutRedirectUri); SampleAppManager.PassportInstance = passport; - // Navigate to the unauthenticated scene after initialising Passport - SceneManager.LoadScene("UnauthenticatedScene"); + // Navigate to the main initialization scene with PassportUI after initialising Passport + SceneManager.LoadScene("InitialisationWithUI"); } catch (Exception ex) { diff --git a/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationWithUIScript.cs b/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationWithUIScript.cs index c54b5257..358951ed 100644 --- a/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationWithUIScript.cs +++ b/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationWithUIScript.cs @@ -4,7 +4,15 @@ using UnityEngine.SceneManagement; using Immutable.Passport; using Immutable.Passport.Core.Logging; - +using Cysharp.Threading.Tasks; + +/// +/// Sample script demonstrating how to initialize Passport using the PassportUI prefab. +/// This is the recommended approach for quick setup - configure Passport settings +/// in the PassportUI Inspector, then call InitializeWithPassport(). +/// +/// For advanced scenarios without UI, see PassportInitialisationScript.cs instead. +/// public class PassportInitialisationWithUIScript : MonoBehaviour { #pragma warning disable CS8618 @@ -21,25 +29,6 @@ void Start() private async void InitialisePassport() { - string redirectUri; - string logoutRedirectUri; - -#if UNITY_WEBGL - var url = Application.absoluteURL; - var uri = new Uri(url); - var scheme = uri.Scheme; - var hostWithPort = uri.IsDefaultPort ? uri.Host : $"{uri.Host}:{uri.Port}"; - var fullPath = uri.AbsolutePath.EndsWith("/") - ? uri.AbsolutePath - : uri.AbsolutePath.Substring(0, uri.AbsolutePath.LastIndexOf('/') + 1); - - redirectUri = $"{scheme}://{hostWithPort}{fullPath}callback.html"; - logoutRedirectUri = $"{scheme}://{hostWithPort}{fullPath}logout.html"; -#else - redirectUri = "immutablerunner://callback"; - logoutRedirectUri = "immutablerunner://logout"; -#endif - try { // Set the log level for the SDK @@ -48,27 +37,43 @@ private async void InitialisePassport() // Don't redact token values from logs Passport.RedactTokensInLogs = false; - // Initialise Passport with UI support enabled - const string environment = Immutable.Passport.Model.Environment.SANDBOX; - // const string clientId = "mp6rxfMDwwZDogcdgNrAaHnG0qMlXuMK"; - const string clientId = "IllW5pJ54DShXtaSXzaAlghm40uQjptd"; - var passport = await Passport.Init(clientId, environment, redirectUri, logoutRedirectUri); - SampleAppManager.PassportInstance = passport; - - // Find and initialize PassportUI at runtime + // Find PassportUI component passportUI = FindObjectOfType(); - if (passportUI != null) + if (passportUI == null) + { + Debug.LogError("PassportUI component not found in scene - UI login will not be available"); + return; + } + + // Subscribe to login events for automatic scene transition + PassportUI.OnLoginSuccessStatic += OnPassportLoginSuccess; + PassportUI.OnLoginFailureStatic += OnPassportLoginFailure; + + // Check if Passport is already initialized (e.g., from logout flow) + if (Passport.Instance != null) { - passportUI.Init(passport); - Debug.Log("PassportUI found and initialized successfully"); + Debug.Log("Passport already initialized, setting up UI only..."); + + // Just initialize the UI with the existing Passport instance + await passportUI.InitializeWithPassport(Passport.Instance); + + // Store reference for other scripts that need it + SampleAppManager.PassportInstance = Passport.Instance; - // Subscribe to login success event for automatic scene transition - PassportUI.OnLoginSuccessStatic += OnPassportLoginSuccess; - PassportUI.OnLoginFailureStatic += OnPassportLoginFailure; + Debug.Log("PassportUI initialized with existing Passport instance"); } else { - Debug.LogWarning("PassportUI component not found in scene - UI login will not be available"); + Debug.Log("Initializing Passport using PassportUI prefab configuration..."); + + // PassportUI handles both Passport.Init() and UI setup + // Configuration is done in the PassportUI Inspector (clientId, environment, etc.) + await passportUI.InitializeWithPassport(); + + // Store reference for other scripts that need it + SampleAppManager.PassportInstance = Passport.Instance; + + Debug.Log("Passport and PassportUI initialized successfully"); } } catch (Exception ex) @@ -79,7 +84,7 @@ private async void InitialisePassport() private void OnPassportLoginSuccess() { - Debug.Log("🎉 Passport login successful! Navigating to authenticated scene..."); + Debug.Log("Passport login successful! Navigating to authenticated scene..."); // Navigate to authenticated scene SceneManager.LoadScene("AuthenticatedScene"); @@ -87,7 +92,7 @@ private void OnPassportLoginSuccess() private void OnPassportLoginFailure(string errorMessage) { - Debug.LogError($"❌ Passport login failed: {errorMessage}"); + Debug.LogError($"Passport login failed: {errorMessage}"); // Could show error UI, retry options, etc. // For now, just log the error From bb1bcf14b1141f069e4d942c32cb165626dab723 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Fri, 19 Sep 2025 19:33:47 +1200 Subject: [PATCH 41/75] feat: add marketing consent direct login options --- .../Private/Model/DirectLoginMethod.cs | 1 + .../Private/Model/DirectLoginOptions.cs | 10 +++- .../Private/Model/MarketingConsentStatus.cs | 35 ++++++++++++++ .../Model/MarketingConsentStatus.cs.meta | 11 +++++ .../Runtime/Scripts/Private/PassportImpl.cs | 48 ++++++++++++------- .../Runtime/Scripts/Public/PassportUI.cs | 17 +++++++ 6 files changed, 104 insertions(+), 18 deletions(-) create mode 100644 src/Packages/Passport/Runtime/Scripts/Private/Model/MarketingConsentStatus.cs create mode 100644 src/Packages/Passport/Runtime/Scripts/Private/Model/MarketingConsentStatus.cs.meta diff --git a/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginMethod.cs b/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginMethod.cs index 5996371f..052064d7 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginMethod.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginMethod.cs @@ -8,6 +8,7 @@ namespace Immutable.Passport.Model [Serializable] public enum DirectLoginMethod { + None, Email, Google, Apple, diff --git a/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginOptions.cs b/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginOptions.cs index fbf157d3..42401629 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginOptions.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginOptions.cs @@ -19,6 +19,11 @@ public class DirectLoginOptions /// public string email; + /// + /// Marketing consent status (optional). + /// + public MarketingConsentStatus? marketingConsentStatus; + /// /// Default constructor. /// @@ -26,6 +31,7 @@ public DirectLoginOptions() { directLoginMethod = DirectLoginMethod.Email; email = null; + marketingConsentStatus = null; } /// @@ -33,10 +39,12 @@ public DirectLoginOptions() /// /// The direct login method /// The email address (optional) - public DirectLoginOptions(DirectLoginMethod loginMethod, string emailAddress = null) + /// The marketing consent status (optional) + public DirectLoginOptions(DirectLoginMethod loginMethod, string emailAddress = null, MarketingConsentStatus? marketingConsentStatus = null) { directLoginMethod = loginMethod; email = emailAddress; + this.marketingConsentStatus = marketingConsentStatus; } /// diff --git a/src/Packages/Passport/Runtime/Scripts/Private/Model/MarketingConsentStatus.cs b/src/Packages/Passport/Runtime/Scripts/Private/Model/MarketingConsentStatus.cs new file mode 100644 index 00000000..07e74fce --- /dev/null +++ b/src/Packages/Passport/Runtime/Scripts/Private/Model/MarketingConsentStatus.cs @@ -0,0 +1,35 @@ +using System; + +namespace Immutable.Passport.Model +{ + /// + /// Enum representing marketing consent status. + /// + [Serializable] + public enum MarketingConsentStatus + { + OptedIn, + Unsubscribed + } + + /// + /// Extension methods for MarketingConsentStatus enum. + /// + public static class MarketingConsentStatusExtensions + { + /// + /// Converts the enum value to the string format expected by the game bridge. + /// + /// The marketing consent status + /// The corresponding string value + public static string ToApiString(this MarketingConsentStatus status) + { + return status switch + { + MarketingConsentStatus.OptedIn => "opted_in", + MarketingConsentStatus.Unsubscribed => "unsubscribed", + _ => throw new ArgumentOutOfRangeException(nameof(status), status, "Unknown MarketingConsentStatus value") + }; + } + } +} diff --git a/src/Packages/Passport/Runtime/Scripts/Private/Model/MarketingConsentStatus.cs.meta b/src/Packages/Passport/Runtime/Scripts/Private/Model/MarketingConsentStatus.cs.meta new file mode 100644 index 00000000..8ac5513e --- /dev/null +++ b/src/Packages/Passport/Runtime/Scripts/Private/Model/MarketingConsentStatus.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4e38402137fa13e409b3c88ab5cb27c6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs b/src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs index e717e048..ac45c427 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs @@ -275,22 +275,19 @@ private async UniTask LaunchAuthUrl() { try { - // Create the request JSON manually to ensure proper serialization - var requestJson = $"{{\"isConnectImx\":{(!_pkceLoginOnly).ToString().ToLower()}"; - - if (_directLoginOptions != null) + // Create the request using a serializable class for clean JSON generation + var request = new AuthUrlRequest { - requestJson += $",\"directLoginOptions\":{{\"directLoginMethod\":\"{_directLoginOptions.directLoginMethod.ToString().ToLower()}\""; - - if (_directLoginOptions.IsEmailValid()) + isConnectImx = !_pkceLoginOnly, + directLoginOptions = _directLoginOptions != null ? new DirectLoginRequestOptions { - requestJson += $",\"email\":\"{_directLoginOptions.email}\""; - } + directLoginMethod = _directLoginOptions.directLoginMethod.ToString().ToLower(), + email = _directLoginOptions.IsEmailValid() ? _directLoginOptions.email : null, + marketingConsentStatus = _directLoginOptions.marketingConsentStatus?.ToApiString() + } : null + }; - requestJson += "}"; - } - - requestJson += "}"; + var requestJson = JsonUtility.ToJson(request); var callResponse = await _communicationsManager.Call(PassportFunction.GET_PKCE_AUTH_URL, requestJson); var response = callResponse.OptDeserializeObject(); @@ -299,10 +296,6 @@ private async UniTask LaunchAuthUrl() { var url = response.result.Replace(" ", "+"); - // force marketing consent to true for now - // TODO: remove this once we have a way to get the marketing consent from the user - url = url + "&marketingConsent=opted_in"; - #if UNITY_ANDROID && !UNITY_EDITOR loginPKCEUrl = url; SendAuthEvent(_pkceLoginOnly ? PassportAuthEvent.LoginPKCELaunchingCustomTabs : PassportAuthEvent.ConnectImxPKCELaunchingCustomTabs); @@ -818,4 +811,25 @@ async void onDeeplinkResult(string url) } } #endif + + /// + /// Serializable request class for LaunchAuthUrl to replace manual JSON string concatenation + /// + [Serializable] + internal class AuthUrlRequest + { + public bool isConnectImx; + public DirectLoginRequestOptions directLoginOptions; + } + + /// + /// Serializable class for directLoginOptions within AuthUrlRequest + /// + [Serializable] + internal class DirectLoginRequestOptions + { + public string directLoginMethod; + public string email; + public string marketingConsentStatus; + } } \ No newline at end of file diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index 978d53bc..eeb4d6bd 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -449,6 +449,23 @@ private async void HandleLoginData(string jsonData) loginOptions.email = loginData.email; } + // Parse and set marketing consent status if provided + if (!string.IsNullOrEmpty(loginData.marketingConsentStatus)) + { + switch (loginData.marketingConsentStatus.ToLower()) + { + case "opted_in": + loginOptions.marketingConsentStatus = MarketingConsentStatus.OptedIn; + break; + case "unsubscribed": + loginOptions.marketingConsentStatus = MarketingConsentStatus.Unsubscribed; + break; + default: + PassportLogger.Warn($"{TAG} Unknown marketing consent status: {loginData.marketingConsentStatus}"); + break; + } + } + // Perform the login and handle the result PassportLogger.Info($"{TAG} Starting login with method: {loginData.directLoginMethod}"); bool loginSuccess = await _passportInstance.Login(false, loginOptions); From 4ad308d974fd69f3321769249804be36459393fe Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Fri, 19 Sep 2025 19:34:11 +1200 Subject: [PATCH 42/75] chore: update scene metadata --- sample/Assets/Scenes/Passport/InitialisationWithUI.unity | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sample/Assets/Scenes/Passport/InitialisationWithUI.unity b/sample/Assets/Scenes/Passport/InitialisationWithUI.unity index 36146296..cc74aaca 100644 --- a/sample/Assets/Scenes/Passport/InitialisationWithUI.unity +++ b/sample/Assets/Scenes/Passport/InitialisationWithUI.unity @@ -346,7 +346,13 @@ MonoBehaviour: enableRemoteDebugging: 1 remoteDebuggingPort: 9222 clearCacheOnLogin: 1 - webViewUrl: https://auth.immutable.com/im-embedded-login-prompt?client_id=IllW5pJ54DShXtaSXzaAlghm40uQjptd + clientId: IllW5pJ54DShXtaSXzaAlghm40uQjptd + environment: sandbox + redirectUri: immutablerunner://callback + logoutRedirectUri: immutablerunner://logout + webViewBaseUrl: https://auth.immutable.com/im-embedded-login-prompt + webViewWidth: 1920 + webViewHeight: 1080 --- !u!1 &96192975 GameObject: m_ObjectHideFlags: 0 From a2b268b92228bb9384539a52568ff1d9b5022d6f Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Fri, 19 Sep 2025 19:35:13 +1200 Subject: [PATCH 43/75] chore: remove old test login page --- WebviewTestPage/.gitignore | 1 - WebviewTestPage/index.html | 399 -------------------- WebviewTestPage/package-lock.json | 595 ------------------------------ WebviewTestPage/package.json | 15 - 4 files changed, 1010 deletions(-) delete mode 100644 WebviewTestPage/.gitignore delete mode 100644 WebviewTestPage/index.html delete mode 100644 WebviewTestPage/package-lock.json delete mode 100644 WebviewTestPage/package.json diff --git a/WebviewTestPage/.gitignore b/WebviewTestPage/.gitignore deleted file mode 100644 index c2658d7d..00000000 --- a/WebviewTestPage/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules/ diff --git a/WebviewTestPage/index.html b/WebviewTestPage/index.html deleted file mode 100644 index 89b4d03b..00000000 --- a/WebviewTestPage/index.html +++ /dev/null @@ -1,399 +0,0 @@ - - - - - - Immutable Passport Login - - - - - - - - diff --git a/WebviewTestPage/package-lock.json b/WebviewTestPage/package-lock.json deleted file mode 100644 index d4d3a99c..00000000 --- a/WebviewTestPage/package-lock.json +++ /dev/null @@ -1,595 +0,0 @@ -{ - "name": "webviewtestpage", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "webviewtestpage", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "http-server": "^14.1.1" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "license": "MIT", - "dependencies": { - "whatwg-encoding": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-server": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", - "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", - "license": "MIT", - "dependencies": { - "basic-auth": "^2.0.1", - "chalk": "^4.1.2", - "corser": "^2.0.1", - "he": "^1.2.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy": "^1.18.1", - "mime": "^1.6.0", - "minimist": "^1.2.6", - "opener": "^1.5.1", - "portfinder": "^1.0.28", - "secure-compare": "3.0.1", - "union": "~0.5.0", - "url-join": "^4.0.1" - }, - "bin": { - "http-server": "bin/http-server" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "license": "(WTFPL OR MIT)", - "bin": { - "opener": "bin/opener-bin.js" - } - }, - "node_modules/portfinder": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.37.tgz", - "integrity": "sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==", - "license": "MIT", - "dependencies": { - "async": "^3.2.6", - "debug": "^4.3.6" - }, - "engines": { - "node": ">= 10.12" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "license": "MIT" - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/secure-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", - "license": "MIT" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/union": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", - "dependencies": { - "qs": "^6.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "license": "MIT" - }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - } - } -} diff --git a/WebviewTestPage/package.json b/WebviewTestPage/package.json deleted file mode 100644 index 12aa4e0c..00000000 --- a/WebviewTestPage/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "webviewtestpage", - "version": "1.0.0", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "http-server -p 8080 -c-1" - }, - "author": "", - "license": "ISC", - "description": "", - "dependencies": { - "http-server": "^14.1.1" - } -} From 0bdf95d2c8a2e82c99761ff7b489e5324a278a12 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 22 Sep 2025 16:24:57 +1200 Subject: [PATCH 44/75] feat: start passport instance in ui on startup --- .../Runtime/Scripts/Public/PassportUI.cs | 67 ++++++++++++++++--- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index eeb4d6bd..9bd75a5a 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -163,7 +163,44 @@ public int WebViewHeight private GameObject bridgeWebViewGameObject; // Input management - private Coroutine inputActivationCoroutine; + // Note: Input coroutine removed - games should manage cursor state themselves + + /// + /// Auto-initialize PassportUI when the component starts if clientId is configured. + /// This enables "drag and drop" functionality - just configure the Inspector fields and it works. + /// + private async void Start() + { + if (!isInitialized && !string.IsNullOrEmpty(clientId)) + { + try + { + PassportLogger.Info($"{TAG} Auto-initializing PassportUI from Start()..."); + if (Passport.Instance != null) + { + PassportLogger.Info($"{TAG} Auto-initialization: Passport already exists, setting up UI only"); + await InitializeWithPassport(Passport.Instance); + } + else + { + PassportLogger.Info($"{TAG} Auto-initialization: Creating new Passport instance"); + await InitializeWithPassport(); + } + } + catch (Exception ex) + { + PassportLogger.Error($"{TAG} Auto-initialization failed: {ex.Message}"); + } + } + else if (string.IsNullOrEmpty(clientId)) + { + PassportLogger.Warn($"{TAG} Auto-initialization skipped - Client ID not configured in Inspector"); + } + else if (isInitialized) + { + PassportLogger.Info($"{TAG} Auto-initialization skipped - Already initialized"); + } + } /// /// Initialize Passport and PassportUI in one call using the configured settings. @@ -173,6 +210,12 @@ public int WebViewHeight /// UniTask that completes when initialization is finished public async UniTask InitializeWithPassport() { + if (isInitialized) + { + PassportLogger.Warn($"{TAG} PassportUI is already initialized, skipping InitializeWithPassport()"); + return; + } + // Validate configuration if (string.IsNullOrEmpty(clientId)) { @@ -208,6 +251,12 @@ public async UniTask InitializeWithPassport() /// UniTask that completes when UI initialization is finished public async UniTask InitializeWithPassport(Passport passportInstance) { + if (isInitialized) + { + PassportLogger.Warn($"{TAG} PassportUI is already initialized, skipping InitializeWithPassport(passport)"); + return; + } + if (passportInstance == null) { PassportLogger.Error($"{TAG} Passport instance cannot be null"); @@ -241,6 +290,12 @@ public async UniTask InitializeWithPassport(Passport passportInstance) /// The initialized Passport instance public void Init(Passport passportInstance) { + if (isInitialized) + { + PassportLogger.Warn($"{TAG} PassportUI is already initialized, skipping Init(passport)"); + return; + } + if (passportInstance == null) { PassportLogger.Error($"{TAG} Passport instance cannot be null"); @@ -279,6 +334,7 @@ public void Init(Passport passportInstance) // Hide initially HideLoginUI(logMessage: false); + isInitialized = true; PassportLogger.Info($"{TAG} PassportUI initialized successfully"); } @@ -329,7 +385,6 @@ private void CreateWebView() webView.RegisterJavaScriptMethod("HandleLoginError", HandleLoginError); webView.RegisterJavaScriptMethod("HandleClose", (data) => HideLoginUI()); - isInitialized = true; PassportLogger.Info($"{TAG} Cross-platform WebView created successfully"); } catch (Exception ex) @@ -678,13 +733,7 @@ private void OnDestroy() PassportLogger.Info($"{TAG} WebView disposed on destroy"); } - // Stop any running coroutines - if (inputActivationCoroutine != null) - { - StopCoroutine(inputActivationCoroutine); - inputActivationCoroutine = null; - PassportLogger.Info($"{TAG} Stopped input activation coroutine during cleanup"); - } + // Note: Input coroutine handling removed - games should manage cursor state themselves } catch (Exception ex) { From 8fc498066ff18f5dc29611d490526f474ad54122 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 22 Sep 2025 16:26:00 +1200 Subject: [PATCH 45/75] feat: add passport ui prefab --- src/Packages/Passport/PassportLogin.prefab | 206 ++++++++++++++++++ .../Passport/PassportLogin.prefab.meta | 7 + 2 files changed, 213 insertions(+) create mode 100644 src/Packages/Passport/PassportLogin.prefab create mode 100644 src/Packages/Passport/PassportLogin.prefab.meta diff --git a/src/Packages/Passport/PassportLogin.prefab b/src/Packages/Passport/PassportLogin.prefab new file mode 100644 index 00000000..4e235607 --- /dev/null +++ b/src/Packages/Passport/PassportLogin.prefab @@ -0,0 +1,206 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &5904238481854722446 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5904238481854722442} + - component: {fileID: 5904238481854722445} + - component: {fileID: 5904238481854722444} + - component: {fileID: 5904238481854722447} + m_Layer: 5 + m_Name: PassportLogin + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5904238481854722442 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5904238481854722446} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 5904238482241975487} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!223 &5904238481854722445 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5904238481854722446} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 1000 + m_TargetDisplay: 0 +--- !u!114 &5904238481854722444 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5904238481854722446} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!114 &5904238481854722447 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5904238481854722446} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!1 &5904238482241975475 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5904238482241975487} + - component: {fileID: 5904238482241975486} + - component: {fileID: 5904238482241975473} + - component: {fileID: 5904238482241975484} + m_Layer: 5 + m_Name: RawImage + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5904238482241975487 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5904238482241975475} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1.0392305, y: 1.0392305, z: 1.0392305} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 5904238481854722442} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &5904238482241975486 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5904238482241975475} + m_CullTransparentMesh: 1 +--- !u!114 &5904238482241975473 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5904238482241975475} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Texture: {fileID: 0} + m_UVRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 +--- !u!114 &5904238482241975484 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5904238482241975475} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 32208e882b664251bb0da03443581c5f, type: 3} + m_Name: + m_EditorClassIdentifier: + loginButton: {fileID: 0} + OnLoginSuccess: + m_PersistentCalls: + m_Calls: [] + OnLoginFailure: + m_PersistentCalls: + m_Calls: [] + enableRemoteDebugging: 1 + remoteDebuggingPort: 9222 + clearCacheOnLogin: 1 + clientId: IllW5pJ54DShXtaSXzaAlghm40uQjptd + environment: sandbox + redirectUri: immutablerunner://callback + logoutRedirectUri: immutablerunner://logout + webViewBaseUrl: https://auth.immutable.com/im-embedded-login-prompt + webViewWidth: 1920 + webViewHeight: 1080 diff --git a/src/Packages/Passport/PassportLogin.prefab.meta b/src/Packages/Passport/PassportLogin.prefab.meta new file mode 100644 index 00000000..09f87161 --- /dev/null +++ b/src/Packages/Passport/PassportLogin.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 318101a79a6d44d48a0863a613fa1909 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From d7c5a208d35d90cdd854d21d7a6c4f240aec7103 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 22 Sep 2025 18:12:17 +1200 Subject: [PATCH 46/75] fix: handle new and legacy input systems for windows webview --- .../Passport/InitialisationWithUI.unity | 305 ++++++------------ .../UI/WebViews/WindowsPassportWebView.cs | 28 +- 2 files changed, 126 insertions(+), 207 deletions(-) diff --git a/sample/Assets/Scenes/Passport/InitialisationWithUI.unity b/sample/Assets/Scenes/Passport/InitialisationWithUI.unity index cc74aaca..762f3513 100644 --- a/sample/Assets/Scenes/Passport/InitialisationWithUI.unity +++ b/sample/Assets/Scenes/Passport/InitialisationWithUI.unity @@ -250,109 +250,6 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 27701369} m_CullTransparentMesh: 1 ---- !u!1 &96180837 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 96180841} - - component: {fileID: 96180840} - - component: {fileID: 96180839} - - component: {fileID: 96180842} - m_Layer: 5 - m_Name: RawImage - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &96180839 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 96180837} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} - m_RaycastTarget: 1 - m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} - m_Maskable: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_Texture: {fileID: 0} - m_UVRect: - serializedVersion: 2 - x: 0 - y: 0 - width: 1 - height: 1 ---- !u!222 &96180840 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 96180837} - m_CullTransparentMesh: 1 ---- !u!224 &96180841 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 96180837} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1.0392305, y: 1.0392305, z: 1.0392305} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 447127388} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &96180842 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 96180837} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 32208e882b664251bb0da03443581c5f, type: 3} - m_Name: - m_EditorClassIdentifier: - loginButton: {fileID: 1274086209} - OnLoginSuccess: - m_PersistentCalls: - m_Calls: [] - OnLoginFailure: - m_PersistentCalls: - m_Calls: [] - enableRemoteDebugging: 1 - remoteDebuggingPort: 9222 - clearCacheOnLogin: 1 - clientId: IllW5pJ54DShXtaSXzaAlghm40uQjptd - environment: sandbox - redirectUri: immutablerunner://callback - logoutRedirectUri: immutablerunner://logout - webViewBaseUrl: https://auth.immutable.com/im-embedded-login-prompt - webViewWidth: 1920 - webViewHeight: 1080 --- !u!1 &96192975 GameObject: m_ObjectHideFlags: 0 @@ -732,107 +629,6 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 416242723} m_CullTransparentMesh: 1 ---- !u!1 &447127384 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 447127388} - - component: {fileID: 447127387} - - component: {fileID: 447127386} - - component: {fileID: 447127385} - m_Layer: 5 - m_Name: Canvas - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &447127385 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 447127384} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} - m_Name: - m_EditorClassIdentifier: - m_IgnoreReversedGraphics: 1 - m_BlockingObjects: 0 - m_BlockingMask: - serializedVersion: 2 - m_Bits: 4294967295 ---- !u!114 &447127386 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 447127384} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} - m_Name: - m_EditorClassIdentifier: - m_UiScaleMode: 0 - m_ReferencePixelsPerUnit: 100 - m_ScaleFactor: 1 - m_ReferenceResolution: {x: 800, y: 600} - m_ScreenMatchMode: 0 - m_MatchWidthOrHeight: 0 - m_PhysicalUnit: 3 - m_FallbackScreenDPI: 96 - m_DefaultSpriteDPI: 96 - m_DynamicPixelsPerUnit: 1 - m_PresetInfoIsWorld: 0 ---- !u!223 &447127387 -Canvas: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 447127384} - m_Enabled: 1 - serializedVersion: 3 - m_RenderMode: 0 - m_Camera: {fileID: 0} - m_PlaneDistance: 100 - m_PixelPerfect: 0 - m_ReceivesEvents: 1 - m_OverrideSorting: 0 - m_OverridePixelPerfect: 0 - m_SortingBucketNormalizedSize: 0 - m_AdditionalShaderChannelsFlag: 0 - m_SortingLayerID: 0 - m_SortingOrder: 1000 - m_TargetDisplay: 0 ---- !u!224 &447127388 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 447127384} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 0, y: 0, z: 0} - m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 96180841} - m_Father: {fileID: 0} - m_RootOrder: 4 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} - m_Pivot: {x: 0, y: 0} --- !u!1 &519420028 GameObject: m_ObjectHideFlags: 0 @@ -2141,3 +1937,104 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1850628783} m_CullTransparentMesh: 1 +--- !u!1001 &5904238482297122518 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_Pivot.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_Pivot.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_RootOrder + value: 4 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722442, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5904238481854722446, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: m_Name + value: Canvas + objectReference: {fileID: 0} + - target: {fileID: 5904238482241975484, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} + propertyPath: loginButton + value: + objectReference: {fileID: 1274086209} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 318101a79a6d44d48a0863a613fa1909, type: 3} diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/WindowsPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/WindowsPassportWebView.cs index a52c6686..be417848 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/WindowsPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/WindowsPassportWebView.cs @@ -10,6 +10,10 @@ using Immutable.Passport.Core.Logging; using Cysharp.Threading.Tasks; +#if ENABLE_INPUT_SYSTEM +using UnityEngine.InputSystem; +#endif + #if UNITY_EDITOR using UnityEditor; #endif @@ -520,8 +524,25 @@ private void ConfigureInputHandler() { try { - // Load the BrowserInput asset from SDK Resources - var inputHandler = Resources.Load("BrowserInput"); +#if ENABLE_INPUT_SYSTEM + // Use UWB's built-in Input System handler + PassportLogger.Info($"{TAG} Using UWB Input System handler"); + var inputSystemHandler = ScriptableObject.CreateInstance(); + + // Configure Input Actions for the handler + // Set up scroll input action + inputSystemHandler.scrollInput = new InputAction("Scroll", InputActionType.Value, "/scroll"); + inputSystemHandler.scrollInput.Enable(); + + // Set up pointer position input action + inputSystemHandler.pointPosition = new InputAction("PointerPosition", InputActionType.Value, "/position"); + inputSystemHandler.pointPosition.Enable(); + + webBrowserUI.inputHandler = inputSystemHandler; + PassportLogger.Info($"{TAG} Input System handler configured with mouse actions"); +#else + // Load the BrowserInput asset from SDK Resources (legacy input) + var inputHandler = Resources.Load("BrowserInput"); if (inputHandler != null) { webBrowserUI.inputHandler = inputHandler; @@ -530,9 +551,10 @@ private void ConfigureInputHandler() else { PassportLogger.Warn($"{TAG} BrowserInput.asset not found in Resources, creating fallback"); - var fallbackHandler = ScriptableObject.CreateInstance(); + var fallbackHandler = ScriptableObject.CreateInstance(); webBrowserUI.inputHandler = fallbackHandler; } +#endif } catch (Exception ex) { From f06905913a231e50941988be9ede8ae6b16a5e40 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 22 Sep 2025 18:13:21 +1200 Subject: [PATCH 47/75] fix: incorrect file path delimiters in deeplink script --- .../Private/Helpers/WindowsDeepLink.cs | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/Helpers/WindowsDeepLink.cs b/src/Packages/Passport/Runtime/Scripts/Private/Helpers/WindowsDeepLink.cs index 1a6c948f..f9ce0e55 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/Helpers/WindowsDeepLink.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/Helpers/WindowsDeepLink.cs @@ -134,8 +134,32 @@ private static void CreateCommandScript(string protocolName) " if not \"%%B\"==\"\" set \"cmdline=!cmdline!%%B\"", " )", " echo [%date% %time%] Raw command line: !cmdline! >> \"%LOG_PATH%\"", + " REM Try multiple path formats to handle Unity's various command line formats", + " set \"MATCH_FOUND=0\"", + " ", + " REM Try 1: Exact match with backslashes", " echo !cmdline! | findstr /I /C:\"-projectPath\" | findstr /I /C:\"%PROJECT_PATH%\" >nul", - " if not errorlevel 1 (", + " if not errorlevel 1 set \"MATCH_FOUND=1\"", + " ", + " REM Try 2: Forward slashes if backslashes didn't match", + " if \"!MATCH_FOUND!\"==\"0\" (", + " set \"PROJECT_PATH_FORWARD=%PROJECT_PATH:\\=/%\"", + " echo [%date% %time%] Trying forward slash path: !PROJECT_PATH_FORWARD! >> \"%LOG_PATH%\"", + " echo !cmdline! | findstr /I /C:\"-projectPath\" | findstr /I /C:\"!PROJECT_PATH_FORWARD!\" >nul", + " if not errorlevel 1 set \"MATCH_FOUND=1\"", + " )", + " ", + " REM Try 3: With quotes around paths", + " if \"!MATCH_FOUND!\"==\"0\" (", + " echo [%date% %time%] Trying quoted paths >> \"%LOG_PATH%\"", + " echo !cmdline! | findstr /I /C:\"-projectPath \\\"!PROJECT_PATH!\\\"\" >nul", + " if not errorlevel 1 set \"MATCH_FOUND=1\"", + " if \"!MATCH_FOUND!\"==\"0\" (", + " echo !cmdline! | findstr /I /C:\"-projectPath \\\"!PROJECT_PATH_FORWARD!\\\"\" >nul", + " if not errorlevel 1 set \"MATCH_FOUND=1\"", + " )", + " )", + " if \"!MATCH_FOUND!\"==\"1\" (", " echo [%date% %time%] Found matching Unity process ID: %%A >> \"%LOG_PATH%\"", " echo [%date% %time%] Command line: !cmdline! >> \"%LOG_PATH%\"", " powershell -NoProfile -ExecutionPolicy Bypass -Command ^", @@ -161,7 +185,7 @@ private static void CreateCommandScript(string protocolName) $"echo [%date% %time%] Starting new Unity instance >> \"%LOG_PATH%\"", $"start \"\" \"{unityExe}\" -projectPath \"%PROJECT_PATH%\" >nul 2>&1" }; - + File.WriteAllLines(cmdPath, scriptLines); PassportLogger.Debug($"Writing script to {cmdPath}"); PassportLogger.Debug($"Writing logs to {logPath}"); @@ -198,7 +222,7 @@ private static void CreateCommandScript(string protocolName) private static void RegisterProtocol(string protocolName) { PassportLogger.Debug($"Register protocol: {protocolName}"); - + UIntPtr hKey; uint disposition; // Create registry key for the protocol @@ -214,14 +238,14 @@ private static void RegisterProtocol(string protocolName) out disposition); if (result != 0) - { + { throw new Exception($"Failed to create PKCE registry key. Error code: {result}"); } // Set the default value for the protocol key to Application.productName // This is often used by Windows as the display name for the protocol var appProductName = Application.productName; - var productNameDataSize = (uint)((appProductName.Length + 1) * Marshal.SystemDefaultCharSize); + var productNameDataSize = (uint)((appProductName.Length + 1) * Marshal.SystemDefaultCharSize); var setDefaultResult = RegSetValueEx(hKey, null, 0, REG_SZ, appProductName, productNameDataSize); if (setDefaultResult != 0) @@ -230,7 +254,7 @@ private static void RegisterProtocol(string protocolName) } // Set URL Protocol value - RegSetValueEx(hKey, "URL Protocol", 0, REG_SZ, string.Empty, (uint)(1 * Marshal.SystemDefaultCharSize)); + RegSetValueEx(hKey, "URL Protocol", 0, REG_SZ, string.Empty, (uint)(1 * Marshal.SystemDefaultCharSize)); // Create command subkey UIntPtr commandKey; @@ -246,7 +270,7 @@ private static void RegisterProtocol(string protocolName) out disposition); if (result != 0) - { + { RegCloseKey(hKey); throw new Exception($"Failed to create PKCE command registry key. Error code: {result}"); } @@ -255,7 +279,7 @@ private static void RegisterProtocol(string protocolName) var scriptLocation = GetGameExecutablePath(".cmd"); string command = $"\"{scriptLocation}\" \"%1\""; uint commandSize = (uint)((command.Length + 1) * 2); - + result = RegSetValueEx(commandKey, "", 0, REG_SZ, command, commandSize); if (result != 0) { @@ -268,7 +292,7 @@ private static void RegisterProtocol(string protocolName) RegCloseKey(commandKey); RegCloseKey(hKey); } - + private static string GetGameExecutablePath(string suffix) { #if !UNITY_EDITOR_WIN From 4ac41d52b8072db24c7d72052b96d779552e05fd Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 22 Sep 2025 18:21:05 +1200 Subject: [PATCH 48/75] chore: update assembly references for input fix --- .../Private/Immutable.Passport.Runtime.Private.asmdef | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/Immutable.Passport.Runtime.Private.asmdef b/src/Packages/Passport/Runtime/Scripts/Private/Immutable.Passport.Runtime.Private.asmdef index 5ca03f58..5e9e9c5d 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/Immutable.Passport.Runtime.Private.asmdef +++ b/src/Packages/Passport/Runtime/Scripts/Private/Immutable.Passport.Runtime.Private.asmdef @@ -7,7 +7,8 @@ "Immutable.Browser.Core", "Immutable.Browser.Gree", "Immutable.Passport.Core.Logging", - "Vuplex.WebView" + "Vuplex.WebView", + "Unity.InputSystem" ], "includePlatforms": [ "Android", @@ -26,6 +27,12 @@ ], "autoReferenced": true, "defineConstraints": [], - "versionDefines": [], + "versionDefines": [ + { + "name": "com.unity.inputsystem", + "expression": "1.0.0", + "define": "ENABLE_INPUT_SYSTEM" + } + ], "noEngineReferences": false } \ No newline at end of file From e8551b71f81bb63e59c7ee20f284878bc4d49bec Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 22 Sep 2025 18:21:42 +1200 Subject: [PATCH 49/75] feat: add prefab for windows and vuplex webviews --- src/Packages/Passport/Prefabs.meta | 8 + .../PassportLogin_Vuplex.prefab} | 2 +- .../PassportLogin_Vuplex.prefab.meta} | 0 .../Prefabs/PassportLogin_Windows.prefab | 169 ++++++++++++++++++ .../Prefabs/PassportLogin_Windows.prefab.meta | 7 + 5 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 src/Packages/Passport/Prefabs.meta rename src/Packages/Passport/{PassportLogin.prefab => Prefabs/PassportLogin_Vuplex.prefab} (99%) rename src/Packages/Passport/{PassportLogin.prefab.meta => Prefabs/PassportLogin_Vuplex.prefab.meta} (100%) create mode 100644 src/Packages/Passport/Prefabs/PassportLogin_Windows.prefab create mode 100644 src/Packages/Passport/Prefabs/PassportLogin_Windows.prefab.meta diff --git a/src/Packages/Passport/Prefabs.meta b/src/Packages/Passport/Prefabs.meta new file mode 100644 index 00000000..032d815d --- /dev/null +++ b/src/Packages/Passport/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 302294de4bba1e3458ca7b7b2d0dc771 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Packages/Passport/PassportLogin.prefab b/src/Packages/Passport/Prefabs/PassportLogin_Vuplex.prefab similarity index 99% rename from src/Packages/Passport/PassportLogin.prefab rename to src/Packages/Passport/Prefabs/PassportLogin_Vuplex.prefab index 4e235607..6ecfdf10 100644 --- a/src/Packages/Passport/PassportLogin.prefab +++ b/src/Packages/Passport/Prefabs/PassportLogin_Vuplex.prefab @@ -13,7 +13,7 @@ GameObject: - component: {fileID: 5904238481854722444} - component: {fileID: 5904238481854722447} m_Layer: 5 - m_Name: PassportLogin + m_Name: PassportLogin_Vuplex m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 diff --git a/src/Packages/Passport/PassportLogin.prefab.meta b/src/Packages/Passport/Prefabs/PassportLogin_Vuplex.prefab.meta similarity index 100% rename from src/Packages/Passport/PassportLogin.prefab.meta rename to src/Packages/Passport/Prefabs/PassportLogin_Vuplex.prefab.meta diff --git a/src/Packages/Passport/Prefabs/PassportLogin_Windows.prefab b/src/Packages/Passport/Prefabs/PassportLogin_Windows.prefab new file mode 100644 index 00000000..63af83a2 --- /dev/null +++ b/src/Packages/Passport/Prefabs/PassportLogin_Windows.prefab @@ -0,0 +1,169 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2554157028554517038 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2554157028554517031} + - component: {fileID: 2554157028554517028} + - component: {fileID: 2554157028554517029} + - component: {fileID: 2554157028554517026} + - component: {fileID: 2554157028554517027} + - component: {fileID: 2554157028554517024} + - component: {fileID: 2554157028554517025} + m_Layer: 5 + m_Name: PassportLogin_Windows + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2554157028554517031 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2554157028554517038} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!223 &2554157028554517028 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2554157028554517038} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!114 &2554157028554517029 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2554157028554517038} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!114 &2554157028554517026 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2554157028554517038} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!222 &2554157028554517027 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2554157028554517038} + m_CullTransparentMesh: 1 +--- !u!114 &2554157028554517024 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2554157028554517038} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Texture: {fileID: 0} + m_UVRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 +--- !u!114 &2554157028554517025 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2554157028554517038} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 32208e882b664251bb0da03443581c5f, type: 3} + m_Name: + m_EditorClassIdentifier: + loginButton: {fileID: 0} + OnLoginSuccess: + m_PersistentCalls: + m_Calls: [] + OnLoginFailure: + m_PersistentCalls: + m_Calls: [] + enableRemoteDebugging: 0 + remoteDebuggingPort: 9222 + clearCacheOnLogin: 1 + clientId: IllW5pJ54DShXtaSXzaAlghm40uQjptd + environment: sandbox + redirectUri: mygame://callback + logoutRedirectUri: mygame://logout + webViewBaseUrl: https://auth.immutable.com/im-embedded-login-prompt + webViewWidth: 1920 + webViewHeight: 1080 diff --git a/src/Packages/Passport/Prefabs/PassportLogin_Windows.prefab.meta b/src/Packages/Passport/Prefabs/PassportLogin_Windows.prefab.meta new file mode 100644 index 00000000..ba131fe6 --- /dev/null +++ b/src/Packages/Passport/Prefabs/PassportLogin_Windows.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 422432bb6ceb7af4490f24f2b0383786 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From fb3aee63e5836e5fe62f99cdca2af5b1cb5068f6 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 23 Sep 2025 17:58:14 +1200 Subject: [PATCH 50/75] fix: web content size inside mac and ios webview --- .../Private/UI/WebViews/MacOSPassportWebView.cs | 12 ++++++------ .../Private/UI/WebViews/iOSPassportWebView.cs | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/MacOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/MacOSPassportWebView.cs index 11928d3f..543a1da9 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/MacOSPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/MacOSPassportWebView.cs @@ -83,10 +83,10 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) // Create WebView prefab and parent to Canvas _webViewPrefab = CanvasWebViewPrefab.Instantiate(); PassportLogger.Info($"{TAG} CanvasWebViewPrefab created successfully"); - _webViewPrefab.Native2DModeEnabled = false; // Use standard mode for better desktop compatibility + _webViewPrefab.Native2DModeEnabled = true; // Use Native2DMode for better performance on desktop - // Set higher resolution for desktop - MacOS can handle larger textures - _webViewPrefab.Resolution = 1.5f; // 1.5px per Unity unit for crisp rendering + // Set lower resolution for faster loading and rendering + _webViewPrefab.Resolution = 0.75f; // Balanced resolution for desktop // Must be child of Canvas for Vuplex to work _webViewPrefab.transform.SetParent(_canvasReference.canvas.transform, false); @@ -96,9 +96,9 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) rect.anchorMin = new Vector2(0.5f, 0.5f); // Center anchor rect.anchorMax = new Vector2(0.5f, 0.5f); - // Use configured dimensions or fallback to desktop-appropriate defaults - float width = config.Width > 0 ? config.Width : 1200; - float height = config.Height > 0 ? config.Height : 800; + // Use configured dimensions or fallback to desktop-appropriate defaults (optimized for performance) + float width = config.Width > 0 ? config.Width : 600; + float height = config.Height > 0 ? config.Height : 650; rect.sizeDelta = new Vector2(width, height); rect.anchoredPosition = Vector2.zero; // Center position diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/iOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/iOSPassportWebView.cs index 839cd8c2..518c4a46 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/iOSPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/iOSPassportWebView.cs @@ -82,8 +82,8 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) _webViewPrefab = CanvasWebViewPrefab.Instantiate(); _webViewPrefab.Native2DModeEnabled = false; // Disable Native2DMode to avoid Unity integration issues - // Set reasonable resolution - much lower to avoid texture size issues - _webViewPrefab.Resolution = 1.0f; // 1px per Unity unit - creates 800x600px texture + // Set higher resolution for better content scaling on mobile + _webViewPrefab.Resolution = 0.5f; // increase to scale down content, decrease to scale up content // Must be child of Canvas for Vuplex to work _webViewPrefab.transform.SetParent(_canvasReference.canvas.transform, false); @@ -93,9 +93,9 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) rect.anchorMin = new Vector2(0.5f, 0.5f); // Center anchor rect.anchorMax = new Vector2(0.5f, 0.5f); - // Use configured dimensions or fallback to reasonable defaults + // Use configured dimensions or fallback to mobile-optimized defaults float width = config.Width > 0 ? config.Width : 1000; - float height = config.Height > 0 ? config.Height : 700; + float height = config.Height > 0 ? config.Height : 1200; rect.sizeDelta = new Vector2(width, height); rect.anchoredPosition = Vector2.zero; // Center position From 298600975cbe3d7cb2ddfd39f7bd780846921dfc Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 23 Sep 2025 18:33:31 +1200 Subject: [PATCH 51/75] chore: cleanup unused meta file --- sample/Assets/Scripts/Passport/WebViewTesting.meta | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 sample/Assets/Scripts/Passport/WebViewTesting.meta diff --git a/sample/Assets/Scripts/Passport/WebViewTesting.meta b/sample/Assets/Scripts/Passport/WebViewTesting.meta deleted file mode 100644 index 3f39cbea..00000000 --- a/sample/Assets/Scripts/Passport/WebViewTesting.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 75a849bb19b9de44fb93dbc29a0d874d -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: From 6554da7479b900212ce18ce9cef4d37710705f13 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 23 Sep 2025 18:33:56 +1200 Subject: [PATCH 52/75] revert: sample game package manifest --- sample/Packages/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample/Packages/manifest.json b/sample/Packages/manifest.json index b2e624c3..abd09528 100644 --- a/sample/Packages/manifest.json +++ b/sample/Packages/manifest.json @@ -64,7 +64,7 @@ }, { "name": "npmjs", - "url": "https://registry.npmjs.org", + "url": "https://registry.npmjs.org/", "scopes": [ "com.unity.uiextensions" ] From 63a03a4355aa24ad309beb6cd175c3dbbe72b5c8 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 23 Sep 2025 18:55:12 +1200 Subject: [PATCH 53/75] revert: revert sample game to use non-webview login --- sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs | 3 +-- sample/Assets/Scripts/Passport/Logout/LogoutScript.cs | 3 +-- .../PassportInitialisation/PassportInitialisationScript.cs | 4 ++-- sample/ProjectSettings/EditorBuildSettings.asset | 4 ++-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs b/sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs index 63a3edc6..262029ff 100644 --- a/sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs +++ b/sample/Assets/Scripts/Passport/AuthenticatedSceneManager.cs @@ -60,7 +60,6 @@ public static void NavigateToAuthenticatedScene() public static void NavigateToUnauthenticatedScene() { - // Navigate to the main initialization scene with PassportUI instead - UnityEngine.SceneManagement.SceneManager.LoadScene("InitialisationWithUI"); + UnityEngine.SceneManagement.SceneManager.LoadScene("UnauthenticatedScene"); } } \ No newline at end of file diff --git a/sample/Assets/Scripts/Passport/Logout/LogoutScript.cs b/sample/Assets/Scripts/Passport/Logout/LogoutScript.cs index b098a768..8aba609a 100644 --- a/sample/Assets/Scripts/Passport/Logout/LogoutScript.cs +++ b/sample/Assets/Scripts/Passport/Logout/LogoutScript.cs @@ -25,8 +25,7 @@ private async UniTaskVoid LogoutAsync() await Passport.Instance.Logout(); SampleAppManager.IsConnectedToImx = false; SampleAppManager.IsConnectedToZkEvm = false; - // Navigate back to the main initialization scene with PassportUI - SceneManager.LoadScene("InitialisationWithUI"); + AuthenticatedSceneManager.NavigateToUnauthenticatedScene(); } catch (System.Exception ex) { diff --git a/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs b/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs index 3cb9224a..cd22015a 100644 --- a/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs +++ b/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs @@ -54,8 +54,8 @@ private async void InitialisePassport() var passport = await Passport.Init(clientId, environment, redirectUri, logoutRedirectUri); SampleAppManager.PassportInstance = passport; - // Navigate to the main initialization scene with PassportUI after initialising Passport - SceneManager.LoadScene("InitialisationWithUI"); + // Navigate to the unauthenticated scene after initialising Passport + SceneManager.LoadScene("UnauthenticatedScene"); } catch (Exception ex) { diff --git a/sample/ProjectSettings/EditorBuildSettings.asset b/sample/ProjectSettings/EditorBuildSettings.asset index fea8f891..58026432 100644 --- a/sample/ProjectSettings/EditorBuildSettings.asset +++ b/sample/ProjectSettings/EditorBuildSettings.asset @@ -5,10 +5,10 @@ EditorBuildSettings: m_ObjectHideFlags: 0 serializedVersion: 2 m_Scenes: - - enabled: 0 + - enabled: 1 path: Assets/Scenes/Passport/Initialisation.unity guid: bb0668e0c95b745ce8e2f127d5940ede - - enabled: 1 + - enabled: 0 path: Assets/Scenes/Passport/InitialisationWithUI.unity guid: b588e10e8f614e0458aee90c7d02a499 - enabled: 1 From cc81a8cc110878c9a6244397ab3d44bbf9c4933d Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 23 Sep 2025 19:17:41 +1200 Subject: [PATCH 54/75] fix: set default marketing consent status value --- .../Runtime/Scripts/Private/Model/DirectLoginOptions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginOptions.cs b/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginOptions.cs index 42401629..9915e9a0 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginOptions.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginOptions.cs @@ -20,7 +20,7 @@ public class DirectLoginOptions public string email; /// - /// Marketing consent status (optional). + /// Marketing consent status. Defaults to OptedIn. Required by Auth endpoints. /// public MarketingConsentStatus? marketingConsentStatus; @@ -31,7 +31,7 @@ public DirectLoginOptions() { directLoginMethod = DirectLoginMethod.Email; email = null; - marketingConsentStatus = null; + marketingConsentStatus = MarketingConsentStatus.OptedIn; } /// @@ -39,12 +39,12 @@ public DirectLoginOptions() /// /// The direct login method /// The email address (optional) - /// The marketing consent status (optional) + /// Marketing consent status. Defaults to OptedIn if not specified. Required. public DirectLoginOptions(DirectLoginMethod loginMethod, string emailAddress = null, MarketingConsentStatus? marketingConsentStatus = null) { directLoginMethod = loginMethod; email = emailAddress; - this.marketingConsentStatus = marketingConsentStatus; + this.marketingConsentStatus = marketingConsentStatus ?? MarketingConsentStatus.OptedIn; } /// From d03d2d346792909e6a26f9b9857bbaf472c318aa Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 23 Sep 2025 19:18:16 +1200 Subject: [PATCH 55/75] chore: switch to test project with working configuration --- .../PassportInitialisation/PassportInitialisationScript.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs b/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs index cd22015a..c2527e28 100644 --- a/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs +++ b/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs @@ -50,7 +50,8 @@ private async void InitialisePassport() // Initialise Passport const string environment = Immutable.Passport.Model.Environment.SANDBOX; - const string clientId = "mp6rxfMDwwZDogcdgNrAaHnG0qMlXuMK"; + // const string clientId = "mp6rxfMDwwZDogcdgNrAaHnG0qMlXuMK"; + const string clientId = "IllW5pJ54DShXtaSXzaAlghm40uQjptd"; var passport = await Passport.Init(clientId, environment, redirectUri, logoutRedirectUri); SampleAppManager.PassportInstance = passport; From 998578122541c4ee02926483b9286537a2ddd741 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 23 Sep 2025 19:47:54 +1200 Subject: [PATCH 56/75] fix: move sample init with passport ui to ignore it from builds --- .../UI}/PassportInitialisationWithUIScript.cs | 0 .../UI}/PassportInitialisationWithUIScript.cs.meta | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename sample/Assets/{Scripts/Passport/PassportInitialisation => Editor/UI}/PassportInitialisationWithUIScript.cs (100%) rename sample/Assets/{Scripts/Passport/PassportInitialisation => Editor/UI}/PassportInitialisationWithUIScript.cs.meta (100%) diff --git a/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationWithUIScript.cs b/sample/Assets/Editor/UI/PassportInitialisationWithUIScript.cs similarity index 100% rename from sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationWithUIScript.cs rename to sample/Assets/Editor/UI/PassportInitialisationWithUIScript.cs diff --git a/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationWithUIScript.cs.meta b/sample/Assets/Editor/UI/PassportInitialisationWithUIScript.cs.meta similarity index 100% rename from sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationWithUIScript.cs.meta rename to sample/Assets/Editor/UI/PassportInitialisationWithUIScript.cs.meta From bd43fd9b76c65f7d561eefe6e44450f9330ba03e Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 23 Sep 2025 19:48:52 +1200 Subject: [PATCH 57/75] fix: nullable warnings in ci tests --- .../Runtime/Scripts/Private/Model/DirectLoginOptions.cs | 4 ++-- src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginOptions.cs b/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginOptions.cs index 9915e9a0..7c23dae2 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginOptions.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/Model/DirectLoginOptions.cs @@ -17,7 +17,7 @@ public class DirectLoginOptions /// /// Email address for email-based authentication (only used when directLoginMethod is Email). /// - public string email; + public string? email; /// /// Marketing consent status. Defaults to OptedIn. Required by Auth endpoints. @@ -40,7 +40,7 @@ public DirectLoginOptions() /// The direct login method /// The email address (optional) /// Marketing consent status. Defaults to OptedIn if not specified. Required. - public DirectLoginOptions(DirectLoginMethod loginMethod, string emailAddress = null, MarketingConsentStatus? marketingConsentStatus = null) + public DirectLoginOptions(DirectLoginMethod loginMethod, string? emailAddress = null, MarketingConsentStatus? marketingConsentStatus = null) { directLoginMethod = loginMethod; email = emailAddress; diff --git a/src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs b/src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs index ac45c427..39521147 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/PassportImpl.cs @@ -98,7 +98,7 @@ public void SetCallTimeout(int ms) _communicationsManager.SetCallTimeout(ms); } - public UniTask Login(bool useCachedSession = false, DirectLoginOptions directLoginOptions = null) + public UniTask Login(bool useCachedSession = false, DirectLoginOptions? directLoginOptions = null) { if (useCachedSession) { @@ -163,7 +163,7 @@ private async UniTask Relogin() return false; } - public async UniTask ConnectImx(bool useCachedSession = false, DirectLoginOptions directLoginOptions = null) + public async UniTask ConnectImx(bool useCachedSession = false, DirectLoginOptions? directLoginOptions = null) { if (useCachedSession) { From 8ae2f740f3dd04dad28d306fd31fb78388f59a21 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Wed, 24 Sep 2025 12:52:40 +1200 Subject: [PATCH 58/75] feat: define vuplex symbol on load --- .../Passport/Editor/VuplexSymbolManager.cs | 108 ++++++++++++++++++ .../Editor/VuplexSymbolManager.cs.meta | 11 ++ .../UI/WebViews/MacOSPassportWebView.cs | 37 +----- .../Runtime/Scripts/Public/PassportUI.cs | 2 +- 4 files changed, 122 insertions(+), 36 deletions(-) create mode 100644 src/Packages/Passport/Editor/VuplexSymbolManager.cs create mode 100644 src/Packages/Passport/Editor/VuplexSymbolManager.cs.meta diff --git a/src/Packages/Passport/Editor/VuplexSymbolManager.cs b/src/Packages/Passport/Editor/VuplexSymbolManager.cs new file mode 100644 index 00000000..3202c4c2 --- /dev/null +++ b/src/Packages/Passport/Editor/VuplexSymbolManager.cs @@ -0,0 +1,108 @@ +using UnityEngine; +using UnityEditor; +using System.Linq; + +namespace Immutable.Passport.Editor +{ + /// + /// Automatically manages VUPLEX_WEBVIEW scripting define symbol + /// based on whether Vuplex WebView is present in the project + /// + [InitializeOnLoad] + public static class VuplexSymbolManager + { + private const string VUPLEX_SYMBOL = "VUPLEX_WEBVIEW"; + + static VuplexSymbolManager() + { + // Run on editor startup and after assembly reload + EditorApplication.delayCall += CheckAndUpdateVuplexSymbol; + } + + private static void CheckAndUpdateVuplexSymbol() + { + try + { + // Check if Vuplex WebView assembly exists + bool vuplexExists = DoesVuplexAssemblyExist(); + + // Get current build target group + var buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup; + + // Get current scripting define symbols + var currentSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup); + var symbolList = currentSymbols.Split(';').Where(s => !string.IsNullOrEmpty(s)).ToList(); + + bool symbolExists = symbolList.Contains(VUPLEX_SYMBOL); + + // Update symbol if needed + if (vuplexExists && !symbolExists) + { + // Add symbol + symbolList.Add(VUPLEX_SYMBOL); + var newSymbols = string.Join(";", symbolList); + PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, newSymbols); + Debug.Log($"[VuplexSymbolManager] Added {VUPLEX_SYMBOL} symbol - Vuplex WebView detected"); + } + else if (!vuplexExists && symbolExists) + { + // Remove symbol + symbolList.Remove(VUPLEX_SYMBOL); + var newSymbols = string.Join(";", symbolList); + PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, newSymbols); + Debug.Log($"[VuplexSymbolManager] Removed {VUPLEX_SYMBOL} symbol - Vuplex WebView not found"); + } + + // Log current status (only once per session to avoid spam) + if (!SessionState.GetBool("VuplexSymbolManager.LoggedStatus", false)) + { + if (vuplexExists) + { + Debug.Log($"[VuplexSymbolManager] Vuplex WebView available, {VUPLEX_SYMBOL} symbol active"); + } + else + { + Debug.Log($"[VuplexSymbolManager] Vuplex WebView not found"); + } + SessionState.SetBool("VuplexSymbolManager.LoggedStatus", true); + } + } + catch (System.Exception ex) + { + Debug.LogError($"[VuplexSymbolManager] Error managing Vuplex symbol: {ex.Message}"); + } + } + + private static bool DoesVuplexAssemblyExist() + { + // Check for Vuplex assembly in multiple ways + + // Method 1: Check compiled assemblies + var assemblies = System.AppDomain.CurrentDomain.GetAssemblies(); + if (assemblies.Any(a => a.GetName().Name == "Vuplex.WebView")) + { + return true; + } + + // Method 2: Check for Vuplex types + var vuplexType = System.Type.GetType("Vuplex.WebView.CanvasWebViewPrefab, Vuplex.WebView"); + if (vuplexType != null) + { + return true; + } + + // Method 3: Check for Vuplex assets in project + var vuplexAssets = AssetDatabase.FindAssets("CanvasWebViewPrefab"); + foreach (var guid in vuplexAssets) + { + var path = AssetDatabase.GUIDToAssetPath(guid); + if (path.Contains("Vuplex")) + { + return true; + } + } + + return false; + } + } +} diff --git a/src/Packages/Passport/Editor/VuplexSymbolManager.cs.meta b/src/Packages/Passport/Editor/VuplexSymbolManager.cs.meta new file mode 100644 index 00000000..36e8e3e3 --- /dev/null +++ b/src/Packages/Passport/Editor/VuplexSymbolManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 285b7ffebc370477a823844aa4c8aa83 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/MacOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/MacOSPassportWebView.cs index 543a1da9..78148591 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/MacOSPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/MacOSPassportWebView.cs @@ -6,9 +6,8 @@ using UnityEngine.UI; using Immutable.Passport.Core.Logging; -#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX +#if (UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX) && VUPLEX_WEBVIEW using Vuplex.WebView; -#endif namespace Immutable.Passport { @@ -21,9 +20,7 @@ public class MacOSPassportWebView : IPassportWebView { private const string TAG = "[MacOSPassportWebView]"; -#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX private CanvasWebViewPrefab? _webViewPrefab; -#endif private readonly Dictionary> _jsHandlers = new Dictionary>(); private readonly RawImage _canvasReference; private bool _isInitialized = false; @@ -33,14 +30,8 @@ public class MacOSPassportWebView : IPassportWebView public event Action? OnLoadFinished; public event Action? OnLoadStarted; - // Safe access - check initialization -#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX public bool IsVisible => _webViewPrefab?.Visible ?? false; public string CurrentUrl => _webViewPrefab?.WebView?.Url ?? ""; -#else - public bool IsVisible => false; - public string CurrentUrl => ""; -#endif public MacOSPassportWebView(RawImage canvasReference) { @@ -55,7 +46,6 @@ public void Initialize(PassportWebViewConfig config) return; } -#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX try { PassportLogger.Info($"{TAG} Initializing MacOS WebView..."); @@ -68,13 +58,8 @@ public void Initialize(PassportWebViewConfig config) PassportLogger.Error($"{TAG} Failed to initialize: {ex.Message}"); throw; } -#else - PassportLogger.Warn($"{TAG} Vuplex WebView is only supported on MacOS builds, not in editor"); - _isInitialized = true; -#endif } -#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) { try @@ -162,11 +147,9 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) throw; } } -#endif public void LoadUrl(string url) { -#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX if (!_isInitialized || _webViewPrefab?.WebView == null) { PassportLogger.Info($"{TAG} WebView not ready, queueing URL: {url}"); @@ -176,40 +159,28 @@ public void LoadUrl(string url) PassportLogger.Info($"{TAG} Loading URL: {url}"); _webViewPrefab.WebView.LoadUrl(url); -#else - PassportLogger.Warn($"{TAG} LoadUrl not supported in MacOS editor mode"); -#endif } public void Show() { -#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX if (_webViewPrefab != null) { _webViewPrefab.Visible = true; PassportLogger.Info($"{TAG} WebView shown"); } -#else - PassportLogger.Info($"{TAG} Show() called (editor mode)"); -#endif } public void Hide() { -#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX if (_webViewPrefab != null) { _webViewPrefab.Visible = false; PassportLogger.Info($"{TAG} WebView hidden"); } -#else - PassportLogger.Info($"{TAG} Hide() called (editor mode)"); -#endif } public void ExecuteJavaScript(string js) { -#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX if (!_isInitialized || _webViewPrefab?.WebView == null) { PassportLogger.Error($"{TAG} Cannot execute JavaScript - MacOS WebView not initialized"); @@ -217,9 +188,6 @@ public void ExecuteJavaScript(string js) } _webViewPrefab.WebView.ExecuteJavaScript(js); -#else - PassportLogger.Warn($"{TAG} ExecuteJavaScript not supported in MacOS editor mode"); -#endif } public void RegisterJavaScriptMethod(string methodName, Action handler) @@ -233,17 +201,16 @@ public void RegisterJavaScriptMethod(string methodName, Action handler) public void Dispose() { -#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX if (_webViewPrefab != null) { PassportLogger.Info($"{TAG} Disposing MacOS WebView"); _webViewPrefab.Destroy(); _webViewPrefab = null; } -#endif _jsHandlers.Clear(); _isInitialized = false; } } } +#endif diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index 9bd75a5a..9e0898fa 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -408,7 +408,7 @@ private IPassportWebView CreatePlatformWebView() #elif UNITY_ANDROID PassportLogger.Info($"{TAG} Creating Android WebView (Vuplex)"); return new AndroidVuplexWebView(rawImage); -#elif UNITY_STANDALONE_OSX +#elif (UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX) && VUPLEX_WEBVIEW PassportLogger.Info($"{TAG} Creating MacOS WebView (Vuplex)"); return new MacOSPassportWebView(rawImage); #else From 29165dbbd1ad7b27525ad04b7dd8f2de5347d6df Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Wed, 24 Sep 2025 13:35:02 +1200 Subject: [PATCH 59/75] fix: linting --- .../Passport/Editor/VuplexSymbolManager.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Packages/Passport/Editor/VuplexSymbolManager.cs b/src/Packages/Passport/Editor/VuplexSymbolManager.cs index 3202c4c2..31750163 100644 --- a/src/Packages/Passport/Editor/VuplexSymbolManager.cs +++ b/src/Packages/Passport/Editor/VuplexSymbolManager.cs @@ -12,29 +12,29 @@ namespace Immutable.Passport.Editor public static class VuplexSymbolManager { private const string VUPLEX_SYMBOL = "VUPLEX_WEBVIEW"; - + static VuplexSymbolManager() { // Run on editor startup and after assembly reload EditorApplication.delayCall += CheckAndUpdateVuplexSymbol; } - + private static void CheckAndUpdateVuplexSymbol() { try { // Check if Vuplex WebView assembly exists bool vuplexExists = DoesVuplexAssemblyExist(); - + // Get current build target group var buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup; - + // Get current scripting define symbols var currentSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup); var symbolList = currentSymbols.Split(';').Where(s => !string.IsNullOrEmpty(s)).ToList(); - + bool symbolExists = symbolList.Contains(VUPLEX_SYMBOL); - + // Update symbol if needed if (vuplexExists && !symbolExists) { @@ -52,7 +52,7 @@ private static void CheckAndUpdateVuplexSymbol() PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, newSymbols); Debug.Log($"[VuplexSymbolManager] Removed {VUPLEX_SYMBOL} symbol - Vuplex WebView not found"); } - + // Log current status (only once per session to avoid spam) if (!SessionState.GetBool("VuplexSymbolManager.LoggedStatus", false)) { @@ -72,25 +72,25 @@ private static void CheckAndUpdateVuplexSymbol() Debug.LogError($"[VuplexSymbolManager] Error managing Vuplex symbol: {ex.Message}"); } } - + private static bool DoesVuplexAssemblyExist() { // Check for Vuplex assembly in multiple ways - + // Method 1: Check compiled assemblies var assemblies = System.AppDomain.CurrentDomain.GetAssemblies(); if (assemblies.Any(a => a.GetName().Name == "Vuplex.WebView")) { return true; } - + // Method 2: Check for Vuplex types var vuplexType = System.Type.GetType("Vuplex.WebView.CanvasWebViewPrefab, Vuplex.WebView"); if (vuplexType != null) { return true; } - + // Method 3: Check for Vuplex assets in project var vuplexAssets = AssetDatabase.FindAssets("CanvasWebViewPrefab"); foreach (var guid in vuplexAssets) @@ -101,7 +101,7 @@ private static bool DoesVuplexAssemblyExist() return true; } } - + return false; } } From ed4c3ce96e4aa4b8106fac97862e755b5298017b Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Wed, 24 Sep 2025 14:24:52 +1200 Subject: [PATCH 60/75] fix: standalone platforms in editor build assemblies --- src/Packages/Passport/Editor/PassportEditor.asmdef | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Packages/Passport/Editor/PassportEditor.asmdef b/src/Packages/Passport/Editor/PassportEditor.asmdef index 4d5cf619..8ea966a5 100644 --- a/src/Packages/Passport/Editor/PassportEditor.asmdef +++ b/src/Packages/Passport/Editor/PassportEditor.asmdef @@ -3,9 +3,7 @@ "rootNamespace": "Immutable.Passport.Editor", "references": [], "includePlatforms": [ - "Editor", - "macOSStandalone", - "WindowsStandalone64" + "Editor" ], "excludePlatforms": [], "allowUnsafeCode": false, From a5b314f56a7b4903c55443a481b2287ddb265357 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Wed, 24 Sep 2025 14:45:51 +1200 Subject: [PATCH 61/75] fix: add vuplex webview compilation flags for mobile platforms --- src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index 9e0898fa..415e217e 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -402,10 +402,10 @@ private IPassportWebView CreatePlatformWebView() #if !IMMUTABLE_CUSTOM_BROWSER && (UNITY_STANDALONE_WIN || (UNITY_EDITOR && UNITY_EDITOR_WIN)) PassportLogger.Info($"{TAG} Creating Windows WebView (UWB)"); return new WindowsPassportWebView(rawImage, this); -#elif UNITY_IOS +#elif UNITY_IOS && VUPLEX_WEBVIEW PassportLogger.Info($"{TAG} Creating iOS WebView (Vuplex)"); return new iOSPassportWebView(rawImage); -#elif UNITY_ANDROID +#elif UNITY_ANDROID && VUPLEX_WEBVIEW PassportLogger.Info($"{TAG} Creating Android WebView (Vuplex)"); return new AndroidVuplexWebView(rawImage); #elif (UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX) && VUPLEX_WEBVIEW From 8909d99afe052dd363e1cecc5d82b67401a9b0c1 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Wed, 24 Sep 2025 15:17:31 +1200 Subject: [PATCH 62/75] fix: add vuplex webview compilation flags for mobile webviews --- .../UI/WebViews/AndroidVuplexWebView.cs | 31 ++----------------- .../Private/UI/WebViews/iOSPassportWebView.cs | 31 ++----------------- 2 files changed, 4 insertions(+), 58 deletions(-) diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/AndroidVuplexWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/AndroidVuplexWebView.cs index e523781d..15efe2ed 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/AndroidVuplexWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/AndroidVuplexWebView.cs @@ -6,9 +6,8 @@ using UnityEngine.UI; using Immutable.Passport.Core.Logging; -#if UNITY_ANDROID && !UNITY_EDITOR +#if UNITY_ANDROID && VUPLEX_WEBVIEW using Vuplex.WebView; -#endif namespace Immutable.Passport { @@ -21,9 +20,7 @@ public class AndroidVuplexWebView : IPassportWebView { private const string TAG = "[AndroidVuplexWebView]"; -#if UNITY_ANDROID && !UNITY_EDITOR private CanvasWebViewPrefab? _webViewPrefab; -#endif private readonly Dictionary> _jsHandlers = new Dictionary>(); private readonly RawImage _canvasReference; private bool _isInitialized = false; @@ -32,14 +29,8 @@ public class AndroidVuplexWebView : IPassportWebView public event Action? OnLoadFinished; public event Action? OnLoadStarted; - // Safe access - check initialization -#if UNITY_ANDROID && !UNITY_EDITOR public bool IsVisible => _webViewPrefab?.Visible ?? false; public string CurrentUrl => _webViewPrefab?.WebView?.Url ?? ""; -#else - public bool IsVisible => false; - public string CurrentUrl => ""; -#endif public AndroidVuplexWebView(RawImage canvasReference) { @@ -54,7 +45,6 @@ public void Initialize(PassportWebViewConfig config) return; } -#if UNITY_ANDROID && !UNITY_EDITOR try { PassportLogger.Info($"{TAG} Initializing Vuplex WebView..."); @@ -67,13 +57,8 @@ public void Initialize(PassportWebViewConfig config) PassportLogger.Error($"{TAG} Failed to initialize: {ex.Message}"); throw; } -#else - PassportLogger.Warn($"{TAG} Vuplex WebView is only supported on Android builds, not in editor"); - _isInitialized = true; -#endif } -#if UNITY_ANDROID && !UNITY_EDITOR private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) { try @@ -155,11 +140,9 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) throw; } } -#endif public void LoadUrl(string url) { -#if UNITY_ANDROID && !UNITY_EDITOR if (!_isInitialized || _webViewPrefab?.WebView == null) { PassportLogger.Error($"{TAG} Cannot load URL - WebView not initialized"); @@ -167,34 +150,26 @@ public void LoadUrl(string url) } _webViewPrefab.WebView.LoadUrl(url); -#else - PassportLogger.Warn($"{TAG} LoadUrl not supported in editor mode"); -#endif } public void Show() { -#if UNITY_ANDROID && !UNITY_EDITOR if (_webViewPrefab != null) { _webViewPrefab.Visible = true; } -#endif } public void Hide() { -#if UNITY_ANDROID && !UNITY_EDITOR if (_webViewPrefab != null) { _webViewPrefab.Visible = false; } -#endif } public void ExecuteJavaScript(string js) { -#if UNITY_ANDROID && !UNITY_EDITOR if (!_isInitialized || _webViewPrefab?.WebView == null) { PassportLogger.Error($"{TAG} Cannot execute JavaScript - WebView not initialized"); @@ -202,7 +177,6 @@ public void ExecuteJavaScript(string js) } _webViewPrefab.WebView.ExecuteJavaScript(js); -#endif } public void RegisterJavaScriptMethod(string methodName, Action handler) @@ -216,16 +190,15 @@ public void RegisterJavaScriptMethod(string methodName, Action handler) public void Dispose() { -#if UNITY_ANDROID && !UNITY_EDITOR if (_webViewPrefab != null) { _webViewPrefab.Destroy(); _webViewPrefab = null; } -#endif _jsHandlers.Clear(); _isInitialized = false; } } } +#endif diff --git a/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/iOSPassportWebView.cs b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/iOSPassportWebView.cs index 518c4a46..70869b34 100644 --- a/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/iOSPassportWebView.cs +++ b/src/Packages/Passport/Runtime/Scripts/Private/UI/WebViews/iOSPassportWebView.cs @@ -6,9 +6,8 @@ using UnityEngine.UI; using Immutable.Passport.Core.Logging; -#if UNITY_IOS && !UNITY_EDITOR +#if UNITY_IOS && VUPLEX_WEBVIEW using Vuplex.WebView; -#endif namespace Immutable.Passport { @@ -21,9 +20,7 @@ public class iOSPassportWebView : IPassportWebView { private const string TAG = "[iOSPassportWebView]"; -#if UNITY_IOS && !UNITY_EDITOR private CanvasWebViewPrefab? _webViewPrefab; -#endif private readonly Dictionary> _jsHandlers = new Dictionary>(); private readonly RawImage _canvasReference; private bool _isInitialized = false; @@ -32,14 +29,8 @@ public class iOSPassportWebView : IPassportWebView public event Action? OnLoadFinished; public event Action? OnLoadStarted; - // Safe access - check initialization -#if UNITY_IOS && !UNITY_EDITOR public bool IsVisible => _webViewPrefab?.Visible ?? false; public string CurrentUrl => _webViewPrefab?.WebView?.Url ?? ""; -#else - public bool IsVisible => false; - public string CurrentUrl => ""; -#endif public iOSPassportWebView(RawImage canvasReference) { @@ -54,7 +45,6 @@ public void Initialize(PassportWebViewConfig config) return; } -#if UNITY_IOS && !UNITY_EDITOR try { PassportLogger.Info($"{TAG} Initializing iOS WebView..."); @@ -67,13 +57,8 @@ public void Initialize(PassportWebViewConfig config) PassportLogger.Error($"{TAG} Failed to initialize: {ex.Message}"); throw; } -#else - PassportLogger.Warn($"{TAG} Vuplex WebView is only supported on iOS builds, not in editor"); - _isInitialized = true; -#endif } -#if UNITY_IOS && !UNITY_EDITOR private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) { try @@ -149,11 +134,9 @@ private async UniTaskVoid InitializeAsync(PassportWebViewConfig config) throw; } } -#endif public void LoadUrl(string url) { -#if UNITY_IOS && !UNITY_EDITOR if (!_isInitialized || _webViewPrefab?.WebView == null) { PassportLogger.Error($"{TAG} Cannot load URL - iOS WebView not initialized"); @@ -161,34 +144,26 @@ public void LoadUrl(string url) } _webViewPrefab.WebView.LoadUrl(url); -#else - PassportLogger.Warn($"{TAG} LoadUrl not supported in iOS editor mode"); -#endif } public void Show() { -#if UNITY_IOS && !UNITY_EDITOR if (_webViewPrefab != null) { _webViewPrefab.Visible = true; } -#endif } public void Hide() { -#if UNITY_IOS && !UNITY_EDITOR if (_webViewPrefab != null) { _webViewPrefab.Visible = false; } -#endif } public void ExecuteJavaScript(string js) { -#if UNITY_IOS && !UNITY_EDITOR if (!_isInitialized || _webViewPrefab?.WebView == null) { PassportLogger.Error($"{TAG} Cannot execute JavaScript - iOS WebView not initialized"); @@ -196,7 +171,6 @@ public void ExecuteJavaScript(string js) } _webViewPrefab.WebView.ExecuteJavaScript(js); -#endif } public void RegisterJavaScriptMethod(string methodName, Action handler) @@ -210,16 +184,15 @@ public void RegisterJavaScriptMethod(string methodName, Action handler) public void Dispose() { -#if UNITY_IOS && !UNITY_EDITOR if (_webViewPrefab != null) { _webViewPrefab.Destroy(); _webViewPrefab = null; } -#endif _jsHandlers.Clear(); _isInitialized = false; } } } +#endif From 0e1ff868ccc7635056153c7611ac32e47e29669e Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Thu, 25 Sep 2025 14:48:04 +1200 Subject: [PATCH 63/75] fix: windows tests chrome driver path missing --- sample/Tests/test/test_windows_helpers.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sample/Tests/test/test_windows_helpers.py b/sample/Tests/test/test_windows_helpers.py index dbf92891..0bb79b66 100644 --- a/sample/Tests/test/test_windows_helpers.py +++ b/sample/Tests/test/test_windows_helpers.py @@ -150,8 +150,17 @@ def logout_with_controlled_browser(): chrome_options.add_experimental_option("debuggerAddress", "localhost:9222") try: - # Connect to the existing browser instance - driver = webdriver.Chrome(options=chrome_options) + # Connect to the existing browser instance with explicit ChromeDriver path + from selenium.webdriver.chrome.service import Service + chromedriver_path = r"C:\Users\WindowsBuildsdkServi\Development\chromedriver-win64\chromedriver-win64\chromedriver.exe" + + import os + if os.path.exists(chromedriver_path): + service = Service(executable_path=chromedriver_path) + else: + service = Service() # Let Selenium Manager handle it + + driver = webdriver.Chrome(service=service, options=chrome_options) print("Connected to existing browser for logout") # Monitor Unity logs for logout URL From 9b2d22da19a31b94c058fe7a269fa41ebafad946 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Thu, 25 Sep 2025 15:22:33 +1200 Subject: [PATCH 64/75] fix: windows test browser path --- sample/Tests/test/test_windows_helpers.py | 27 +++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/sample/Tests/test/test_windows_helpers.py b/sample/Tests/test/test_windows_helpers.py index 0bb79b66..2c47f702 100644 --- a/sample/Tests/test/test_windows_helpers.py +++ b/sample/Tests/test/test_windows_helpers.py @@ -150,15 +150,16 @@ def logout_with_controlled_browser(): chrome_options.add_experimental_option("debuggerAddress", "localhost:9222") try: - # Connect to the existing browser instance with explicit ChromeDriver path + # Connect to the existing browser instance with explicit paths from selenium.webdriver.chrome.service import Service chromedriver_path = r"C:\Users\WindowsBuildsdkServi\Development\chromedriver-win64\chromedriver-win64\chromedriver.exe" + brave_path = r"C:\Program Files\BraveSoftware\Brave-Browser\Application\brave.exe" - import os - if os.path.exists(chromedriver_path): - service = Service(executable_path=chromedriver_path) - else: - service = Service() # Let Selenium Manager handle it + # Set Brave as the browser binary + chrome_options.binary_location = brave_path + + # Create service with explicit ChromeDriver path + service = Service(executable_path=chromedriver_path) driver = webdriver.Chrome(service=service, options=chrome_options) print("Connected to existing browser for logout") @@ -287,8 +288,20 @@ def login(): # (Brave uses Chromium engine so Chrome WebDriver works) chrome_options = Options() chrome_options.add_experimental_option("debuggerAddress", "localhost:9222") + + # Explicitly specify ChromeDriver path and Brave browser path + from selenium.webdriver.chrome.service import Service + chromedriver_path = r"C:\Users\WindowsBuildsdkServi\Development\chromedriver-win64\chromedriver-win64\chromedriver.exe" + brave_path = r"C:\Program Files\BraveSoftware\Brave-Browser\Application\brave.exe" + + # Set Brave as the browser binary + chrome_options.binary_location = brave_path + + # Create service with explicit ChromeDriver path + service = Service(executable_path=chromedriver_path) + # Connect to the existing Brave browser instance - driver = webdriver.Chrome(options=chrome_options) + driver = webdriver.Chrome(service=service, options=chrome_options) # HYBRID APPROACH: Try multi-window detection first (proven to work in CI), # then fall back to Unity log monitoring if needed From 47b427f5ba9d826aaabfbf48a90d244295d570c9 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Thu, 25 Sep 2025 16:11:43 +1200 Subject: [PATCH 65/75] fix: sample game passport client --- .../PassportInitialisation/PassportInitialisationScript.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs b/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs index c2527e28..fe9be7ba 100644 --- a/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs +++ b/sample/Assets/Scripts/Passport/PassportInitialisation/PassportInitialisationScript.cs @@ -50,8 +50,8 @@ private async void InitialisePassport() // Initialise Passport const string environment = Immutable.Passport.Model.Environment.SANDBOX; - // const string clientId = "mp6rxfMDwwZDogcdgNrAaHnG0qMlXuMK"; - const string clientId = "IllW5pJ54DShXtaSXzaAlghm40uQjptd"; + const string clientId = "mp6rxfMDwwZDogcdgNrAaHnG0qMlXuMK"; + // const string clientId = "IllW5pJ54DShXtaSXzaAlghm40uQjptd"; var passport = await Passport.Init(clientId, environment, redirectUri, logoutRedirectUri); SampleAppManager.PassportInstance = passport; From 3ff6814abccdf73742bc279cb45539af06a43017 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Thu, 25 Sep 2025 17:11:31 +1200 Subject: [PATCH 66/75] chore: remove sample game settings from prefabs --- .../Prefabs/PassportLogin_Vuplex.prefab | 10 ++++---- .../Prefabs/PassportLogin_Windows.prefab | 10 ++++---- .../Runtime/Scripts/Public/PassportUI.cs | 24 ++++++++----------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/Packages/Passport/Prefabs/PassportLogin_Vuplex.prefab b/src/Packages/Passport/Prefabs/PassportLogin_Vuplex.prefab index 6ecfdf10..3ea74854 100644 --- a/src/Packages/Passport/Prefabs/PassportLogin_Vuplex.prefab +++ b/src/Packages/Passport/Prefabs/PassportLogin_Vuplex.prefab @@ -197,10 +197,10 @@ MonoBehaviour: enableRemoteDebugging: 1 remoteDebuggingPort: 9222 clearCacheOnLogin: 1 - clientId: IllW5pJ54DShXtaSXzaAlghm40uQjptd + clientId: environment: sandbox - redirectUri: immutablerunner://callback - logoutRedirectUri: immutablerunner://logout + redirectUri: + logoutRedirectUri: webViewBaseUrl: https://auth.immutable.com/im-embedded-login-prompt - webViewWidth: 1920 - webViewHeight: 1080 + webViewWidth: 800 + webViewHeight: 600 diff --git a/src/Packages/Passport/Prefabs/PassportLogin_Windows.prefab b/src/Packages/Passport/Prefabs/PassportLogin_Windows.prefab index 63af83a2..0a1af10b 100644 --- a/src/Packages/Passport/Prefabs/PassportLogin_Windows.prefab +++ b/src/Packages/Passport/Prefabs/PassportLogin_Windows.prefab @@ -160,10 +160,10 @@ MonoBehaviour: enableRemoteDebugging: 0 remoteDebuggingPort: 9222 clearCacheOnLogin: 1 - clientId: IllW5pJ54DShXtaSXzaAlghm40uQjptd + clientId: environment: sandbox - redirectUri: mygame://callback - logoutRedirectUri: mygame://logout + redirectUri: + logoutRedirectUri: webViewBaseUrl: https://auth.immutable.com/im-embedded-login-prompt - webViewWidth: 1920 - webViewHeight: 1080 + webViewWidth: 800 + webViewHeight: 600 diff --git a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs index 415e217e..4a89a007 100644 --- a/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs +++ b/src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs @@ -95,21 +95,21 @@ public class PassportUI : MonoBehaviour [Tooltip("Passport environment (sandbox or production)")] [SerializeField] private string environment = "sandbox"; - [Tooltip("OAuth redirect URI for authentication callbacks")] - [SerializeField] private string redirectUri = "immutablerunner://callback"; + [Tooltip("Redirect URI for authentication callbacks (configure in the Immutable Developer Hub)")] + [SerializeField] private string redirectUri = ""; - [Tooltip("OAuth logout redirect URI for logout callbacks")] - [SerializeField] private string logoutRedirectUri = "immutablerunner://logout"; + [Tooltip("OAuth logout redirect URI for logout callbacks (configure in the Immutable Developer Hub)")] + [SerializeField] private string logoutRedirectUri = ""; [Header("WebView Settings")] - [Tooltip("Base URL for the WebView authentication page. The client_id parameter will be automatically appended from the Passport Configuration above.")] - [SerializeField] private string webViewBaseUrl = "https://auth.immutable.com/im-embedded-login-prompt"; + // Internal base URL for Passport authentication - users don't need to modify this + private const string webViewBaseUrl = "https://auth.immutable.com/im-embedded-login-prompt"; [Tooltip("Width of the WebView in pixels. Set to 0 to use the RawImage's current width.")] - [SerializeField] private int webViewWidth = 1920; + [SerializeField] private int webViewWidth = 800; [Tooltip("Height of the WebView in pixels. Set to 0 to use the RawImage's current height.")] - [SerializeField] private int webViewHeight = 1080; + [SerializeField] private int webViewHeight = 600; /// /// Gets the complete WebView URL with the client ID automatically appended @@ -127,13 +127,9 @@ public string WebViewUrl } /// - /// Gets or sets the base WebView URL (without client_id parameter) + /// Gets the base WebView URL (without client_id parameter) - read-only /// - public string WebViewBaseUrl - { - get => webViewBaseUrl; - set => webViewBaseUrl = value; - } + public string WebViewBaseUrl => webViewBaseUrl; /// /// Gets or sets the WebView width in pixels From e13832b94918e5f2318def73823674b9011a88e2 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Thu, 25 Sep 2025 17:12:08 +1200 Subject: [PATCH 67/75] fix: mac test webdriver config --- sample/Tests/test/test_mac.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sample/Tests/test/test_mac.py b/sample/Tests/test/test_mac.py index 77f85ca6..22a8dfe7 100644 --- a/sample/Tests/test/test_mac.py +++ b/sample/Tests/test/test_mac.py @@ -92,8 +92,20 @@ def login(cls): # Set up Chrome options to connect to the existing Chrome instance chrome_options = Options() chrome_options.add_experimental_option("debuggerAddress", "localhost:9222") + + # Explicitly specify ChromeDriver path and Chrome browser path for macOS + from selenium.webdriver.chrome.service import Service + chromedriver_path = "/usr/local/bin/chromedriver" + chrome_path = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" + + # Set Chrome as the browser binary + chrome_options.binary_location = chrome_path + + # Create service with explicit ChromeDriver path + service = Service(executable_path=chromedriver_path) + # Connect to the existing Chrome instance - cls.seleniumdriver = webdriver.Chrome(options=chrome_options) + cls.seleniumdriver = webdriver.Chrome(service=service, options=chrome_options) print("Open a window on Chrome") From 3433f66426a953ae9681219b6dc19d66e2732c08 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Thu, 25 Sep 2025 21:30:54 +1200 Subject: [PATCH 68/75] chore: update bridge and sample game mac test from main --- sample/Tests/test/test_mac.py | 62 +++++--- .../Passport/Runtime/Resources/index.html | 136 +++++++++++++----- 2 files changed, 146 insertions(+), 52 deletions(-) diff --git a/sample/Tests/test/test_mac.py b/sample/Tests/test/test_mac.py index 22a8dfe7..ce4203d4 100644 --- a/sample/Tests/test/test_mac.py +++ b/sample/Tests/test/test_mac.py @@ -50,19 +50,33 @@ def launch_browser(cls): break if not browser_path: - print("Brave executable not found.") + print("Brave Browser executable not found.") exit(1) subprocess.Popen([ browser_path, - "--remote-debugging-port=9222" + "--remote-debugging-port=9222", + "--no-first-run", + "--no-default-browser-check" ]) - time.sleep(5) + # Give Brave more time to fully initialize remote debugging + print("Waiting for Brave to fully initialize...") + time.sleep(10) + + # Verify remote debugging is accessible + try: + import urllib.request + with urllib.request.urlopen("http://127.0.0.1:9222/json", timeout=5) as response: + tabs = response.read() + print(f"Remote debugging verified - found {len(eval(tabs))} tabs") + except Exception as e: + print(f"Remote debugging check failed: {e}") + print("Continuing anyway...") @classmethod def stop_browser(cls): - print("Stopping Brave...") + print("Stopping Brave Browser...") try: # First try graceful shutdown using AppleScript subprocess.run([ @@ -78,36 +92,50 @@ def stop_browser(cls): # Still running, force kill subprocess.run(["pkill", "-f", "Brave Browser"], check=False, capture_output=True) + print("Killed Brave Browser processes") - print("All Brave processes have been closed.") + print("Brave Browser has been closed.") except Exception as e: - print("Brave might not be running.") + print("Brave Browser might not be running.") time.sleep(3) - print("Stopped Brave") + print("Stopped Brave Browser") @classmethod def login(cls): - print("Connect to Chrome") - # Set up Chrome options to connect to the existing Chrome instance + print("Connect to Brave Browser") + # Set up Chrome options to connect to the existing Brave instance (Brave uses Chromium engine) chrome_options = Options() chrome_options.add_experimental_option("debuggerAddress", "localhost:9222") - + # Explicitly specify ChromeDriver path and Chrome browser path for macOS from selenium.webdriver.chrome.service import Service chromedriver_path = "/usr/local/bin/chromedriver" - chrome_path = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" - # Set Chrome as the browser binary - chrome_options.binary_location = chrome_path + # Use Brave Browser only for macOS automation + brave_path = "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser" - # Create service with explicit ChromeDriver path - service = Service(executable_path=chromedriver_path) + import os + browser_path = None + if os.path.exists(brave_path): + browser_path = brave_path + print(f"Found Brave at: {browser_path}") + else: + print("Brave Browser not found - required for macOS tests") + raise FileNotFoundError("Brave Browser is required for macOS CI tests") - # Connect to the existing Chrome instance + # Set Brave as the browser binary if found + if browser_path: + chrome_options.binary_location = browser_path + + # Create service with explicit ChromeDriver path and bypass version checking + service_args = ["--whitelisted-ips=", "--disable-build-check"] + service = Service(executable_path=chromedriver_path, service_args=service_args) + + # Connect to the existing Brave instance cls.seleniumdriver = webdriver.Chrome(service=service, options=chrome_options) - print("Open a window on Chrome") + print("Open a window on Brave") wait = WebDriverWait(cls.seleniumdriver, 60) diff --git a/src/Packages/Passport/Runtime/Resources/index.html b/src/Packages/Passport/Runtime/Resources/index.html index 6b03c737..f0a72fbc 100644 --- a/src/Packages/Passport/Runtime/Resources/index.html +++ b/src/Packages/Passport/Runtime/Resources/index.html @@ -1,4 +1,4 @@ -GameSDK Bridge