|  | @@ -1,45 +1,56 @@
 | 
	
		
			
			| 1 | 1 |  package com.reactnativecommunity.webview;
 | 
	
		
			
			| 2 |  | -
 | 
	
		
			
			| 3 | 2 |  import android.annotation.SuppressLint;
 | 
	
		
			
			| 4 |  | -import android.annotation.TargetApi;
 | 
	
		
			
			| 5 | 3 |  import android.app.DownloadManager;
 | 
	
		
			
			| 6 |  | -import android.content.Context;
 | 
	
		
			
			| 7 |  | -import android.content.Intent;
 | 
	
		
			
			| 8 | 4 |  import android.content.pm.ActivityInfo;
 | 
	
		
			
			| 9 |  | -import android.content.pm.PackageManager;
 | 
	
		
			
			| 10 |  | -import android.graphics.Bitmap;
 | 
	
		
			
			| 11 | 5 |  import android.graphics.Color;
 | 
	
		
			
			| 12 | 6 |  import android.Manifest;
 | 
	
		
			
			| 13 |  | -import android.net.Uri;
 | 
	
		
			
			| 14 |  | -import android.os.Build;
 | 
	
		
			
			| 15 | 7 |  import android.os.Environment;
 | 
	
		
			
			| 16 | 8 |  import androidx.annotation.RequiresApi;
 | 
	
		
			
			| 17 | 9 |  import androidx.core.content.ContextCompat;
 | 
	
		
			
			| 18 |  | -import android.text.TextUtils;
 | 
	
		
			
			| 19 | 10 |  import android.view.Gravity;
 | 
	
		
			
			| 20 |  | -import android.view.View;
 | 
	
		
			
			| 21 | 11 |  import android.view.ViewGroup;
 | 
	
		
			
			| 22 |  | -import android.view.ViewGroup.LayoutParams;
 | 
	
		
			
			| 23 | 12 |  import android.view.WindowManager;
 | 
	
		
			
			| 24 |  | -import android.webkit.ConsoleMessage;
 | 
	
		
			
			| 25 |  | -import android.webkit.CookieManager;
 | 
	
		
			
			| 26 | 13 |  import android.webkit.DownloadListener;
 | 
	
		
			
			| 27 |  | -import android.webkit.GeolocationPermissions;
 | 
	
		
			
			| 28 |  | -import android.webkit.JavascriptInterface;
 | 
	
		
			
			| 29 | 14 |  import android.webkit.PermissionRequest;
 | 
	
		
			
			| 30 | 15 |  import android.webkit.URLUtil;
 | 
	
		
			
			| 31 |  | -import android.webkit.ValueCallback;
 | 
	
		
			
			| 32 |  | -import android.webkit.WebChromeClient;
 | 
	
		
			
			| 33 | 16 |  import android.webkit.WebResourceRequest;
 | 
	
		
			
			| 34 | 17 |  import android.webkit.WebResourceResponse;
 | 
	
		
			
			| 35 |  | -import android.webkit.WebSettings;
 | 
	
		
			
			| 36 |  | -import android.webkit.WebView;
 | 
	
		
			
			| 37 |  | -import android.webkit.WebViewClient;
 | 
	
		
			
			| 38 | 18 |  import android.widget.FrameLayout;
 | 
	
		
			
			| 39 |  | -
 | 
	
		
			
			| 40 | 19 |  import com.facebook.react.views.scroll.ScrollEvent;
 | 
	
		
			
			| 41 | 20 |  import com.facebook.react.views.scroll.ScrollEventType;
 | 
	
		
			
			| 42 | 21 |  import com.facebook.react.views.scroll.OnScrollDispatchHelper;
 | 
	
		
			
			|  | 22 | +import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
 | 
	
		
			
			|  | 23 | +import com.reactnativecommunity.webview.events.TopHttpErrorEvent;
 | 
	
		
			
			|  | 24 | +import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
 | 
	
		
			
			|  | 25 | +import com.reactnativecommunity.webview.events.TopLoadingProgressEvent;
 | 
	
		
			
			|  | 26 | +import com.reactnativecommunity.webview.events.TopLoadingStartEvent;
 | 
	
		
			
			|  | 27 | +import com.reactnativecommunity.webview.events.TopMessageEvent;
 | 
	
		
			
			|  | 28 | +import com.reactnativecommunity.webview.events.TopShouldStartLoadWithRequestEvent;
 | 
	
		
			
			|  | 29 | +import java.net.MalformedURLException;
 | 
	
		
			
			|  | 30 | +import java.net.URL;
 | 
	
		
			
			|  | 31 | +import android.annotation.TargetApi;
 | 
	
		
			
			|  | 32 | +import android.content.ActivityNotFoundException;
 | 
	
		
			
			|  | 33 | +import android.content.Context;
 | 
	
		
			
			|  | 34 | +import android.content.Intent;
 | 
	
		
			
			|  | 35 | +import android.content.pm.PackageManager;
 | 
	
		
			
			|  | 36 | +import android.content.pm.ResolveInfo;
 | 
	
		
			
			|  | 37 | +import android.graphics.Bitmap;
 | 
	
		
			
			|  | 38 | +import android.graphics.Picture;
 | 
	
		
			
			|  | 39 | +import android.net.Uri;
 | 
	
		
			
			|  | 40 | +import android.os.Build;
 | 
	
		
			
			|  | 41 | +import android.text.TextUtils;
 | 
	
		
			
			|  | 42 | +import android.view.View;
 | 
	
		
			
			|  | 43 | +import android.view.ViewGroup.LayoutParams;
 | 
	
		
			
			|  | 44 | +import android.webkit.ConsoleMessage;
 | 
	
		
			
			|  | 45 | +import android.webkit.CookieManager;
 | 
	
		
			
			|  | 46 | +import android.webkit.GeolocationPermissions;
 | 
	
		
			
			|  | 47 | +import android.webkit.JavascriptInterface;
 | 
	
		
			
			|  | 48 | +import android.webkit.ValueCallback;
 | 
	
		
			
			|  | 49 | +import android.webkit.WebChromeClient;
 | 
	
		
			
			|  | 50 | +import android.webkit.WebSettings;
 | 
	
		
			
			|  | 51 | +import android.webkit.WebView;
 | 
	
		
			
			|  | 52 | +import android.webkit.WebViewClient;
 | 
	
		
			
			|  | 53 | +import com.facebook.common.logging.FLog;
 | 
	
		
			
			| 43 | 54 |  import com.facebook.react.bridge.Arguments;
 | 
	
		
			
			| 44 | 55 |  import com.facebook.react.bridge.LifecycleEventListener;
 | 
	
		
			
			| 45 | 56 |  import com.facebook.react.bridge.ReactContext;
 | 
	
	
		
			
			|  | @@ -48,6 +59,7 @@ import com.facebook.react.bridge.ReadableMap;
 | 
	
		
			
			| 48 | 59 |  import com.facebook.react.bridge.ReadableMapKeySetIterator;
 | 
	
		
			
			| 49 | 60 |  import com.facebook.react.bridge.WritableMap;
 | 
	
		
			
			| 50 | 61 |  import com.facebook.react.common.MapBuilder;
 | 
	
		
			
			|  | 62 | +import com.facebook.react.common.ReactConstants;
 | 
	
		
			
			| 51 | 63 |  import com.facebook.react.common.build.ReactBuildConfig;
 | 
	
		
			
			| 52 | 64 |  import com.facebook.react.module.annotations.ReactModule;
 | 
	
		
			
			| 53 | 65 |  import com.facebook.react.uimanager.SimpleViewManager;
 | 
	
	
		
			
			|  | @@ -57,27 +69,23 @@ import com.facebook.react.uimanager.annotations.ReactProp;
 | 
	
		
			
			| 57 | 69 |  import com.facebook.react.uimanager.events.ContentSizeChangeEvent;
 | 
	
		
			
			| 58 | 70 |  import com.facebook.react.uimanager.events.Event;
 | 
	
		
			
			| 59 | 71 |  import com.facebook.react.uimanager.events.EventDispatcher;
 | 
	
		
			
			| 60 |  | -import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
 | 
	
		
			
			| 61 |  | -import com.reactnativecommunity.webview.events.TopHttpErrorEvent;
 | 
	
		
			
			| 62 |  | -import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
 | 
	
		
			
			| 63 |  | -import com.reactnativecommunity.webview.events.TopLoadingProgressEvent;
 | 
	
		
			
			| 64 |  | -import com.reactnativecommunity.webview.events.TopLoadingStartEvent;
 | 
	
		
			
			| 65 |  | -import com.reactnativecommunity.webview.events.TopMessageEvent;
 | 
	
		
			
			| 66 |  | -import com.reactnativecommunity.webview.events.TopShouldStartLoadWithRequestEvent;
 | 
	
		
			
			| 67 |  | -
 | 
	
		
			
			| 68 |  | -import org.json.JSONException;
 | 
	
		
			
			| 69 |  | -import org.json.JSONObject;
 | 
	
		
			
			| 70 |  | -
 | 
	
		
			
			|  | 72 | +// import com.facebook.react.views.webview.events.TopLoadingErrorEvent;
 | 
	
		
			
			|  | 73 | +// import com.facebook.react.views.webview.events.TopLoadingFinishEvent;
 | 
	
		
			
			|  | 74 | +// import com.facebook.react.views.webview.events.TopLoadingStartEvent;
 | 
	
		
			
			|  | 75 | +// import com.facebook.react.views.webview.events.TopMessageEvent;
 | 
	
		
			
			| 71 | 76 |  import java.io.UnsupportedEncodingException;
 | 
	
		
			
			| 72 |  | -import java.net.MalformedURLException;
 | 
	
		
			
			| 73 |  | -import java.net.URL;
 | 
	
		
			
			|  | 77 | +import java.net.URISyntaxException;
 | 
	
		
			
			| 74 | 78 |  import java.net.URLEncoder;
 | 
	
		
			
			| 75 | 79 |  import java.util.ArrayList;
 | 
	
		
			
			| 76 | 80 |  import java.util.HashMap;
 | 
	
		
			
			|  | 81 | +import java.util.LinkedList;
 | 
	
		
			
			|  | 82 | +import java.util.List;
 | 
	
		
			
			| 77 | 83 |  import java.util.Locale;
 | 
	
		
			
			| 78 | 84 |  import java.util.Map;
 | 
	
		
			
			| 79 |  | -
 | 
	
		
			
			|  | 85 | +import java.util.regex.Pattern;
 | 
	
		
			
			| 80 | 86 |  import javax.annotation.Nullable;
 | 
	
		
			
			|  | 87 | +import org.json.JSONException;
 | 
	
		
			
			|  | 88 | +import org.json.JSONObject;
 | 
	
		
			
			| 81 | 89 |  
 | 
	
		
			
			| 82 | 90 |  /**
 | 
	
		
			
			| 83 | 91 |   * Manages instances of {@link WebView}
 | 
	
	
		
			
			|  | @@ -129,6 +137,10 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
 | 
	
		
			
			| 129 | 137 |    // Use `webView.loadUrl("about:blank")` to reliably reset the view
 | 
	
		
			
			| 130 | 138 |    // state and release page resources (including any running JavaScript).
 | 
	
		
			
			| 131 | 139 |    protected static final String BLANK_URL = "about:blank";
 | 
	
		
			
			|  | 140 | +  
 | 
	
		
			
			|  | 141 | +  // Intent urls are a type of deeplinks which start with: intent://
 | 
	
		
			
			|  | 142 | +  private static final String INTENT_URL_PREFIX = "intent://";
 | 
	
		
			
			|  | 143 | +  
 | 
	
		
			
			| 132 | 144 |    protected WebViewConfig mWebViewConfig;
 | 
	
		
			
			| 133 | 145 |  
 | 
	
		
			
			| 134 | 146 |    protected RNCWebChromeClient mWebChromeClient = null;
 | 
	
	
		
			
			|  | @@ -739,18 +751,82 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
 | 
	
		
			
			| 739 | 751 |            createWebViewEvent(webView, url)));
 | 
	
		
			
			| 740 | 752 |      }
 | 
	
		
			
			| 741 | 753 |  
 | 
	
		
			
			|  | 754 | +
 | 
	
		
			
			| 742 | 755 |      @Override
 | 
	
		
			
			| 743 | 756 |      public boolean shouldOverrideUrlLoading(WebView view, String url) {
 | 
	
		
			
			| 744 |  | -      activeUrl = url;
 | 
	
		
			
			| 745 |  | -      dispatchEvent(
 | 
	
		
			
			| 746 |  | -        view,
 | 
	
		
			
			| 747 |  | -        new TopShouldStartLoadWithRequestEvent(
 | 
	
		
			
			| 748 |  | -          view.getId(),
 | 
	
		
			
			| 749 |  | -          createWebViewEvent(view, url)));
 | 
	
		
			
			|  | 757 | +      if (url.equals(BLANK_URL)) return false;
 | 
	
		
			
			|  | 758 | +
 | 
	
		
			
			|  | 759 | +      // url blacklisting
 | 
	
		
			
			|  | 760 | +      if (mUrlPrefixesForDefaultIntent != null && mUrlPrefixesForDefaultIntent.size() > 0) {
 | 
	
		
			
			|  | 761 | +        ArrayList<Object> urlPrefixesForDefaultIntent = mUrlPrefixesForDefaultIntent.toArrayList();
 | 
	
		
			
			|  | 762 | +        for (Object urlPrefix : urlPrefixesForDefaultIntent) {
 | 
	
		
			
			|  | 763 | +          if (url.startsWith((String) urlPrefix)) {
 | 
	
		
			
			|  | 764 | +            launchIntent(view.getContext(), url);
 | 
	
		
			
			|  | 765 | +            return true;
 | 
	
		
			
			|  | 766 | +          }
 | 
	
		
			
			|  | 767 | +        }
 | 
	
		
			
			|  | 768 | +      }
 | 
	
		
			
			|  | 769 | +
 | 
	
		
			
			|  | 770 | +      launchIntent(view.getContext(), url);
 | 
	
		
			
			| 750 | 771 |        return true;
 | 
	
		
			
			| 751 | 772 |      }
 | 
	
		
			
			| 752 | 773 |  
 | 
	
		
			
			| 753 | 774 |  
 | 
	
		
			
			|  | 775 | +    private void launchIntent(Context context, String url) {
 | 
	
		
			
			|  | 776 | +      Intent intent = null;
 | 
	
		
			
			|  | 777 | +
 | 
	
		
			
			|  | 778 | +      // URLs starting with 'intent://' require special handling.
 | 
	
		
			
			|  | 779 | +      if (url.startsWith(INTENT_URL_PREFIX)) {
 | 
	
		
			
			|  | 780 | +        try {
 | 
	
		
			
			|  | 781 | +          intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
 | 
	
		
			
			|  | 782 | +        } catch (URISyntaxException e) {
 | 
	
		
			
			|  | 783 | +          FLog.e(ReactConstants.TAG, "Can't parse intent:// URI", e);
 | 
	
		
			
			|  | 784 | +        }
 | 
	
		
			
			|  | 785 | +      }
 | 
	
		
			
			|  | 786 | +
 | 
	
		
			
			|  | 787 | +      if (intent != null) {
 | 
	
		
			
			|  | 788 | +        // This is needed to prevent security issue where non-exported activities from the same process can be started with intent:// URIs.
 | 
	
		
			
			|  | 789 | +        // See: T10607927/S136245
 | 
	
		
			
			|  | 790 | +        intent.addCategory(Intent.CATEGORY_BROWSABLE);
 | 
	
		
			
			|  | 791 | +        intent.setComponent(null);
 | 
	
		
			
			|  | 792 | +        intent.setSelector(null);
 | 
	
		
			
			|  | 793 | +
 | 
	
		
			
			|  | 794 | +        PackageManager packageManager = context.getPackageManager();
 | 
	
		
			
			|  | 795 | +        ResolveInfo info = packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
 | 
	
		
			
			|  | 796 | +        if (info != null) {
 | 
	
		
			
			|  | 797 | +            // App is installed.
 | 
	
		
			
			|  | 798 | +            context.startActivity(intent);
 | 
	
		
			
			|  | 799 | +        } else {
 | 
	
		
			
			|  | 800 | +            String fallbackUrl = intent.getStringExtra("browser_fallback_url");
 | 
	
		
			
			|  | 801 | +            intent = new Intent(Intent.ACTION_VIEW, Uri.parse(fallbackUrl));
 | 
	
		
			
			|  | 802 | +        }
 | 
	
		
			
			|  | 803 | +      } else {
 | 
	
		
			
			|  | 804 | +        intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
 | 
	
		
			
			|  | 805 | +      }
 | 
	
		
			
			|  | 806 | +
 | 
	
		
			
			|  | 807 | +      try {
 | 
	
		
			
			|  | 808 | +        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
	
		
			
			|  | 809 | +        intent.addCategory(Intent.CATEGORY_BROWSABLE);
 | 
	
		
			
			|  | 810 | +        context.startActivity(intent);
 | 
	
		
			
			|  | 811 | +      } catch (ActivityNotFoundException e) {
 | 
	
		
			
			|  | 812 | +        FLog.w(ReactConstants.TAG, "activity not found to handle uri scheme for: " + url, e);
 | 
	
		
			
			|  | 813 | +      }
 | 
	
		
			
			|  | 814 | +    }
 | 
	
		
			
			|  | 815 | +
 | 
	
		
			
			|  | 816 | +    private boolean shouldHandleURL(List<Pattern> originWhitelist, String url) {
 | 
	
		
			
			|  | 817 | +      Uri uri = Uri.parse(url);
 | 
	
		
			
			|  | 818 | +      String scheme = uri.getScheme() != null ? uri.getScheme() : "";
 | 
	
		
			
			|  | 819 | +      String authority = uri.getAuthority() != null ? uri.getAuthority() : "";
 | 
	
		
			
			|  | 820 | +      String urlToCheck = scheme + "://" + authority;
 | 
	
		
			
			|  | 821 | +      for (Pattern pattern : originWhitelist) {
 | 
	
		
			
			|  | 822 | +        if (pattern.matcher(urlToCheck).matches()) {
 | 
	
		
			
			|  | 823 | +          return true;
 | 
	
		
			
			|  | 824 | +        }
 | 
	
		
			
			|  | 825 | +      }
 | 
	
		
			
			|  | 826 | +      return false;
 | 
	
		
			
			|  | 827 | +    }
 | 
	
		
			
			|  | 828 | +
 | 
	
		
			
			|  | 829 | +
 | 
	
		
			
			| 754 | 830 |      @TargetApi(Build.VERSION_CODES.N)
 | 
	
		
			
			| 755 | 831 |      @Override
 | 
	
		
			
			| 756 | 832 |      public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
 |