Browse Source

chore(format): Android project formatting (#433)

Added an extremely simple `.editorconfig` I inferred from the main part of the project, then reformatted the codebase according to it. 🙂
Stanisław Chmiela 5 years ago
parent
commit
e697dff1d0

+ 6
- 0
android/.editorconfig View File

@@ -0,0 +1,6 @@
1
+[*]
2
+charset=utf-8
3
+end_of_line=lf
4
+insert_final_newline=false
5
+indent_style=space
6
+indent_size=2

+ 14
- 12
android/src/main/AndroidManifest.xml View File

@@ -1,13 +1,15 @@
1
-<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.reactnativecommunity.webview">
2
-    <application>
3
-        <provider
4
-        android:name=".RNCWebViewFileProvider"
5
-        android:authorities="${applicationId}.fileprovider"
6
-        android:exported="false"
7
-        android:grantUriPermissions="true">
8
-            <meta-data
9
-                android:name="android.support.FILE_PROVIDER_PATHS"
10
-                android:resource="@xml/file_provider_paths" />
11
-        </provider>
12
-    </application>
1
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+  package="com.reactnativecommunity.webview">
3
+
4
+  <application>
5
+    <provider
6
+      android:name=".RNCWebViewFileProvider"
7
+      android:authorities="${applicationId}.fileprovider"
8
+      android:exported="false"
9
+      android:grantUriPermissions="true">
10
+      <meta-data
11
+        android:name="android.support.FILE_PROVIDER_PATHS"
12
+        android:resource="@xml/file_provider_paths" />
13
+    </provider>
14
+  </application>
13 15
 </manifest>

+ 1
- 1
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewFileProvider.java View File

@@ -4,7 +4,7 @@ import android.support.v4.content.FileProvider;
4 4
 
5 5
 /**
6 6
  * Providing a custom {@code FileProvider} prevents manifest {@code <provider>} name collisions.
7
- *
7
+ * <p>
8 8
  * See https://developer.android.com/guide/topics/manifest/provider-element.html for details.
9 9
  */
10 10
 public class RNCWebViewFileProvider extends FileProvider {

+ 308
- 322
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java View File

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

+ 106
- 110
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewModule.java View File

@@ -1,4 +1,3 @@
1
-
2 1
 package com.reactnativecommunity.webview;
3 2
 
4 3
 import android.Manifest;
@@ -37,20 +36,35 @@ import static android.app.Activity.RESULT_OK;
37 36
 
38 37
 public class RNCWebViewModule extends ReactContextBaseJavaModule implements ActivityEventListener {
39 38
 
40
-  private final ReactApplicationContext reactContext;
41
-  private RNCWebViewPackage aPackage;
42
-
43 39
   private static final int PICKER = 1;
44 40
   private static final int PICKER_LEGACY = 3;
45
-
41
+  private static final int FILE_DOWNLOAD_PERMISSION_REQUEST = 1;
42
+  final String DEFAULT_MIME_TYPES = "*/*";
43
+  private final ReactApplicationContext reactContext;
44
+  private RNCWebViewPackage aPackage;
46 45
   private ValueCallback<Uri> filePathCallbackLegacy;
47 46
   private ValueCallback<Uri[]> filePathCallback;
48 47
   private Uri outputFileUri;
49
-
50 48
   private DownloadManager.Request downloadRequest;
51
-  private static final int FILE_DOWNLOAD_PERMISSION_REQUEST = 1;
52
-
53
-  final String DEFAULT_MIME_TYPES = "*/*";
49
+  private PermissionListener webviewFileDownloaderPermissionListener = new PermissionListener() {
50
+    @Override
51
+    public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
52
+      switch (requestCode) {
53
+        case FILE_DOWNLOAD_PERMISSION_REQUEST: {
54
+          // If request is cancelled, the result arrays are empty.
55
+          if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
56
+            if (downloadRequest != null) {
57
+              downloadFile();
58
+            }
59
+          } else {
60
+            Toast.makeText(getCurrentActivity().getApplicationContext(), "Cannot download files as permission was denied. Please provide permission to write to storage, in order to download files.", Toast.LENGTH_LONG).show();
61
+          }
62
+          return true;
63
+        }
64
+      }
65
+      return false;
66
+    }
67
+  };
54 68
 
55 69
   public RNCWebViewModule(ReactApplicationContext reactContext) {
56 70
     super(reactContext);
@@ -65,49 +79,49 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
65 79
 
66 80
   @ReactMethod
67 81
   public void isFileUploadSupported(final Promise promise) {
68
-      Boolean result = false;
69
-      int current = Build.VERSION.SDK_INT;
70
-      if (current >= Build.VERSION_CODES.LOLLIPOP) {
71
-          result = true;
72
-      }
73
-      if (current >= Build.VERSION_CODES.JELLY_BEAN && current <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
74
-          result = true;
75
-      }
76
-      promise.resolve(result);
82
+    Boolean result = false;
83
+    int current = Build.VERSION.SDK_INT;
84
+    if (current >= Build.VERSION_CODES.LOLLIPOP) {
85
+      result = true;
86
+    }
87
+    if (current >= Build.VERSION_CODES.JELLY_BEAN && current <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
88
+      result = true;
89
+    }
90
+    promise.resolve(result);
77 91
   }
78 92
 
79 93
   public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
80 94
 
81 95
     if (filePathCallback == null && filePathCallbackLegacy == null) {
82
-        return;
96
+      return;
83 97
     }
84 98
 
85 99
     // based off of which button was pressed, we get an activity result and a file
86 100
     // the camera activity doesn't properly return the filename* (I think?) so we use
87 101
     // this filename instead
88 102
     switch (requestCode) {
89
-    case PICKER:
103
+      case PICKER:
90 104
         if (resultCode != RESULT_OK) {
91
-            if (filePathCallback != null) {
92
-                filePathCallback.onReceiveValue(null);
93
-            }
105
+          if (filePathCallback != null) {
106
+            filePathCallback.onReceiveValue(null);
107
+          }
94 108
         } else {
95
-            Uri result[] = this.getSelectedFiles(data, resultCode);
96
-            if (result != null) {
97
-                filePathCallback.onReceiveValue(result);
98
-            } else {
99
-                filePathCallback.onReceiveValue(new Uri[] { outputFileUri });
100
-            }
109
+          Uri result[] = this.getSelectedFiles(data, resultCode);
110
+          if (result != null) {
111
+            filePathCallback.onReceiveValue(result);
112
+          } else {
113
+            filePathCallback.onReceiveValue(new Uri[]{outputFileUri});
114
+          }
101 115
         }
102 116
         break;
103
-    case PICKER_LEGACY:
117
+      case PICKER_LEGACY:
104 118
         Uri result = resultCode != Activity.RESULT_OK ? null : data == null ? outputFileUri : data.getData();
105 119
         filePathCallbackLegacy.onReceiveValue(result);
106 120
         break;
107 121
 
108 122
     }
109 123
     filePathCallback = null;
110
-    filePathCallbackLegacy= null;
124
+    filePathCallbackLegacy = null;
111 125
     outputFileUri = null;
112 126
   }
113 127
 
@@ -116,50 +130,50 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
116 130
 
117 131
   private Uri[] getSelectedFiles(Intent data, int resultCode) {
118 132
     if (data == null) {
119
-        return null;
133
+      return null;
120 134
     }
121 135
 
122 136
     // we have one file selected
123 137
     if (data.getData() != null) {
124
-        if (resultCode == RESULT_OK && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
125
-            return WebChromeClient.FileChooserParams.parseResult(resultCode, data);
126
-        } else {
127
-            return null;
128
-        }
138
+      if (resultCode == RESULT_OK && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
139
+        return WebChromeClient.FileChooserParams.parseResult(resultCode, data);
140
+      } else {
141
+        return null;
142
+      }
129 143
     }
130 144
 
131 145
     // we have multiple files selected
132 146
     if (data.getClipData() != null) {
133
-        final int numSelectedFiles = data.getClipData().getItemCount();
134
-        Uri[] result = new Uri[numSelectedFiles];
135
-        for (int i = 0; i < numSelectedFiles; i++) {
136
-            result[i] = data.getClipData().getItemAt(i).getUri();
137
-        }
138
-        return result;
147
+      final int numSelectedFiles = data.getClipData().getItemCount();
148
+      Uri[] result = new Uri[numSelectedFiles];
149
+      for (int i = 0; i < numSelectedFiles; i++) {
150
+        result[i] = data.getClipData().getItemAt(i).getUri();
151
+      }
152
+      return result;
139 153
     }
140 154
     return null;
141 155
   }
142 156
 
143 157
   public void startPhotoPickerIntent(ValueCallback<Uri> filePathCallback, String acceptType) {
144
-      filePathCallbackLegacy = filePathCallback;
158
+    filePathCallbackLegacy = filePathCallback;
145 159
 
146
-      Intent fileChooserIntent = getFileChooserIntent(acceptType);
147
-      Intent chooserIntent = Intent.createChooser(fileChooserIntent, "");
160
+    Intent fileChooserIntent = getFileChooserIntent(acceptType);
161
+    Intent chooserIntent = Intent.createChooser(fileChooserIntent, "");
148 162
 
149
-      ArrayList<Parcelable> extraIntents = new ArrayList<>();
150
-      if (acceptsImages(acceptType)) {
151
-          extraIntents.add(getPhotoIntent());
152
-      }
153
-      if (acceptsVideo(acceptType)) {
154
-          extraIntents.add(getVideoIntent());
155
-      }
156
-      chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Parcelable[]{}));
163
+    ArrayList<Parcelable> extraIntents = new ArrayList<>();
164
+    if (acceptsImages(acceptType)) {
165
+      extraIntents.add(getPhotoIntent());
166
+    }
167
+    if (acceptsVideo(acceptType)) {
168
+      extraIntents.add(getVideoIntent());
169
+    }
170
+    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Parcelable[]{}));
157 171
 
158
-      if (chooserIntent.resolveActivity(getCurrentActivity().getPackageManager()) != null) {
159
-          getCurrentActivity().startActivityForResult(chooserIntent, PICKER_LEGACY);
160
-      } else {
161
-          Log.w("RNCWebViewModule", "there is no Activity to handle this Intent");
162
-      }
172
+    if (chooserIntent.resolveActivity(getCurrentActivity().getPackageManager()) != null) {
173
+      getCurrentActivity().startActivityForResult(chooserIntent, PICKER_LEGACY);
174
+    } else {
175
+      Log.w("RNCWebViewModule", "there is no Activity to handle this Intent");
176
+    }
163 177
   }
164 178
 
165 179
   @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@@ -181,9 +195,9 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
181 195
     chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Parcelable[]{}));
182 196
 
183 197
     if (chooserIntent.resolveActivity(getCurrentActivity().getPackageManager()) != null) {
184
-        getCurrentActivity().startActivityForResult(chooserIntent, PICKER);
198
+      getCurrentActivity().startActivityForResult(chooserIntent, PICKER);
185 199
     } else {
186
-        Log.w("RNCWebViewModule", "there is no Activity to handle this Intent");
200
+      Log.w("RNCWebViewModule", "there is no Activity to handle this Intent");
187 201
     }
188 202
 
189 203
     return true;
@@ -214,7 +228,7 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
214 228
 
215 229
     if (!result) {
216 230
       PermissionAwareActivity activity = getPermissionAwareActivity();
217
-      activity.requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE }, FILE_DOWNLOAD_PERMISSION_REQUEST, webviewFileDownloaderPermissionListener);
231
+      activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, FILE_DOWNLOAD_PERMISSION_REQUEST, webviewFileDownloaderPermissionListener);
218 232
     }
219 233
 
220 234
     return result;
@@ -270,10 +284,11 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
270 284
   private Boolean acceptsImages(String types) {
271 285
     String mimeType = types;
272 286
     if (types.matches("\\.\\w+")) {
273
-        mimeType = getMimeTypeFromExtension(types.replace(".", ""));
287
+      mimeType = getMimeTypeFromExtension(types.replace(".", ""));
274 288
     }
275 289
     return mimeType.isEmpty() || mimeType.toLowerCase().contains("image");
276 290
   }
291
+
277 292
   private Boolean acceptsImages(String[] types) {
278 293
     String[] mimeTypes = getAcceptedMimeType(types);
279 294
     return isArrayEmpty(mimeTypes) || arrayContainsString(mimeTypes, "image");
@@ -282,38 +297,39 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
282 297
   private Boolean acceptsVideo(String types) {
283 298
     String mimeType = types;
284 299
     if (types.matches("\\.\\w+")) {
285
-        mimeType = getMimeTypeFromExtension(types.replace(".", ""));
300
+      mimeType = getMimeTypeFromExtension(types.replace(".", ""));
286 301
     }
287 302
     return mimeType.isEmpty() || mimeType.toLowerCase().contains("video");
288 303
   }
304
+
289 305
   private Boolean acceptsVideo(String[] types) {
290 306
     String[] mimeTypes = getAcceptedMimeType(types);
291 307
     return isArrayEmpty(mimeTypes) || arrayContainsString(mimeTypes, "video");
292 308
   }
293 309
 
294
-  private Boolean arrayContainsString(String[] array, String pattern){
295
-    for(String content : array){
296
-        if(content.contains(pattern)){
297
-            return true;
298
-        }
310
+  private Boolean arrayContainsString(String[] array, String pattern) {
311
+    for (String content : array) {
312
+      if (content.contains(pattern)) {
313
+        return true;
314
+      }
299 315
     }
300 316
     return false;
301 317
   }
302 318
 
303 319
   private String[] getAcceptedMimeType(String[] types) {
304 320
     if (isArrayEmpty(types)) {
305
-        return new String[]{DEFAULT_MIME_TYPES};
321
+      return new String[]{DEFAULT_MIME_TYPES};
306 322
     }
307 323
     String[] mimeTypes = new String[types.length];
308 324
     for (int i = 0; i < types.length; i++) {
309
-        String t = types[i];
310
-        // convert file extensions to mime types
311
-        if (t.matches("\\.\\w+")) {
312
-            String mimeType = getMimeTypeFromExtension(t.replace(".", ""));
313
-            mimeTypes[i] = mimeType;
314
-        } else {
315
-            mimeTypes[i] = t;
316
-        }
325
+      String t = types[i];
326
+      // convert file extensions to mime types
327
+      if (t.matches("\\.\\w+")) {
328
+        String mimeType = getMimeTypeFromExtension(t.replace(".", ""));
329
+        mimeTypes[i] = mimeType;
330
+      } else {
331
+        mimeTypes[i] = t;
332
+      }
317 333
     }
318 334
     return mimeTypes;
319 335
   }
@@ -321,7 +337,7 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
321 337
   private String getMimeTypeFromExtension(String extension) {
322 338
     String type = null;
323 339
     if (extension != null) {
324
-        type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
340
+      type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
325 341
     }
326 342
     return type;
327 343
   }
@@ -329,20 +345,20 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
329 345
   private Uri getOutputUri(String intentType) {
330 346
     File capturedFile = null;
331 347
     try {
332
-        capturedFile = getCapturedFile(intentType);
348
+      capturedFile = getCapturedFile(intentType);
333 349
     } catch (IOException e) {
334
-        Log.e("CREATE FILE", "Error occurred while creating the File", e);
335
-        e.printStackTrace();
350
+      Log.e("CREATE FILE", "Error occurred while creating the File", e);
351
+      e.printStackTrace();
336 352
     }
337 353
 
338 354
     // for versions below 6.0 (23) we use the old File creation & permissions model
339 355
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
340
-        return Uri.fromFile(capturedFile);
356
+      return Uri.fromFile(capturedFile);
341 357
     }
342 358
 
343 359
     // for versions 6.0+ (23) we use the FileProvider to avoid runtime permissions
344 360
     String packageName = getReactApplicationContext().getPackageName();
345
-    return FileProvider.getUriForFile(getReactApplicationContext(), packageName+".fileprovider", capturedFile);
361
+    return FileProvider.getUriForFile(getReactApplicationContext(), packageName + ".fileprovider", capturedFile);
346 362
   }
347 363
 
348 364
   private File getCapturedFile(String intentType) throws IOException {
@@ -365,10 +381,10 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
365 381
 
366 382
     // for versions below 6.0 (23) we use the old File creation & permissions model
367 383
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
368
-        // only this Directory works on all tested Android versions
369
-        // ctx.getExternalFilesDir(dir) was failing on Android 5.0 (sdk 21)
370
-        File storageDir = Environment.getExternalStoragePublicDirectory(dir);
371
-        return new File(storageDir, filename);
384
+      // only this Directory works on all tested Android versions
385
+      // ctx.getExternalFilesDir(dir) was failing on Android 5.0 (sdk 21)
386
+      File storageDir = Environment.getExternalStoragePublicDirectory(dir);
387
+      return new File(storageDir, filename);
372 388
     }
373 389
 
374 390
     File storageDir = getReactApplicationContext().getExternalFilesDir(null);
@@ -385,30 +401,10 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
385 401
   private PermissionAwareActivity getPermissionAwareActivity() {
386 402
     Activity activity = getCurrentActivity();
387 403
     if (activity == null) {
388
-        throw new IllegalStateException("Tried to use permissions API while not attached to an Activity.");
404
+      throw new IllegalStateException("Tried to use permissions API while not attached to an Activity.");
389 405
     } else if (!(activity instanceof PermissionAwareActivity)) {
390
-        throw new IllegalStateException("Tried to use permissions API but the host Activity doesn't implement PermissionAwareActivity.");
406
+      throw new IllegalStateException("Tried to use permissions API but the host Activity doesn't implement PermissionAwareActivity.");
391 407
     }
392 408
     return (PermissionAwareActivity) activity;
393 409
   }
394
-
395
-  private PermissionListener webviewFileDownloaderPermissionListener = new PermissionListener() {
396
-    @Override
397
-    public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
398
-      switch (requestCode) {
399
-        case FILE_DOWNLOAD_PERMISSION_REQUEST: {
400
-          // If request is cancelled, the result arrays are empty.
401
-          if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
402
-            if (downloadRequest != null) {
403
-              downloadFile();
404
-            }
405
-          } else {
406
-            Toast.makeText(getCurrentActivity().getApplicationContext(), "Cannot download files as permission was denied. Please provide permission to write to storage, in order to download files.", Toast.LENGTH_LONG).show();
407
-          }
408
-          return true;
409
-        }
410
-      }
411
-      return false;
412
-    }
413
-  };
414 410
 }

+ 33
- 34
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewPackage.java View File

@@ -1,44 +1,43 @@
1
-
2 1
 package com.reactnativecommunity.webview;
3 2
 
4
-import java.util.ArrayList;
5
-import java.util.Arrays;
6
-import java.util.Collections;
7
-import java.util.List;
8
-
9 3
 import com.facebook.react.ReactPackage;
4
+import com.facebook.react.bridge.JavaScriptModule;
10 5
 import com.facebook.react.bridge.NativeModule;
11 6
 import com.facebook.react.bridge.ReactApplicationContext;
12 7
 import com.facebook.react.uimanager.ViewManager;
13
-import com.facebook.react.bridge.JavaScriptModule;
8
+
9
+import java.util.ArrayList;
10
+import java.util.Arrays;
11
+import java.util.Collections;
12
+import java.util.List;
14 13
 
15 14
 public class RNCWebViewPackage implements ReactPackage {
16 15
 
17
-    private RNCWebViewManager manager;
18
-    private RNCWebViewModule module;
19
-
20
-    @Override
21
-    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
22
-      List<NativeModule> modulesList = new ArrayList<>();
23
-      module = new RNCWebViewModule(reactContext);
24
-      module.setPackage(this);
25
-      modulesList.add(module);
26
-      return modulesList;
27
-    }
28
-
29
-    // Deprecated from RN 0.47
30
-    public List<Class<? extends JavaScriptModule>> createJSModules() {
31
-      return Collections.emptyList();
32
-    }
33
-
34
-    @Override
35
-    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
36
-      manager = new RNCWebViewManager();
37
-      manager.setPackage(this);
38
-      return Arrays.<ViewManager>asList(manager);
39
-    }
40
-
41
-    public RNCWebViewModule getModule() {
42
-      return module;
43
-    }
16
+  private RNCWebViewManager manager;
17
+  private RNCWebViewModule module;
18
+
19
+  @Override
20
+  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
21
+    List<NativeModule> modulesList = new ArrayList<>();
22
+    module = new RNCWebViewModule(reactContext);
23
+    module.setPackage(this);
24
+    modulesList.add(module);
25
+    return modulesList;
26
+  }
27
+
28
+  // Deprecated from RN 0.47
29
+  public List<Class<? extends JavaScriptModule>> createJSModules() {
30
+    return Collections.emptyList();
31
+  }
32
+
33
+  @Override
34
+  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
35
+    manager = new RNCWebViewManager();
36
+    manager.setPackage(this);
37
+    return Arrays.<ViewManager>asList(manager);
38
+  }
39
+
40
+  public RNCWebViewModule getModule() {
41
+    return module;
42
+  }
44 43
 }

+ 9
- 9
android/src/main/java/com/reactnativecommunity/webview/events/TopLoadingErrorEvent.kt View File

@@ -8,18 +8,18 @@ import com.facebook.react.uimanager.events.RCTEventEmitter
8 8
  * Event emitted when there is an error in loading.
9 9
  */
10 10
 class TopLoadingErrorEvent(viewId: Int, private val mEventData: WritableMap) :
11
-    Event<TopLoadingErrorEvent>(viewId) {
12
-    companion object {
13
-        const val EVENT_NAME = "topLoadingError"
14
-    }
11
+  Event<TopLoadingErrorEvent>(viewId) {
12
+  companion object {
13
+    const val EVENT_NAME = "topLoadingError"
14
+  }
15 15
 
16
-    override fun getEventName(): String = EVENT_NAME
16
+  override fun getEventName(): String = EVENT_NAME
17 17
 
18
-    override fun canCoalesce(): Boolean = false
18
+  override fun canCoalesce(): Boolean = false
19 19
 
20
-    override fun getCoalescingKey(): Short = 0
20
+  override fun getCoalescingKey(): Short = 0
21 21
 
22
-    override fun dispatch(rctEventEmitter: RCTEventEmitter) =
23
-        rctEventEmitter.receiveEvent(viewTag, eventName, mEventData)
22
+  override fun dispatch(rctEventEmitter: RCTEventEmitter) =
23
+    rctEventEmitter.receiveEvent(viewTag, eventName, mEventData)
24 24
 
25 25
 }

+ 9
- 9
android/src/main/java/com/reactnativecommunity/webview/events/TopLoadingFinishEvent.kt View File

@@ -8,17 +8,17 @@ import com.facebook.react.uimanager.events.RCTEventEmitter
8 8
  * Event emitted when loading is completed.
9 9
  */
10 10
 class TopLoadingFinishEvent(viewId: Int, private val mEventData: WritableMap) :
11
-    Event<TopLoadingFinishEvent>(viewId) {
12
-    companion object {
13
-        const val EVENT_NAME = "topLoadingFinish"
14
-    }
11
+  Event<TopLoadingFinishEvent>(viewId) {
12
+  companion object {
13
+    const val EVENT_NAME = "topLoadingFinish"
14
+  }
15 15
 
16
-    override fun getEventName(): String = EVENT_NAME
16
+  override fun getEventName(): String = EVENT_NAME
17 17
 
18
-    override fun canCoalesce(): Boolean = false
18
+  override fun canCoalesce(): Boolean = false
19 19
 
20
-    override fun getCoalescingKey(): Short = 0
20
+  override fun getCoalescingKey(): Short = 0
21 21
 
22
-    override fun dispatch(rctEventEmitter: RCTEventEmitter) =
23
-        rctEventEmitter.receiveEvent(viewTag, eventName, mEventData)
22
+  override fun dispatch(rctEventEmitter: RCTEventEmitter) =
23
+    rctEventEmitter.receiveEvent(viewTag, eventName, mEventData)
24 24
 }

+ 9
- 9
android/src/main/java/com/reactnativecommunity/webview/events/TopLoadingProgressEvent.kt View File

@@ -8,17 +8,17 @@ import com.facebook.react.uimanager.events.RCTEventEmitter
8 8
  * Event emitted when there is a loading progress event.
9 9
  */
10 10
 class TopLoadingProgressEvent(viewId: Int, private val mEventData: WritableMap) :
11
-    Event<TopLoadingProgressEvent>(viewId) {
12
-    companion object {
13
-        const val EVENT_NAME = "topLoadingProgress"
14
-    }
11
+  Event<TopLoadingProgressEvent>(viewId) {
12
+  companion object {
13
+    const val EVENT_NAME = "topLoadingProgress"
14
+  }
15 15
 
16
-    override fun getEventName(): String = EVENT_NAME
16
+  override fun getEventName(): String = EVENT_NAME
17 17
 
18
-    override fun canCoalesce(): Boolean = false
18
+  override fun canCoalesce(): Boolean = false
19 19
 
20
-    override fun getCoalescingKey(): Short = 0
20
+  override fun getCoalescingKey(): Short = 0
21 21
 
22
-    override fun dispatch(rctEventEmitter: RCTEventEmitter) =
23
-        rctEventEmitter.receiveEvent(viewTag, eventName, mEventData)
22
+  override fun dispatch(rctEventEmitter: RCTEventEmitter) =
23
+    rctEventEmitter.receiveEvent(viewTag, eventName, mEventData)
24 24
 }

+ 9
- 9
android/src/main/java/com/reactnativecommunity/webview/events/TopLoadingStartEvent.kt View File

@@ -8,18 +8,18 @@ import com.facebook.react.uimanager.events.RCTEventEmitter
8 8
  * Event emitted when loading has started
9 9
  */
10 10
 class TopLoadingStartEvent(viewId: Int, private val mEventData: WritableMap) :
11
-    Event<TopLoadingStartEvent>(viewId) {
12
-    companion object {
13
-        const val EVENT_NAME = "topLoadingStart"
14
-    }
11
+  Event<TopLoadingStartEvent>(viewId) {
12
+  companion object {
13
+    const val EVENT_NAME = "topLoadingStart"
14
+  }
15 15
 
16
-    override fun getEventName(): String = EVENT_NAME
16
+  override fun getEventName(): String = EVENT_NAME
17 17
 
18
-    override fun canCoalesce(): Boolean = false
18
+  override fun canCoalesce(): Boolean = false
19 19
 
20
-    override fun getCoalescingKey(): Short = 0
20
+  override fun getCoalescingKey(): Short = 0
21 21
 
22
-    override fun dispatch(rctEventEmitter: RCTEventEmitter) =
23
-        rctEventEmitter.receiveEvent(viewTag, eventName, mEventData)
22
+  override fun dispatch(rctEventEmitter: RCTEventEmitter) =
23
+    rctEventEmitter.receiveEvent(viewTag, eventName, mEventData)
24 24
 
25 25
 }

+ 11
- 11
android/src/main/java/com/reactnativecommunity/webview/events/TopMessageEvent.kt View File

@@ -8,19 +8,19 @@ import com.facebook.react.uimanager.events.RCTEventEmitter
8 8
  * Event emitted when there is an error in loading.
9 9
  */
10 10
 class TopMessageEvent(viewId: Int, private val mData: String) : Event<TopMessageEvent>(viewId) {
11
-    companion object {
12
-        const val EVENT_NAME = "topMessage"
13
-    }
11
+  companion object {
12
+    const val EVENT_NAME = "topMessage"
13
+  }
14 14
 
15
-    override fun getEventName(): String = EVENT_NAME
15
+  override fun getEventName(): String = EVENT_NAME
16 16
 
17
-    override fun canCoalesce(): Boolean = false
17
+  override fun canCoalesce(): Boolean = false
18 18
 
19
-    override fun getCoalescingKey(): Short = 0
19
+  override fun getCoalescingKey(): Short = 0
20 20
 
21
-    override fun dispatch(rctEventEmitter: RCTEventEmitter) {
22
-        val data = Arguments.createMap()
23
-        data.putString("data", mData)
24
-        rctEventEmitter.receiveEvent(viewTag, EVENT_NAME, data)
25
-    }
21
+  override fun dispatch(rctEventEmitter: RCTEventEmitter) {
22
+    val data = Arguments.createMap()
23
+    data.putString("data", mData)
24
+    rctEventEmitter.receiveEvent(viewTag, EVENT_NAME, data)
25
+  }
26 26
 }

+ 11
- 11
android/src/main/java/com/reactnativecommunity/webview/events/TopShouldStartLoadWithRequestEvent.kt View File

@@ -8,20 +8,20 @@ import com.facebook.react.uimanager.events.RCTEventEmitter
8 8
  * Event emitted when shouldOverrideUrlLoading is called
9 9
  */
10 10
 class TopShouldStartLoadWithRequestEvent(viewId: Int, private val mData: WritableMap) : Event<TopShouldStartLoadWithRequestEvent>(viewId) {
11
-    companion object {
12
-        const val EVENT_NAME = "topShouldStartLoadWithRequest"
13
-    }
11
+  companion object {
12
+    const val EVENT_NAME = "topShouldStartLoadWithRequest"
13
+  }
14 14
 
15
-    init {
16
-        mData.putString("navigationType", "other")
17
-    }
15
+  init {
16
+    mData.putString("navigationType", "other")
17
+  }
18 18
 
19
-    override fun getEventName(): String = EVENT_NAME
19
+  override fun getEventName(): String = EVENT_NAME
20 20
 
21
-    override fun canCoalesce(): Boolean = false
21
+  override fun canCoalesce(): Boolean = false
22 22
 
23
-    override fun getCoalescingKey(): Short = 0
23
+  override fun getCoalescingKey(): Short = 0
24 24
 
25
-    override fun dispatch(rctEventEmitter: RCTEventEmitter) =
26
-            rctEventEmitter.receiveEvent(viewTag, EVENT_NAME, mData)
25
+  override fun dispatch(rctEventEmitter: RCTEventEmitter) =
26
+    rctEventEmitter.receiveEvent(viewTag, EVENT_NAME, mData)
27 27
 }

+ 4
- 2
android/src/main/res/xml/file_provider_paths.xml View File

@@ -1,4 +1,6 @@
1 1
 <?xml version="1.0" encoding="utf-8"?>
2
-<paths xmlns:android="http://schemas.android.com/apk/res/android">
3
-    <external-path name="shared" path="." />
2
+<paths>
3
+  <external-path
4
+    name="shared"
5
+    path="." />
4 6
 </paths>