1+ using System ;
2+ using System . Collections . Generic ;
3+ using Cysharp . Threading . Tasks ;
4+ using UnityEngine ;
5+ using UnityEngine . UI ;
6+ using Immutable . Passport . Core . Logging ;
7+ using Vuplex . WebView ;
8+
9+ namespace Immutable . Passport
10+ {
11+ public class AndroidVuplexWebView : IPassportWebView
12+ {
13+ private const string TAG = "[AndroidWebView]" ;
14+ private CanvasWebViewPrefab _webViewPrefab ;
15+ private readonly Dictionary < string , Action < string > > _jsHandlers = new Dictionary < string , Action < string > > ( ) ;
16+ private readonly RawImage _canvasReference ;
17+ private bool _isInitialized = false ;
18+
19+ public event Action < string > OnJavaScriptMessage ;
20+ public event Action OnLoadFinished ;
21+ public event Action OnLoadStarted ;
22+
23+ // Safe access - check initialization
24+ public bool IsVisible => _webViewPrefab ? . Visible ?? false ;
25+ public string CurrentUrl => _webViewPrefab ? . WebView ? . Url ?? "" ;
26+
27+ public AndroidVuplexWebView ( RawImage canvasReference )
28+ {
29+ _canvasReference = canvasReference ?? throw new ArgumentNullException ( nameof ( canvasReference ) ) ;
30+ }
31+
32+ public void Initialize ( PassportWebViewConfig config )
33+ {
34+ if ( _isInitialized )
35+ {
36+ PassportLogger . Warn ( $ "{ TAG } Already initialized, skipping") ;
37+ return ;
38+ }
39+
40+ try
41+ {
42+ PassportLogger . Info ( $ "{ TAG } Initializing Vuplex WebView...") ;
43+
44+ // Start async initialization but don't wait
45+ InitializeAsync ( config ) . Forget ( ) ;
46+ }
47+ catch ( Exception ex )
48+ {
49+ PassportLogger . Error ( $ "{ TAG } Failed to initialize: { ex . Message } ") ;
50+ throw ;
51+ }
52+ }
53+
54+ private async UniTaskVoid InitializeAsync ( PassportWebViewConfig config )
55+ {
56+ try
57+ {
58+ // Create WebView prefab and parent to Canvas
59+ _webViewPrefab = CanvasWebViewPrefab . Instantiate ( ) ;
60+ _webViewPrefab . Native2DModeEnabled = true ;
61+
62+ // Must be child of Canvas for Vuplex to work
63+ _webViewPrefab . transform . SetParent ( _canvasReference . canvas . transform , false ) ;
64+
65+ // Set up full-screen layout for Native 2D Mode
66+ var rect = _webViewPrefab . GetComponent < RectTransform > ( ) ;
67+ rect . anchorMin = Vector2 . zero ;
68+ rect . anchorMax = Vector2 . one ;
69+ rect . offsetMin = rect . offsetMax = Vector2 . zero ;
70+
71+ // Wait for WebView initialization
72+ await _webViewPrefab . WaitUntilInitialized ( ) ;
73+
74+ // Setup event handlers
75+ _webViewPrefab . WebView . LoadProgressChanged += ( s , e ) =>
76+ {
77+ if ( e . Type == ProgressChangeType . Started )
78+ {
79+ OnLoadStarted ? . Invoke ( ) ;
80+ }
81+ else if ( e . Type == ProgressChangeType . Finished )
82+ {
83+ OnLoadFinished ? . Invoke ( ) ;
84+ }
85+ } ;
86+ _webViewPrefab . WebView . MessageEmitted += ( s , e ) =>
87+ {
88+ foreach ( var h in _jsHandlers )
89+ {
90+ if ( e . Value . StartsWith ( $ "{ h . Key } :") )
91+ {
92+ h . Value ? . Invoke ( e . Value . Substring ( h . Key . Length + 1 ) ) ;
93+ return ;
94+ }
95+ }
96+
97+ OnJavaScriptMessage ? . Invoke ( e . Value ) ;
98+ } ;
99+ _webViewPrefab . WebView . LoadFailed += ( s , e ) => PassportLogger . Warn ( $ "{ TAG } Load failed: { e . NativeErrorCode } for { e . Url } ") ;
100+
101+ _isInitialized = true ;
102+ PassportLogger . Info ( $ "{ TAG } Vuplex WebView initialized successfully") ;
103+ }
104+ catch ( Exception ex )
105+ {
106+ PassportLogger . Error ( $ "{ TAG } Failed to initialize async: { ex . Message } ") ;
107+ throw ;
108+ }
109+ }
110+
111+ public void LoadUrl ( string url )
112+ {
113+ if ( ! _isInitialized || _webViewPrefab ? . WebView == null )
114+ {
115+ PassportLogger . Error ( $ "{ TAG } Cannot load URL - WebView not initialized") ;
116+ return ;
117+ }
118+
119+ _webViewPrefab . WebView . LoadUrl ( url ) ;
120+ }
121+
122+ public void Show ( )
123+ {
124+ if ( _webViewPrefab != null )
125+ {
126+ _webViewPrefab . Visible = true ;
127+ }
128+ }
129+
130+ public void Hide ( )
131+ {
132+ if ( _webViewPrefab != null )
133+ {
134+ _webViewPrefab . Visible = false ;
135+ }
136+ }
137+
138+ public void ExecuteJavaScript ( string js )
139+ {
140+ if ( ! _isInitialized || _webViewPrefab ? . WebView == null )
141+ {
142+ PassportLogger . Error ( $ "{ TAG } Cannot execute JavaScript - WebView not initialized") ;
143+ return ;
144+ }
145+
146+ _webViewPrefab . WebView . ExecuteJavaScript ( js ) ;
147+ }
148+
149+ public void RegisterJavaScriptMethod ( string methodName , Action < string > handler )
150+ {
151+ _jsHandlers [ methodName ] = handler ;
152+
153+ if ( _isInitialized && _webViewPrefab ? . WebView != null )
154+ {
155+ ExecuteJavaScript ( $ "window.{ methodName } =d=>window.vuplex?.postMessage('{ methodName } :'+(typeof d==='object'?JSON.stringify(d):d))") ;
156+ }
157+ }
158+
159+ public void Dispose ( )
160+ {
161+ if ( _webViewPrefab != null )
162+ {
163+ _webViewPrefab . Destroy ( ) ;
164+ _webViewPrefab = null ;
165+ }
166+
167+ _jsHandlers . Clear ( ) ;
168+ _isInitialized = false ;
169+ }
170+ }
171+ }
0 commit comments