-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathAndroidPassportWebView.cs
More file actions
333 lines (276 loc) · 11 KB
/
Copy pathAndroidPassportWebView.cs
File metadata and controls
333 lines (276 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
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
/// <summary>
/// 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
/// </summary>
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<string> OnJavaScriptMessage;
public event Action OnLoadFinished;
public event Action OnLoadStarted;
// Properties
public bool IsVisible => isVisible;
public string CurrentUrl => currentUrl;
/// <summary>
/// Constructor for Android PassportWebView
/// </summary>
/// <param name="targetRawImage">RawImage component to display WebView content (not used on Android - native overlay)</param>
/// <param name="coroutineRunner">MonoBehaviour to run coroutines (for consistency with other platforms)</param>
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<string> 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
}