|  | @@ -30,6 +30,7 @@ import android.webkit.GeolocationPermissions;
 | 
	
		
			
			| 30 | 30 |  import android.webkit.JavascriptInterface;
 | 
	
		
			
			| 31 | 31 |  import android.webkit.ValueCallback;
 | 
	
		
			
			| 32 | 32 |  import android.webkit.WebChromeClient;
 | 
	
		
			
			|  | 33 | +import android.webkit.WebResourceRequest;
 | 
	
		
			
			| 33 | 34 |  import android.webkit.WebSettings;
 | 
	
		
			
			| 34 | 35 |  import android.webkit.WebView;
 | 
	
		
			
			| 35 | 36 |  import android.webkit.WebViewClient;
 | 
	
	
		
			
			|  | @@ -51,11 +52,19 @@ import com.facebook.react.uimanager.annotations.ReactProp;
 | 
	
		
			
			| 51 | 52 |  import com.facebook.react.uimanager.events.ContentSizeChangeEvent;
 | 
	
		
			
			| 52 | 53 |  import com.facebook.react.uimanager.events.Event;
 | 
	
		
			
			| 53 | 54 |  import com.facebook.react.uimanager.events.EventDispatcher;
 | 
	
		
			
			|  | 55 | +import com.facebook.react.uimanager.events.RCTEventEmitter;
 | 
	
		
			
			| 54 | 56 |  import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
 | 
	
		
			
			| 55 | 57 |  import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
 | 
	
		
			
			| 56 | 58 |  import com.reactnativecommunity.webview.events.TopLoadingStartEvent;
 | 
	
		
			
			| 57 | 59 |  import com.reactnativecommunity.webview.events.TopMessageEvent;
 | 
	
		
			
			| 58 | 60 |  import com.reactnativecommunity.webview.events.TopLoadingProgressEvent;
 | 
	
		
			
			|  | 61 | +import com.reactnativecommunity.webview.events.TopShouldStartLoadWithRequestEvent;
 | 
	
		
			
			|  | 62 | +import java.io.UnsupportedEncodingException;
 | 
	
		
			
			|  | 63 | +import java.util.ArrayList;
 | 
	
		
			
			|  | 64 | +import java.util.HashMap;
 | 
	
		
			
			|  | 65 | +import java.util.Locale;
 | 
	
		
			
			|  | 66 | +import java.util.Map;
 | 
	
		
			
			|  | 67 | +import javax.annotation.Nullable;
 | 
	
		
			
			| 59 | 68 |  import org.json.JSONException;
 | 
	
		
			
			| 60 | 69 |  import org.json.JSONObject;
 | 
	
		
			
			| 61 | 70 |  
 | 
	
	
		
			
			|  | @@ -66,12 +75,14 @@ import org.json.JSONObject;
 | 
	
		
			
			| 66 | 75 |   *  - GO_BACK
 | 
	
		
			
			| 67 | 76 |   *  - GO_FORWARD
 | 
	
		
			
			| 68 | 77 |   *  - RELOAD
 | 
	
		
			
			|  | 78 | + *  - LOAD_URL
 | 
	
		
			
			| 69 | 79 |   *
 | 
	
		
			
			| 70 | 80 |   * {@link WebView} instances could emit following direct events:
 | 
	
		
			
			| 71 | 81 |   *  - topLoadingFinish
 | 
	
		
			
			| 72 | 82 |   *  - topLoadingStart
 | 
	
		
			
			| 73 | 83 |   *  - topLoadingStart
 | 
	
		
			
			| 74 | 84 |   *  - topLoadingProgress
 | 
	
		
			
			|  | 85 | + *  - topShouldStartLoadWithRequest
 | 
	
		
			
			| 75 | 86 |   *
 | 
	
		
			
			| 76 | 87 |   * Each event will carry the following properties:
 | 
	
		
			
			| 77 | 88 |   *  - target - view's react tag
 | 
	
	
		
			
			|  | @@ -99,6 +110,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
 | 
	
		
			
			| 99 | 110 |    public static final int COMMAND_STOP_LOADING = 4;
 | 
	
		
			
			| 100 | 111 |    public static final int COMMAND_POST_MESSAGE = 5;
 | 
	
		
			
			| 101 | 112 |    public static final int COMMAND_INJECT_JAVASCRIPT = 6;
 | 
	
		
			
			|  | 113 | +  public static final int COMMAND_LOAD_URL = 7;
 | 
	
		
			
			| 102 | 114 |  
 | 
	
		
			
			| 103 | 115 |    // Use `webView.loadUrl("about:blank")` to reliably reset the view
 | 
	
		
			
			| 104 | 116 |    // state and release page resources (including any running JavaScript).
 | 
	
	
		
			
			|  | @@ -111,7 +123,6 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
 | 
	
		
			
			| 111 | 123 |  
 | 
	
		
			
			| 112 | 124 |      protected boolean mLastLoadFailed = false;
 | 
	
		
			
			| 113 | 125 |      protected @Nullable ReadableArray mUrlPrefixesForDefaultIntent;
 | 
	
		
			
			| 114 |  | -    protected @Nullable List<Pattern> mOriginWhitelist;
 | 
	
		
			
			| 115 | 126 |  
 | 
	
		
			
			| 116 | 127 |      @Override
 | 
	
		
			
			| 117 | 128 |      public void onPageFinished(WebView webView, String url) {
 | 
	
	
		
			
			|  | @@ -139,50 +150,16 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
 | 
	
		
			
			| 139 | 150 |  
 | 
	
		
			
			| 140 | 151 |      @Override
 | 
	
		
			
			| 141 | 152 |      public boolean shouldOverrideUrlLoading(WebView view, String url) {
 | 
	
		
			
			| 142 |  | -      if (url.equals(BLANK_URL)) return false;
 | 
	
		
			
			| 143 |  | -
 | 
	
		
			
			| 144 |  | -      // url blacklisting
 | 
	
		
			
			| 145 |  | -      if (mUrlPrefixesForDefaultIntent != null && mUrlPrefixesForDefaultIntent.size() > 0) {
 | 
	
		
			
			| 146 |  | -        ArrayList<Object> urlPrefixesForDefaultIntent =
 | 
	
		
			
			| 147 |  | -            mUrlPrefixesForDefaultIntent.toArrayList();
 | 
	
		
			
			| 148 |  | -        for (Object urlPrefix : urlPrefixesForDefaultIntent) {
 | 
	
		
			
			| 149 |  | -          if (url.startsWith((String) urlPrefix)) {
 | 
	
		
			
			| 150 |  | -            launchIntent(view.getContext(), url);
 | 
	
		
			
			| 151 |  | -            return true;
 | 
	
		
			
			| 152 |  | -          }
 | 
	
		
			
			| 153 |  | -        }
 | 
	
		
			
			| 154 |  | -      }
 | 
	
		
			
			| 155 |  | -
 | 
	
		
			
			| 156 |  | -      if (mOriginWhitelist != null && shouldHandleURL(mOriginWhitelist, url)) {
 | 
	
		
			
			| 157 |  | -        return false;
 | 
	
		
			
			| 158 |  | -      }
 | 
	
		
			
			| 159 |  | -
 | 
	
		
			
			| 160 |  | -      launchIntent(view.getContext(), url);
 | 
	
		
			
			|  | 153 | +      dispatchEvent(view, new TopShouldStartLoadWithRequestEvent(view.getId(), url));
 | 
	
		
			
			| 161 | 154 |        return true;
 | 
	
		
			
			| 162 | 155 |      }
 | 
	
		
			
			| 163 | 156 |  
 | 
	
		
			
			| 164 |  | -    private void launchIntent(Context context, String url) {
 | 
	
		
			
			| 165 |  | -      try {
 | 
	
		
			
			| 166 |  | -        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
 | 
	
		
			
			| 167 |  | -        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
	
		
			
			| 168 |  | -        intent.addCategory(Intent.CATEGORY_BROWSABLE);
 | 
	
		
			
			| 169 |  | -        context.startActivity(intent);
 | 
	
		
			
			| 170 |  | -      } catch (ActivityNotFoundException e) {
 | 
	
		
			
			| 171 |  | -        FLog.w(ReactConstants.TAG, "activity not found to handle uri scheme for: " + url, e);
 | 
	
		
			
			| 172 |  | -      }
 | 
	
		
			
			| 173 |  | -    }
 | 
	
		
			
			| 174 | 157 |  
 | 
	
		
			
			| 175 |  | -    private boolean shouldHandleURL(List<Pattern> originWhitelist, String url) {
 | 
	
		
			
			| 176 |  | -      Uri uri = Uri.parse(url);
 | 
	
		
			
			| 177 |  | -      String scheme = uri.getScheme() != null ? uri.getScheme() : "";
 | 
	
		
			
			| 178 |  | -      String authority = uri.getAuthority() != null ? uri.getAuthority() : "";
 | 
	
		
			
			| 179 |  | -      String urlToCheck = scheme + "://" + authority;
 | 
	
		
			
			| 180 |  | -      for (Pattern pattern : originWhitelist) {
 | 
	
		
			
			| 181 |  | -        if (pattern.matcher(urlToCheck).matches()) {
 | 
	
		
			
			| 182 |  | -          return true;
 | 
	
		
			
			| 183 |  | -        }
 | 
	
		
			
			| 184 |  | -      }
 | 
	
		
			
			| 185 |  | -      return false;
 | 
	
		
			
			|  | 158 | +    @TargetApi(Build.VERSION_CODES.N)
 | 
	
		
			
			|  | 159 | +    @Override
 | 
	
		
			
			|  | 160 | +    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
 | 
	
		
			
			|  | 161 | +      dispatchEvent(view, new TopShouldStartLoadWithRequestEvent(view.getId(), request.getUrl().toString()));
 | 
	
		
			
			|  | 162 | +      return true;
 | 
	
		
			
			| 186 | 163 |      }
 | 
	
		
			
			| 187 | 164 |  
 | 
	
		
			
			| 188 | 165 |      @Override
 | 
	
	
		
			
			|  | @@ -231,10 +208,6 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
 | 
	
		
			
			| 231 | 208 |      public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) {
 | 
	
		
			
			| 232 | 209 |        mUrlPrefixesForDefaultIntent = specialUrls;
 | 
	
		
			
			| 233 | 210 |      }
 | 
	
		
			
			| 234 |  | -
 | 
	
		
			
			| 235 |  | -    public void setOriginWhitelist(List<Pattern> originWhitelist) {
 | 
	
		
			
			| 236 |  | -      mOriginWhitelist = originWhitelist;
 | 
	
		
			
			| 237 |  | -    }
 | 
	
		
			
			| 238 | 211 |    }
 | 
	
		
			
			| 239 | 212 |  
 | 
	
		
			
			| 240 | 213 |    /**
 | 
	
	
		
			
			|  | @@ -656,20 +629,6 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
 | 
	
		
			
			| 656 | 629 |      view.getSettings().setGeolocationEnabled(isGeolocationEnabled != null && isGeolocationEnabled);
 | 
	
		
			
			| 657 | 630 |    }
 | 
	
		
			
			| 658 | 631 |  
 | 
	
		
			
			| 659 |  | -  @ReactProp(name = "originWhitelist")
 | 
	
		
			
			| 660 |  | -  public void setOriginWhitelist(
 | 
	
		
			
			| 661 |  | -    WebView view,
 | 
	
		
			
			| 662 |  | -    @Nullable ReadableArray originWhitelist) {
 | 
	
		
			
			| 663 |  | -    RNCWebViewClient client = ((RNCWebView) view).getRNCWebViewClient();
 | 
	
		
			
			| 664 |  | -    if (client != null && originWhitelist != null) {
 | 
	
		
			
			| 665 |  | -      List<Pattern> whiteList = new LinkedList<>();
 | 
	
		
			
			| 666 |  | -      for (int i = 0 ; i < originWhitelist.size() ; i++) {
 | 
	
		
			
			| 667 |  | -        whiteList.add(Pattern.compile(originWhitelist.getString(i)));
 | 
	
		
			
			| 668 |  | -      }
 | 
	
		
			
			| 669 |  | -      client.setOriginWhitelist(whiteList);
 | 
	
		
			
			| 670 |  | -    }
 | 
	
		
			
			| 671 |  | -  }
 | 
	
		
			
			| 672 |  | -
 | 
	
		
			
			| 673 | 632 |    @Override
 | 
	
		
			
			| 674 | 633 |    protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
 | 
	
		
			
			| 675 | 634 |      // Do not register default touch emitter and let WebView implementation handle touches
 | 
	
	
		
			
			|  | @@ -678,9 +637,13 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
 | 
	
		
			
			| 678 | 637 |  
 | 
	
		
			
			| 679 | 638 |    @Override
 | 
	
		
			
			| 680 | 639 |    public Map getExportedCustomDirectEventTypeConstants() {
 | 
	
		
			
			| 681 |  | -    MapBuilder.Builder builder = MapBuilder.builder();
 | 
	
		
			
			| 682 |  | -    builder.put("topLoadingProgress", MapBuilder.of("registrationName", "onLoadingProgress"));
 | 
	
		
			
			| 683 |  | -    return builder.build();
 | 
	
		
			
			|  | 640 | +    Map export = super.getExportedCustomDirectEventTypeConstants();
 | 
	
		
			
			|  | 641 | +    if (export == null) {
 | 
	
		
			
			|  | 642 | +      export = MapBuilder.newHashMap();
 | 
	
		
			
			|  | 643 | +    }
 | 
	
		
			
			|  | 644 | +    export.put(TopLoadingProgressEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingProgress"));
 | 
	
		
			
			|  | 645 | +    export.put(TopShouldStartLoadWithRequestEvent.EVENT_NAME, MapBuilder.of("registrationName", "onShouldStartLoadWithRequest"));
 | 
	
		
			
			|  | 646 | +    return export;
 | 
	
		
			
			| 684 | 647 |    }
 | 
	
		
			
			| 685 | 648 |  
 | 
	
		
			
			| 686 | 649 |    @Override
 | 
	
	
		
			
			|  | @@ -691,7 +654,8 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
 | 
	
		
			
			| 691 | 654 |          "reload", COMMAND_RELOAD,
 | 
	
		
			
			| 692 | 655 |          "stopLoading", COMMAND_STOP_LOADING,
 | 
	
		
			
			| 693 | 656 |          "postMessage", COMMAND_POST_MESSAGE,
 | 
	
		
			
			| 694 |  | -        "injectJavaScript", COMMAND_INJECT_JAVASCRIPT
 | 
	
		
			
			|  | 657 | +        "injectJavaScript", COMMAND_INJECT_JAVASCRIPT,
 | 
	
		
			
			|  | 658 | +        "loadUrl", COMMAND_LOAD_URL
 | 
	
		
			
			| 695 | 659 |        );
 | 
	
		
			
			| 696 | 660 |    }
 | 
	
		
			
			| 697 | 661 |  
 | 
	
	
		
			
			|  | @@ -734,6 +698,12 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
 | 
	
		
			
			| 734 | 698 |          RNCWebView reactWebView = (RNCWebView) root;
 | 
	
		
			
			| 735 | 699 |          reactWebView.evaluateJavascriptWithFallback(args.getString(0));
 | 
	
		
			
			| 736 | 700 |          break;
 | 
	
		
			
			|  | 701 | +      case COMMAND_LOAD_URL:
 | 
	
		
			
			|  | 702 | +        if (args == null) {
 | 
	
		
			
			|  | 703 | +          throw new RuntimeException("Arguments for loading an url are null!");
 | 
	
		
			
			|  | 704 | +        }
 | 
	
		
			
			|  | 705 | +        root.loadUrl(args.getString(0));
 | 
	
		
			
			|  | 706 | +        break;
 | 
	
		
			
			| 737 | 707 |      }
 | 
	
		
			
			| 738 | 708 |    }
 | 
	
		
			
			| 739 | 709 |  
 |