|
@@ -4,24 +4,6 @@ import android.annotation.SuppressLint;
|
4
|
4
|
import android.annotation.TargetApi;
|
5
|
5
|
import android.app.DownloadManager;
|
6
|
6
|
import android.content.Context;
|
7
|
|
-
|
8
|
|
-import com.facebook.react.uimanager.UIManagerModule;
|
9
|
|
-
|
10
|
|
-import java.net.MalformedURLException;
|
11
|
|
-import java.net.URL;
|
12
|
|
-import java.util.LinkedList;
|
13
|
|
-import java.util.List;
|
14
|
|
-import java.util.regex.Pattern;
|
15
|
|
-import javax.annotation.Nullable;
|
16
|
|
-
|
17
|
|
-import java.io.UnsupportedEncodingException;
|
18
|
|
-import java.net.URLEncoder;
|
19
|
|
-import java.util.ArrayList;
|
20
|
|
-import java.util.HashMap;
|
21
|
|
-import java.util.Locale;
|
22
|
|
-import java.util.Map;
|
23
|
|
-
|
24
|
|
-import android.content.ActivityNotFoundException;
|
25
|
7
|
import android.content.Intent;
|
26
|
8
|
import android.graphics.Bitmap;
|
27
|
9
|
import android.net.Uri;
|
|
@@ -43,7 +25,6 @@ import android.webkit.WebSettings;
|
43
|
25
|
import android.webkit.WebView;
|
44
|
26
|
import android.webkit.WebViewClient;
|
45
|
27
|
|
46
|
|
-import com.facebook.common.logging.FLog;
|
47
|
28
|
import com.facebook.react.bridge.Arguments;
|
48
|
29
|
import com.facebook.react.bridge.LifecycleEventListener;
|
49
|
30
|
import com.facebook.react.bridge.ReactContext;
|
|
@@ -52,67 +33,62 @@ import com.facebook.react.bridge.ReadableMap;
|
52
|
33
|
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
53
|
34
|
import com.facebook.react.bridge.WritableMap;
|
54
|
35
|
import com.facebook.react.common.MapBuilder;
|
55
|
|
-import com.facebook.react.common.ReactConstants;
|
56
|
36
|
import com.facebook.react.common.build.ReactBuildConfig;
|
57
|
37
|
import com.facebook.react.module.annotations.ReactModule;
|
58
|
38
|
import com.facebook.react.uimanager.SimpleViewManager;
|
59
|
39
|
import com.facebook.react.uimanager.ThemedReactContext;
|
|
40
|
+import com.facebook.react.uimanager.UIManagerModule;
|
60
|
41
|
import com.facebook.react.uimanager.annotations.ReactProp;
|
61
|
42
|
import com.facebook.react.uimanager.events.ContentSizeChangeEvent;
|
62
|
43
|
import com.facebook.react.uimanager.events.Event;
|
63
|
44
|
import com.facebook.react.uimanager.events.EventDispatcher;
|
64
|
|
-import com.facebook.react.uimanager.events.RCTEventEmitter;
|
65
|
45
|
import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
|
66
|
46
|
import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
|
|
47
|
+import com.reactnativecommunity.webview.events.TopLoadingProgressEvent;
|
67
|
48
|
import com.reactnativecommunity.webview.events.TopLoadingStartEvent;
|
68
|
49
|
import com.reactnativecommunity.webview.events.TopMessageEvent;
|
69
|
|
-import com.reactnativecommunity.webview.events.TopLoadingProgressEvent;
|
70
|
50
|
import com.reactnativecommunity.webview.events.TopShouldStartLoadWithRequestEvent;
|
|
51
|
+
|
|
52
|
+import org.json.JSONException;
|
|
53
|
+import org.json.JSONObject;
|
|
54
|
+
|
71
|
55
|
import java.io.UnsupportedEncodingException;
|
72
|
|
-import java.util.ArrayList;
|
|
56
|
+import java.net.MalformedURLException;
|
|
57
|
+import java.net.URL;
|
|
58
|
+import java.net.URLEncoder;
|
73
|
59
|
import java.util.HashMap;
|
74
|
60
|
import java.util.Locale;
|
75
|
61
|
import java.util.Map;
|
|
62
|
+
|
76
|
63
|
import javax.annotation.Nullable;
|
77
|
|
-import org.json.JSONException;
|
78
|
|
-import org.json.JSONObject;
|
79
|
64
|
|
80
|
65
|
/**
|
81
|
66
|
* Manages instances of {@link WebView}
|
82
|
|
- *
|
|
67
|
+ * <p>
|
83
|
68
|
* Can accept following commands:
|
84
|
|
- * - GO_BACK
|
85
|
|
- * - GO_FORWARD
|
86
|
|
- * - RELOAD
|
87
|
|
- * - LOAD_URL
|
88
|
|
- *
|
|
69
|
+ * - GO_BACK
|
|
70
|
+ * - GO_FORWARD
|
|
71
|
+ * - RELOAD
|
|
72
|
+ * - LOAD_URL
|
|
73
|
+ * <p>
|
89
|
74
|
* {@link WebView} instances could emit following direct events:
|
90
|
|
- * - topLoadingFinish
|
91
|
|
- * - topLoadingStart
|
92
|
|
- * - topLoadingStart
|
93
|
|
- * - topLoadingProgress
|
94
|
|
- * - topShouldStartLoadWithRequest
|
95
|
|
- *
|
|
75
|
+ * - topLoadingFinish
|
|
76
|
+ * - topLoadingStart
|
|
77
|
+ * - topLoadingStart
|
|
78
|
+ * - topLoadingProgress
|
|
79
|
+ * - topShouldStartLoadWithRequest
|
|
80
|
+ * <p>
|
96
|
81
|
* Each event will carry the following properties:
|
97
|
|
- * - target - view's react tag
|
98
|
|
- * - url - url set for the webview
|
99
|
|
- * - loading - whether webview is in a loading state
|
100
|
|
- * - title - title of the current page
|
101
|
|
- * - canGoBack - boolean, whether there is anything on a history stack to go back
|
102
|
|
- * - canGoForward - boolean, whether it is possible to request GO_FORWARD command
|
|
82
|
+ * - target - view's react tag
|
|
83
|
+ * - url - url set for the webview
|
|
84
|
+ * - loading - whether webview is in a loading state
|
|
85
|
+ * - title - title of the current page
|
|
86
|
+ * - canGoBack - boolean, whether there is anything on a history stack to go back
|
|
87
|
+ * - canGoForward - boolean, whether it is possible to request GO_FORWARD command
|
103
|
88
|
*/
|
104
|
89
|
@ReactModule(name = RNCWebViewManager.REACT_CLASS)
|
105
|
90
|
public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
106
|
91
|
|
107
|
|
- protected static final String REACT_CLASS = "RNCWebView";
|
108
|
|
- private RNCWebViewPackage aPackage;
|
109
|
|
-
|
110
|
|
- protected static final String HTML_ENCODING = "UTF-8";
|
111
|
|
- protected static final String HTML_MIME_TYPE = "text/html";
|
112
|
|
- protected static final String JAVASCRIPT_INTERFACE = "ReactNativeWebView";
|
113
|
|
-
|
114
|
|
- protected static final String HTTP_METHOD_POST = "POST";
|
115
|
|
-
|
116
|
92
|
public static final int COMMAND_GO_BACK = 1;
|
117
|
93
|
public static final int COMMAND_GO_FORWARD = 2;
|
118
|
94
|
public static final int COMMAND_RELOAD = 3;
|
|
@@ -120,246 +96,16 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
120
|
96
|
public static final int COMMAND_POST_MESSAGE = 5;
|
121
|
97
|
public static final int COMMAND_INJECT_JAVASCRIPT = 6;
|
122
|
98
|
public static final int COMMAND_LOAD_URL = 7;
|
123
|
|
-
|
|
99
|
+ protected static final String REACT_CLASS = "RNCWebView";
|
|
100
|
+ protected static final String HTML_ENCODING = "UTF-8";
|
|
101
|
+ protected static final String HTML_MIME_TYPE = "text/html";
|
|
102
|
+ protected static final String JAVASCRIPT_INTERFACE = "ReactNativeWebView";
|
|
103
|
+ protected static final String HTTP_METHOD_POST = "POST";
|
124
|
104
|
// Use `webView.loadUrl("about:blank")` to reliably reset the view
|
125
|
105
|
// state and release page resources (including any running JavaScript).
|
126
|
106
|
protected static final String BLANK_URL = "about:blank";
|
127
|
|
-
|
128
|
107
|
protected WebViewConfig mWebViewConfig;
|
129
|
|
-
|
130
|
|
- protected static class RNCWebViewClient extends WebViewClient {
|
131
|
|
-
|
132
|
|
- protected boolean mLastLoadFailed = false;
|
133
|
|
- protected @Nullable ReadableArray mUrlPrefixesForDefaultIntent;
|
134
|
|
-
|
135
|
|
- @Override
|
136
|
|
- public void onPageFinished(WebView webView, String url) {
|
137
|
|
- super.onPageFinished(webView, url);
|
138
|
|
-
|
139
|
|
- if (!mLastLoadFailed) {
|
140
|
|
- RNCWebView reactWebView = (RNCWebView) webView;
|
141
|
|
-
|
142
|
|
- reactWebView.callInjectedJavaScript();
|
143
|
|
-
|
144
|
|
- emitFinishEvent(webView, url);
|
145
|
|
- }
|
146
|
|
- }
|
147
|
|
-
|
148
|
|
- @Override
|
149
|
|
- public void onPageStarted(WebView webView, String url, Bitmap favicon) {
|
150
|
|
- super.onPageStarted(webView, url, favicon);
|
151
|
|
- mLastLoadFailed = false;
|
152
|
|
-
|
153
|
|
- dispatchEvent(
|
154
|
|
- webView,
|
155
|
|
- new TopLoadingStartEvent(
|
156
|
|
- webView.getId(),
|
157
|
|
- createWebViewEvent(webView, url)));
|
158
|
|
- }
|
159
|
|
-
|
160
|
|
- @Override
|
161
|
|
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
162
|
|
- dispatchEvent(
|
163
|
|
- view,
|
164
|
|
- new TopShouldStartLoadWithRequestEvent(
|
165
|
|
- view.getId(),
|
166
|
|
- createWebViewEvent(view, url)));
|
167
|
|
- return true;
|
168
|
|
- }
|
169
|
|
-
|
170
|
|
-
|
171
|
|
- @TargetApi(Build.VERSION_CODES.N)
|
172
|
|
- @Override
|
173
|
|
- public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
174
|
|
- final String url = request.getUrl().toString();
|
175
|
|
- return this.shouldOverrideUrlLoading(view, url);
|
176
|
|
- }
|
177
|
|
-
|
178
|
|
- @Override
|
179
|
|
- public void onReceivedError(
|
180
|
|
- WebView webView,
|
181
|
|
- int errorCode,
|
182
|
|
- String description,
|
183
|
|
- String failingUrl) {
|
184
|
|
- super.onReceivedError(webView, errorCode, description, failingUrl);
|
185
|
|
- mLastLoadFailed = true;
|
186
|
|
-
|
187
|
|
- // In case of an error JS side expect to get a finish event first, and then get an error event
|
188
|
|
- // Android WebView does it in the opposite way, so we need to simulate that behavior
|
189
|
|
- emitFinishEvent(webView, failingUrl);
|
190
|
|
-
|
191
|
|
- WritableMap eventData = createWebViewEvent(webView, failingUrl);
|
192
|
|
- eventData.putDouble("code", errorCode);
|
193
|
|
- eventData.putString("description", description);
|
194
|
|
-
|
195
|
|
- dispatchEvent(
|
196
|
|
- webView,
|
197
|
|
- new TopLoadingErrorEvent(webView.getId(), eventData));
|
198
|
|
- }
|
199
|
|
-
|
200
|
|
- protected void emitFinishEvent(WebView webView, String url) {
|
201
|
|
- dispatchEvent(
|
202
|
|
- webView,
|
203
|
|
- new TopLoadingFinishEvent(
|
204
|
|
- webView.getId(),
|
205
|
|
- createWebViewEvent(webView, url)));
|
206
|
|
- }
|
207
|
|
-
|
208
|
|
- protected WritableMap createWebViewEvent(WebView webView, String url) {
|
209
|
|
- WritableMap event = Arguments.createMap();
|
210
|
|
- event.putDouble("target", webView.getId());
|
211
|
|
- // Don't use webView.getUrl() here, the URL isn't updated to the new value yet in callbacks
|
212
|
|
- // like onPageFinished
|
213
|
|
- event.putString("url", url);
|
214
|
|
- event.putBoolean("loading", !mLastLoadFailed && webView.getProgress() != 100);
|
215
|
|
- event.putString("title", webView.getTitle());
|
216
|
|
- event.putBoolean("canGoBack", webView.canGoBack());
|
217
|
|
- event.putBoolean("canGoForward", webView.canGoForward());
|
218
|
|
- return event;
|
219
|
|
- }
|
220
|
|
-
|
221
|
|
- public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) {
|
222
|
|
- mUrlPrefixesForDefaultIntent = specialUrls;
|
223
|
|
- }
|
224
|
|
- }
|
225
|
|
-
|
226
|
|
- /**
|
227
|
|
- * Subclass of {@link WebView} that implements {@link LifecycleEventListener} interface in order
|
228
|
|
- * to call {@link WebView#destroy} on activity destroy event and also to clear the client
|
229
|
|
- */
|
230
|
|
- protected static class RNCWebView extends WebView implements LifecycleEventListener {
|
231
|
|
- protected @Nullable String injectedJS;
|
232
|
|
- protected boolean messagingEnabled = false;
|
233
|
|
- protected @Nullable RNCWebViewClient mRNCWebViewClient;
|
234
|
|
- protected boolean sendContentSizeChangeEvents = false;
|
235
|
|
- public void setSendContentSizeChangeEvents(boolean sendContentSizeChangeEvents) {
|
236
|
|
- this.sendContentSizeChangeEvents = sendContentSizeChangeEvents;
|
237
|
|
- }
|
238
|
|
-
|
239
|
|
-
|
240
|
|
- protected class RNCWebViewBridge {
|
241
|
|
- RNCWebView mContext;
|
242
|
|
-
|
243
|
|
- RNCWebViewBridge(RNCWebView c) {
|
244
|
|
- mContext = c;
|
245
|
|
- }
|
246
|
|
-
|
247
|
|
- /**
|
248
|
|
- * This method is called whenever JavaScript running within the web view calls:
|
249
|
|
- * - window[JAVASCRIPT_INTERFACE].postMessage
|
250
|
|
- */
|
251
|
|
- @JavascriptInterface
|
252
|
|
- public void postMessage(String message) {
|
253
|
|
- mContext.onMessage(message);
|
254
|
|
- }
|
255
|
|
- }
|
256
|
|
-
|
257
|
|
- /**
|
258
|
|
- * WebView must be created with an context of the current activity
|
259
|
|
- *
|
260
|
|
- * Activity Context is required for creation of dialogs internally by WebView
|
261
|
|
- * Reactive Native needed for access to ReactNative internal system functionality
|
262
|
|
- *
|
263
|
|
- */
|
264
|
|
- public RNCWebView(ThemedReactContext reactContext) {
|
265
|
|
- super(reactContext);
|
266
|
|
- }
|
267
|
|
-
|
268
|
|
- @Override
|
269
|
|
- public void onHostResume() {
|
270
|
|
- // do nothing
|
271
|
|
- }
|
272
|
|
-
|
273
|
|
- @Override
|
274
|
|
- public void onHostPause() {
|
275
|
|
- // do nothing
|
276
|
|
- }
|
277
|
|
-
|
278
|
|
- @Override
|
279
|
|
- public void onHostDestroy() {
|
280
|
|
- cleanupCallbacksAndDestroy();
|
281
|
|
- }
|
282
|
|
-
|
283
|
|
- @Override
|
284
|
|
- protected void onSizeChanged(int w, int h, int ow, int oh) {
|
285
|
|
- super.onSizeChanged(w, h, ow, oh);
|
286
|
|
-
|
287
|
|
- if (sendContentSizeChangeEvents) {
|
288
|
|
- dispatchEvent(
|
289
|
|
- this,
|
290
|
|
- new ContentSizeChangeEvent(
|
291
|
|
- this.getId(),
|
292
|
|
- w,
|
293
|
|
- h
|
294
|
|
- )
|
295
|
|
- );
|
296
|
|
- }
|
297
|
|
- }
|
298
|
|
-
|
299
|
|
- @Override
|
300
|
|
- public void setWebViewClient(WebViewClient client) {
|
301
|
|
- super.setWebViewClient(client);
|
302
|
|
- mRNCWebViewClient = (RNCWebViewClient)client;
|
303
|
|
- }
|
304
|
|
-
|
305
|
|
- public @Nullable RNCWebViewClient getRNCWebViewClient() {
|
306
|
|
- return mRNCWebViewClient;
|
307
|
|
- }
|
308
|
|
-
|
309
|
|
- public void setInjectedJavaScript(@Nullable String js) {
|
310
|
|
- injectedJS = js;
|
311
|
|
- }
|
312
|
|
-
|
313
|
|
- protected RNCWebViewBridge createRNCWebViewBridge(RNCWebView webView) {
|
314
|
|
- return new RNCWebViewBridge(webView);
|
315
|
|
- }
|
316
|
|
-
|
317
|
|
- @SuppressLint("AddJavascriptInterface")
|
318
|
|
- public void setMessagingEnabled(boolean enabled) {
|
319
|
|
- if (messagingEnabled == enabled) {
|
320
|
|
- return;
|
321
|
|
- }
|
322
|
|
-
|
323
|
|
- messagingEnabled = enabled;
|
324
|
|
-
|
325
|
|
- if (enabled) {
|
326
|
|
- addJavascriptInterface(createRNCWebViewBridge(this), JAVASCRIPT_INTERFACE);
|
327
|
|
- } else {
|
328
|
|
- removeJavascriptInterface(JAVASCRIPT_INTERFACE);
|
329
|
|
- }
|
330
|
|
- }
|
331
|
|
-
|
332
|
|
- protected void evaluateJavascriptWithFallback(String script) {
|
333
|
|
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
334
|
|
- evaluateJavascript(script, null);
|
335
|
|
- return;
|
336
|
|
- }
|
337
|
|
-
|
338
|
|
- try {
|
339
|
|
- loadUrl("javascript:" + URLEncoder.encode(script, "UTF-8"));
|
340
|
|
- } catch (UnsupportedEncodingException e) {
|
341
|
|
- // UTF-8 should always be supported
|
342
|
|
- throw new RuntimeException(e);
|
343
|
|
- }
|
344
|
|
- }
|
345
|
|
-
|
346
|
|
- public void callInjectedJavaScript() {
|
347
|
|
- if (getSettings().getJavaScriptEnabled() &&
|
348
|
|
- injectedJS != null &&
|
349
|
|
- !TextUtils.isEmpty(injectedJS)) {
|
350
|
|
- evaluateJavascriptWithFallback("(function() {\n" + injectedJS + ";\n})();");
|
351
|
|
- }
|
352
|
|
- }
|
353
|
|
-
|
354
|
|
- public void onMessage(String message) {
|
355
|
|
- dispatchEvent(this, new TopMessageEvent(this.getId(), message));
|
356
|
|
- }
|
357
|
|
-
|
358
|
|
- protected void cleanupCallbacksAndDestroy() {
|
359
|
|
- setWebViewClient(null);
|
360
|
|
- destroy();
|
361
|
|
- }
|
362
|
|
- }
|
|
108
|
+ private RNCWebViewPackage aPackage;
|
363
|
109
|
|
364
|
110
|
public RNCWebViewManager() {
|
365
|
111
|
mWebViewConfig = new WebViewConfig() {
|
|
@@ -372,6 +118,13 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
372
|
118
|
mWebViewConfig = webViewConfig;
|
373
|
119
|
}
|
374
|
120
|
|
|
121
|
+ protected static void dispatchEvent(WebView webView, Event event) {
|
|
122
|
+ ReactContext reactContext = (ReactContext) webView.getContext();
|
|
123
|
+ EventDispatcher eventDispatcher =
|
|
124
|
+ reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
|
|
125
|
+ eventDispatcher.dispatchEvent(event);
|
|
126
|
+ }
|
|
127
|
+
|
375
|
128
|
@Override
|
376
|
129
|
public String getName() {
|
377
|
130
|
return REACT_CLASS;
|
|
@@ -396,21 +149,21 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
396
|
149
|
}
|
397
|
150
|
|
398
|
151
|
|
399
|
|
- @Override
|
400
|
|
- public void onProgressChanged(WebView webView, int newProgress) {
|
|
152
|
+ @Override
|
|
153
|
+ public void onProgressChanged(WebView webView, int newProgress) {
|
401
|
154
|
super.onProgressChanged(webView, newProgress);
|
402
|
155
|
WritableMap event = Arguments.createMap();
|
403
|
156
|
event.putDouble("target", webView.getId());
|
404
|
157
|
event.putString("title", webView.getTitle());
|
405
|
158
|
event.putBoolean("canGoBack", webView.canGoBack());
|
406
|
159
|
event.putBoolean("canGoForward", webView.canGoForward());
|
407
|
|
- event.putDouble("progress", (float)newProgress/100);
|
|
160
|
+ event.putDouble("progress", (float) newProgress / 100);
|
408
|
161
|
dispatchEvent(
|
409
|
|
- webView,
|
410
|
|
- new TopLoadingProgressEvent(
|
411
|
|
- webView.getId(),
|
412
|
|
- event));
|
413
|
|
- }
|
|
162
|
+ webView,
|
|
163
|
+ new TopLoadingProgressEvent(
|
|
164
|
+ webView.getId(),
|
|
165
|
+ event));
|
|
166
|
+ }
|
414
|
167
|
|
415
|
168
|
@Override
|
416
|
169
|
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
|
|
@@ -420,9 +173,11 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
420
|
173
|
protected void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType) {
|
421
|
174
|
getModule().startPhotoPickerIntent(filePathCallback, acceptType);
|
422
|
175
|
}
|
|
176
|
+
|
423
|
177
|
protected void openFileChooser(ValueCallback<Uri> filePathCallback) {
|
424
|
178
|
getModule().startPhotoPickerIntent(filePathCallback, "");
|
425
|
179
|
}
|
|
180
|
+
|
426
|
181
|
protected void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType, String capture) {
|
427
|
182
|
getModule().startPhotoPickerIntent(filePathCallback, acceptType);
|
428
|
183
|
}
|
|
@@ -453,8 +208,8 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
453
|
208
|
|
454
|
209
|
// Fixes broken full-screen modals/galleries due to body height being 0.
|
455
|
210
|
webView.setLayoutParams(
|
456
|
|
- new LayoutParams(LayoutParams.MATCH_PARENT,
|
457
|
|
- LayoutParams.MATCH_PARENT));
|
|
211
|
+ new LayoutParams(LayoutParams.MATCH_PARENT,
|
|
212
|
+ LayoutParams.MATCH_PARENT));
|
458
|
213
|
|
459
|
214
|
setGeolocationEnabled(webView, false);
|
460
|
215
|
if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
@@ -516,7 +271,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
516
|
271
|
public void setShowsVerticalScrollIndicator(WebView view, boolean enabled) {
|
517
|
272
|
view.setVerticalScrollBarEnabled(enabled);
|
518
|
273
|
}
|
519
|
|
-
|
|
274
|
+
|
520
|
275
|
@ReactProp(name = "cacheEnabled")
|
521
|
276
|
public void setCacheEnabled(WebView view, boolean enabled) {
|
522
|
277
|
if (enabled) {
|
|
@@ -616,7 +371,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
616
|
371
|
String html = source.getString("html");
|
617
|
372
|
if (source.hasKey("baseUrl")) {
|
618
|
373
|
view.loadDataWithBaseURL(
|
619
|
|
- source.getString("baseUrl"), html, HTML_MIME_TYPE, HTML_ENCODING, null);
|
|
374
|
+ source.getString("baseUrl"), html, HTML_MIME_TYPE, HTML_ENCODING, null);
|
620
|
375
|
} else {
|
621
|
376
|
view.loadData(html, HTML_MIME_TYPE + "; charset=" + HTML_ENCODING, null);
|
622
|
377
|
}
|
|
@@ -689,8 +444,8 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
689
|
444
|
|
690
|
445
|
@ReactProp(name = "urlPrefixesForDefaultIntent")
|
691
|
446
|
public void setUrlPrefixesForDefaultIntent(
|
692
|
|
- WebView view,
|
693
|
|
- @Nullable ReadableArray urlPrefixesForDefaultIntent) {
|
|
447
|
+ WebView view,
|
|
448
|
+ @Nullable ReadableArray urlPrefixesForDefaultIntent) {
|
694
|
449
|
RNCWebViewClient client = ((RNCWebView) view).getRNCWebViewClient();
|
695
|
450
|
if (client != null && urlPrefixesForDefaultIntent != null) {
|
696
|
451
|
client.setUrlPrefixesForDefaultIntent(urlPrefixesForDefaultIntent);
|
|
@@ -729,16 +484,17 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
729
|
484
|
}
|
730
|
485
|
|
731
|
486
|
@Override
|
732
|
|
- public @Nullable Map<String, Integer> getCommandsMap() {
|
|
487
|
+ public @Nullable
|
|
488
|
+ Map<String, Integer> getCommandsMap() {
|
733
|
489
|
return MapBuilder.of(
|
734
|
|
- "goBack", COMMAND_GO_BACK,
|
735
|
|
- "goForward", COMMAND_GO_FORWARD,
|
736
|
|
- "reload", COMMAND_RELOAD,
|
737
|
|
- "stopLoading", COMMAND_STOP_LOADING,
|
738
|
|
- "postMessage", COMMAND_POST_MESSAGE,
|
739
|
|
- "injectJavaScript", COMMAND_INJECT_JAVASCRIPT,
|
740
|
|
- "loadUrl", COMMAND_LOAD_URL
|
741
|
|
- );
|
|
490
|
+ "goBack", COMMAND_GO_BACK,
|
|
491
|
+ "goForward", COMMAND_GO_FORWARD,
|
|
492
|
+ "reload", COMMAND_RELOAD,
|
|
493
|
+ "stopLoading", COMMAND_STOP_LOADING,
|
|
494
|
+ "postMessage", COMMAND_POST_MESSAGE,
|
|
495
|
+ "injectJavaScript", COMMAND_INJECT_JAVASCRIPT,
|
|
496
|
+ "loadUrl", COMMAND_LOAD_URL
|
|
497
|
+ );
|
742
|
498
|
}
|
743
|
499
|
|
744
|
500
|
@Override
|
|
@@ -765,13 +521,13 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
765
|
521
|
"var event;" +
|
766
|
522
|
"var data = " + eventInitDict.toString() + ";" +
|
767
|
523
|
"try {" +
|
768
|
|
- "event = new MessageEvent('message', data);" +
|
|
524
|
+ "event = new MessageEvent('message', data);" +
|
769
|
525
|
"} catch (e) {" +
|
770
|
|
- "event = document.createEvent('MessageEvent');" +
|
771
|
|
- "event.initMessageEvent('message', true, true, data.data, data.origin, data.lastEventId, data.source);" +
|
|
526
|
+ "event = document.createEvent('MessageEvent');" +
|
|
527
|
+ "event.initMessageEvent('message', true, true, data.data, data.origin, data.lastEventId, data.source);" +
|
772
|
528
|
"}" +
|
773
|
529
|
"document.dispatchEvent(event);" +
|
774
|
|
- "})();");
|
|
530
|
+ "})();");
|
775
|
531
|
} catch (JSONException e) {
|
776
|
532
|
throw new RuntimeException(e);
|
777
|
533
|
}
|
|
@@ -796,13 +552,6 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
796
|
552
|
((RNCWebView) webView).cleanupCallbacksAndDestroy();
|
797
|
553
|
}
|
798
|
554
|
|
799
|
|
- protected static void dispatchEvent(WebView webView, Event event) {
|
800
|
|
- ReactContext reactContext = (ReactContext) webView.getContext();
|
801
|
|
- EventDispatcher eventDispatcher =
|
802
|
|
- reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
|
803
|
|
- eventDispatcher.dispatchEvent(event);
|
804
|
|
- }
|
805
|
|
-
|
806
|
555
|
public RNCWebViewPackage getPackage() {
|
807
|
556
|
return this.aPackage;
|
808
|
557
|
}
|
|
@@ -814,4 +563,241 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
814
|
563
|
public RNCWebViewModule getModule() {
|
815
|
564
|
return this.aPackage.getModule();
|
816
|
565
|
}
|
|
566
|
+
|
|
567
|
+ protected static class RNCWebViewClient extends WebViewClient {
|
|
568
|
+
|
|
569
|
+ protected boolean mLastLoadFailed = false;
|
|
570
|
+ protected @Nullable
|
|
571
|
+ ReadableArray mUrlPrefixesForDefaultIntent;
|
|
572
|
+
|
|
573
|
+ @Override
|
|
574
|
+ public void onPageFinished(WebView webView, String url) {
|
|
575
|
+ super.onPageFinished(webView, url);
|
|
576
|
+
|
|
577
|
+ if (!mLastLoadFailed) {
|
|
578
|
+ RNCWebView reactWebView = (RNCWebView) webView;
|
|
579
|
+
|
|
580
|
+ reactWebView.callInjectedJavaScript();
|
|
581
|
+
|
|
582
|
+ emitFinishEvent(webView, url);
|
|
583
|
+ }
|
|
584
|
+ }
|
|
585
|
+
|
|
586
|
+ @Override
|
|
587
|
+ public void onPageStarted(WebView webView, String url, Bitmap favicon) {
|
|
588
|
+ super.onPageStarted(webView, url, favicon);
|
|
589
|
+ mLastLoadFailed = false;
|
|
590
|
+
|
|
591
|
+ dispatchEvent(
|
|
592
|
+ webView,
|
|
593
|
+ new TopLoadingStartEvent(
|
|
594
|
+ webView.getId(),
|
|
595
|
+ createWebViewEvent(webView, url)));
|
|
596
|
+ }
|
|
597
|
+
|
|
598
|
+ @Override
|
|
599
|
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
|
600
|
+ dispatchEvent(
|
|
601
|
+ view,
|
|
602
|
+ new TopShouldStartLoadWithRequestEvent(
|
|
603
|
+ view.getId(),
|
|
604
|
+ createWebViewEvent(view, url)));
|
|
605
|
+ return true;
|
|
606
|
+ }
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+ @TargetApi(Build.VERSION_CODES.N)
|
|
610
|
+ @Override
|
|
611
|
+ public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
|
612
|
+ final String url = request.getUrl().toString();
|
|
613
|
+ return this.shouldOverrideUrlLoading(view, url);
|
|
614
|
+ }
|
|
615
|
+
|
|
616
|
+ @Override
|
|
617
|
+ public void onReceivedError(
|
|
618
|
+ WebView webView,
|
|
619
|
+ int errorCode,
|
|
620
|
+ String description,
|
|
621
|
+ String failingUrl) {
|
|
622
|
+ super.onReceivedError(webView, errorCode, description, failingUrl);
|
|
623
|
+ mLastLoadFailed = true;
|
|
624
|
+
|
|
625
|
+ // In case of an error JS side expect to get a finish event first, and then get an error event
|
|
626
|
+ // Android WebView does it in the opposite way, so we need to simulate that behavior
|
|
627
|
+ emitFinishEvent(webView, failingUrl);
|
|
628
|
+
|
|
629
|
+ WritableMap eventData = createWebViewEvent(webView, failingUrl);
|
|
630
|
+ eventData.putDouble("code", errorCode);
|
|
631
|
+ eventData.putString("description", description);
|
|
632
|
+
|
|
633
|
+ dispatchEvent(
|
|
634
|
+ webView,
|
|
635
|
+ new TopLoadingErrorEvent(webView.getId(), eventData));
|
|
636
|
+ }
|
|
637
|
+
|
|
638
|
+ protected void emitFinishEvent(WebView webView, String url) {
|
|
639
|
+ dispatchEvent(
|
|
640
|
+ webView,
|
|
641
|
+ new TopLoadingFinishEvent(
|
|
642
|
+ webView.getId(),
|
|
643
|
+ createWebViewEvent(webView, url)));
|
|
644
|
+ }
|
|
645
|
+
|
|
646
|
+ protected WritableMap createWebViewEvent(WebView webView, String url) {
|
|
647
|
+ WritableMap event = Arguments.createMap();
|
|
648
|
+ event.putDouble("target", webView.getId());
|
|
649
|
+ // Don't use webView.getUrl() here, the URL isn't updated to the new value yet in callbacks
|
|
650
|
+ // like onPageFinished
|
|
651
|
+ event.putString("url", url);
|
|
652
|
+ event.putBoolean("loading", !mLastLoadFailed && webView.getProgress() != 100);
|
|
653
|
+ event.putString("title", webView.getTitle());
|
|
654
|
+ event.putBoolean("canGoBack", webView.canGoBack());
|
|
655
|
+ event.putBoolean("canGoForward", webView.canGoForward());
|
|
656
|
+ return event;
|
|
657
|
+ }
|
|
658
|
+
|
|
659
|
+ public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) {
|
|
660
|
+ mUrlPrefixesForDefaultIntent = specialUrls;
|
|
661
|
+ }
|
|
662
|
+ }
|
|
663
|
+
|
|
664
|
+ /**
|
|
665
|
+ * Subclass of {@link WebView} that implements {@link LifecycleEventListener} interface in order
|
|
666
|
+ * to call {@link WebView#destroy} on activity destroy event and also to clear the client
|
|
667
|
+ */
|
|
668
|
+ protected static class RNCWebView extends WebView implements LifecycleEventListener {
|
|
669
|
+ protected @Nullable
|
|
670
|
+ String injectedJS;
|
|
671
|
+ protected boolean messagingEnabled = false;
|
|
672
|
+ protected @Nullable
|
|
673
|
+ RNCWebViewClient mRNCWebViewClient;
|
|
674
|
+ protected boolean sendContentSizeChangeEvents = false;
|
|
675
|
+
|
|
676
|
+ /**
|
|
677
|
+ * WebView must be created with an context of the current activity
|
|
678
|
+ * <p>
|
|
679
|
+ * Activity Context is required for creation of dialogs internally by WebView
|
|
680
|
+ * Reactive Native needed for access to ReactNative internal system functionality
|
|
681
|
+ */
|
|
682
|
+ public RNCWebView(ThemedReactContext reactContext) {
|
|
683
|
+ super(reactContext);
|
|
684
|
+ }
|
|
685
|
+
|
|
686
|
+ public void setSendContentSizeChangeEvents(boolean sendContentSizeChangeEvents) {
|
|
687
|
+ this.sendContentSizeChangeEvents = sendContentSizeChangeEvents;
|
|
688
|
+ }
|
|
689
|
+
|
|
690
|
+ @Override
|
|
691
|
+ public void onHostResume() {
|
|
692
|
+ // do nothing
|
|
693
|
+ }
|
|
694
|
+
|
|
695
|
+ @Override
|
|
696
|
+ public void onHostPause() {
|
|
697
|
+ // do nothing
|
|
698
|
+ }
|
|
699
|
+
|
|
700
|
+ @Override
|
|
701
|
+ public void onHostDestroy() {
|
|
702
|
+ cleanupCallbacksAndDestroy();
|
|
703
|
+ }
|
|
704
|
+
|
|
705
|
+ @Override
|
|
706
|
+ protected void onSizeChanged(int w, int h, int ow, int oh) {
|
|
707
|
+ super.onSizeChanged(w, h, ow, oh);
|
|
708
|
+
|
|
709
|
+ if (sendContentSizeChangeEvents) {
|
|
710
|
+ dispatchEvent(
|
|
711
|
+ this,
|
|
712
|
+ new ContentSizeChangeEvent(
|
|
713
|
+ this.getId(),
|
|
714
|
+ w,
|
|
715
|
+ h
|
|
716
|
+ )
|
|
717
|
+ );
|
|
718
|
+ }
|
|
719
|
+ }
|
|
720
|
+
|
|
721
|
+ @Override
|
|
722
|
+ public void setWebViewClient(WebViewClient client) {
|
|
723
|
+ super.setWebViewClient(client);
|
|
724
|
+ mRNCWebViewClient = (RNCWebViewClient) client;
|
|
725
|
+ }
|
|
726
|
+
|
|
727
|
+ public @Nullable
|
|
728
|
+ RNCWebViewClient getRNCWebViewClient() {
|
|
729
|
+ return mRNCWebViewClient;
|
|
730
|
+ }
|
|
731
|
+
|
|
732
|
+ public void setInjectedJavaScript(@Nullable String js) {
|
|
733
|
+ injectedJS = js;
|
|
734
|
+ }
|
|
735
|
+
|
|
736
|
+ protected RNCWebViewBridge createRNCWebViewBridge(RNCWebView webView) {
|
|
737
|
+ return new RNCWebViewBridge(webView);
|
|
738
|
+ }
|
|
739
|
+
|
|
740
|
+ @SuppressLint("AddJavascriptInterface")
|
|
741
|
+ public void setMessagingEnabled(boolean enabled) {
|
|
742
|
+ if (messagingEnabled == enabled) {
|
|
743
|
+ return;
|
|
744
|
+ }
|
|
745
|
+
|
|
746
|
+ messagingEnabled = enabled;
|
|
747
|
+
|
|
748
|
+ if (enabled) {
|
|
749
|
+ addJavascriptInterface(createRNCWebViewBridge(this), JAVASCRIPT_INTERFACE);
|
|
750
|
+ } else {
|
|
751
|
+ removeJavascriptInterface(JAVASCRIPT_INTERFACE);
|
|
752
|
+ }
|
|
753
|
+ }
|
|
754
|
+
|
|
755
|
+ protected void evaluateJavascriptWithFallback(String script) {
|
|
756
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
757
|
+ evaluateJavascript(script, null);
|
|
758
|
+ return;
|
|
759
|
+ }
|
|
760
|
+
|
|
761
|
+ try {
|
|
762
|
+ loadUrl("javascript:" + URLEncoder.encode(script, "UTF-8"));
|
|
763
|
+ } catch (UnsupportedEncodingException e) {
|
|
764
|
+ // UTF-8 should always be supported
|
|
765
|
+ throw new RuntimeException(e);
|
|
766
|
+ }
|
|
767
|
+ }
|
|
768
|
+
|
|
769
|
+ public void callInjectedJavaScript() {
|
|
770
|
+ if (getSettings().getJavaScriptEnabled() &&
|
|
771
|
+ injectedJS != null &&
|
|
772
|
+ !TextUtils.isEmpty(injectedJS)) {
|
|
773
|
+ evaluateJavascriptWithFallback("(function() {\n" + injectedJS + ";\n})();");
|
|
774
|
+ }
|
|
775
|
+ }
|
|
776
|
+
|
|
777
|
+ public void onMessage(String message) {
|
|
778
|
+ dispatchEvent(this, new TopMessageEvent(this.getId(), message));
|
|
779
|
+ }
|
|
780
|
+
|
|
781
|
+ protected void cleanupCallbacksAndDestroy() {
|
|
782
|
+ setWebViewClient(null);
|
|
783
|
+ destroy();
|
|
784
|
+ }
|
|
785
|
+
|
|
786
|
+ protected class RNCWebViewBridge {
|
|
787
|
+ RNCWebView mContext;
|
|
788
|
+
|
|
789
|
+ RNCWebViewBridge(RNCWebView c) {
|
|
790
|
+ mContext = c;
|
|
791
|
+ }
|
|
792
|
+
|
|
793
|
+ /**
|
|
794
|
+ * This method is called whenever JavaScript running within the web view calls:
|
|
795
|
+ * - window[JAVASCRIPT_INTERFACE].postMessage
|
|
796
|
+ */
|
|
797
|
+ @JavascriptInterface
|
|
798
|
+ public void postMessage(String message) {
|
|
799
|
+ mContext.onMessage(message);
|
|
800
|
+ }
|
|
801
|
+ }
|
|
802
|
+ }
|
817
|
803
|
}
|