Created
July 22, 2020 07:34
-
-
Save TPXP/5ec918850dd40bd46203c41e98e61003 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java | |
index ab869cf..e37d88d 100644 | |
--- a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java | |
+++ b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java | |
@@ -2,6 +2,7 @@ package com.reactnativecommunity.webview; | |
import android.annotation.SuppressLint; | |
import android.annotation.TargetApi; | |
+import android.app.Activity; | |
import android.app.DownloadManager; | |
import android.content.Context; | |
import android.content.Intent; | |
@@ -39,6 +40,8 @@ import android.webkit.WebView; | |
import android.webkit.WebViewClient; | |
import android.widget.FrameLayout; | |
+import com.facebook.react.modules.core.PermissionAwareActivity; | |
+import com.facebook.react.modules.core.PermissionListener; | |
import com.facebook.react.views.scroll.ScrollEvent; | |
import com.facebook.react.views.scroll.ScrollEventType; | |
import com.facebook.react.views.scroll.OnScrollDispatchHelper; | |
@@ -78,7 +81,9 @@ import java.net.MalformedURLException; | |
import java.net.URL; | |
import java.net.URLEncoder; | |
import java.util.ArrayList; | |
+import java.util.Collections; | |
import java.util.HashMap; | |
+import java.util.List; | |
import java.util.Locale; | |
import java.util.Map; | |
@@ -942,6 +947,8 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> { | |
View.SYSTEM_UI_FLAG_IMMERSIVE | | |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; | |
+ protected static final int COMMON_PERMISSION_REQUEST = 3; | |
+ | |
protected ReactContext mReactContext; | |
protected View mWebView; | |
@@ -950,6 +957,27 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> { | |
protected RNCWebView.ProgressChangedFilter progressChangedFilter = null; | |
+ /* | |
+ * - Permissions - | |
+ * As native permissions are asynchronously handled by the PermissionListener, many fields have | |
+ * to be stored to send permissions results to the webview | |
+ */ | |
+ | |
+ // Webview camera & audio permission callback | |
+ protected PermissionRequest permissionRequest; | |
+ // Webview camera & audio permission already granted | |
+ protected ArrayList<String> grantedPermissions; | |
+ | |
+ // Webview geolocation permission callback | |
+ protected GeolocationPermissions.Callback geolocationPermissionCallback; | |
+ // Webview geolocation permission origin callback | |
+ protected String geolocationPermissionOrigin; | |
+ | |
+ // true if native permissions dialog is shown, false otherwise | |
+ protected boolean permissionsRequestShown = false; | |
+ // Pending Android permissions for the next request | |
+ protected ArrayList<String> pendingPermissions = new ArrayList<>(); | |
+ | |
public RNCWebChromeClient(ReactContext reactContext, WebView webView) { | |
this.mReactContext = reactContext; | |
this.mWebView = webView; | |
@@ -964,42 +992,6 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> { | |
return true; | |
} | |
- // Fix WebRTC permission request error. | |
- @TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
- @Override | |
- public void onPermissionRequest(final PermissionRequest request) { | |
- String[] requestedResources = request.getResources(); | |
- ArrayList<String> permissions = new ArrayList<>(); | |
- ArrayList<String> grantedPermissions = new ArrayList<String>(); | |
- for (int i = 0; i < requestedResources.length; i++) { | |
- if (requestedResources[i].equals(PermissionRequest.RESOURCE_AUDIO_CAPTURE)) { | |
- permissions.add(Manifest.permission.RECORD_AUDIO); | |
- } else if (requestedResources[i].equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) { | |
- permissions.add(Manifest.permission.CAMERA); | |
- } | |
- // TODO: RESOURCE_MIDI_SYSEX, RESOURCE_PROTECTED_MEDIA_ID. | |
- } | |
- | |
- for (int i = 0; i < permissions.size(); i++) { | |
- if (ContextCompat.checkSelfPermission(mReactContext, permissions.get(i)) != PackageManager.PERMISSION_GRANTED) { | |
- continue; | |
- } | |
- if (permissions.get(i).equals(Manifest.permission.RECORD_AUDIO)) { | |
- grantedPermissions.add(PermissionRequest.RESOURCE_AUDIO_CAPTURE); | |
- } else if (permissions.get(i).equals(Manifest.permission.CAMERA)) { | |
- grantedPermissions.add(PermissionRequest.RESOURCE_VIDEO_CAPTURE); | |
- } | |
- } | |
- | |
- if (grantedPermissions.isEmpty()) { | |
- request.deny(); | |
- } else { | |
- String[] grantedPermissionsArray = new String[grantedPermissions.size()]; | |
- grantedPermissionsArray = grantedPermissions.toArray(grantedPermissionsArray); | |
- request.grant(grantedPermissionsArray); | |
- } | |
- } | |
- | |
@Override | |
public void onProgressChanged(WebView webView, int newProgress) { | |
super.onProgressChanged(webView, newProgress); | |
@@ -1021,11 +1013,163 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> { | |
event)); | |
} | |
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
+ @Override | |
+ public void onPermissionRequest(final PermissionRequest request) { | |
+ | |
+ grantedPermissions = new ArrayList<>(); | |
+ | |
+ ArrayList<String> requestedAndroidPermissions = new ArrayList<>(); | |
+ for (String requestedResource : request.getResources()) { | |
+ String androidPermission = null; | |
+ | |
+ if (requestedResource.equals(PermissionRequest.RESOURCE_AUDIO_CAPTURE)) { | |
+ androidPermission = Manifest.permission.RECORD_AUDIO; | |
+ } else if (requestedResource.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) { | |
+ androidPermission = Manifest.permission.CAMERA; | |
+ } | |
+ // TODO: RESOURCE_MIDI_SYSEX, RESOURCE_PROTECTED_MEDIA_ID. | |
+ | |
+ if (androidPermission != null) { | |
+ if (ContextCompat.checkSelfPermission(mReactContext, androidPermission) == PackageManager.PERMISSION_GRANTED) { | |
+ grantedPermissions.add(requestedResource); | |
+ } else { | |
+ requestedAndroidPermissions.add(androidPermission); | |
+ } | |
+ } | |
+ } | |
+ | |
+ // If all the permissions are already granted, send the response to the WebView synchronously | |
+ if (requestedAndroidPermissions.isEmpty()) { | |
+ request.grant(grantedPermissions.toArray(new String[0])); | |
+ grantedPermissions = null; | |
+ return; | |
+ } | |
+ | |
+ // Otherwise, ask to Android System for native permissions asynchronously | |
+ | |
+ this.permissionRequest = request; | |
+ | |
+ requestPermissions(requestedAndroidPermissions); | |
+ } | |
+ | |
@Override | |
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { | |
- callback.invoke(origin, true, false); | |
+ | |
+ | |
+ if (ContextCompat.checkSelfPermission(mReactContext, Manifest.permission.ACCESS_FINE_LOCATION) | |
+ != PackageManager.PERMISSION_GRANTED) { | |
+ | |
+ /* | |
+ * Keep the trace of callback and origin for the async permission request | |
+ */ | |
+ geolocationPermissionCallback = callback; | |
+ geolocationPermissionOrigin = origin; | |
+ | |
+ requestPermissions(Collections.singletonList(Manifest.permission.ACCESS_FINE_LOCATION)); | |
+ | |
+ } else { | |
+ callback.invoke(origin, true, true); | |
+ } | |
+ } | |
+ | |
+ private PermissionAwareActivity getPermissionAwareActivity() { | |
+ Activity activity = mReactContext.getCurrentActivity(); | |
+ if (activity == null) { | |
+ throw new IllegalStateException("Tried to use permissions API while not attached to an Activity."); | |
+ } else if (!(activity instanceof PermissionAwareActivity)) { | |
+ throw new IllegalStateException("Tried to use permissions API but the host Activity doesn't implement PermissionAwareActivity."); | |
+ } | |
+ return (PermissionAwareActivity) activity; | |
+ } | |
+ | |
+ private synchronized void requestPermissions(List<String> permissions) { | |
+ | |
+ /* | |
+ * If permissions request dialog is displayed on the screen and another request is sent to the | |
+ * activity, the last permission asked is skipped. As a work-around, we use pendingPermissions | |
+ * to store next required permissions. | |
+ */ | |
+ | |
+ if (permissionsRequestShown) { | |
+ pendingPermissions.addAll(permissions); | |
+ return; | |
+ } | |
+ | |
+ PermissionAwareActivity activity = getPermissionAwareActivity(); | |
+ permissionsRequestShown = true; | |
+ | |
+ activity.requestPermissions( | |
+ permissions.toArray(new String[0]), | |
+ COMMON_PERMISSION_REQUEST, | |
+ webviewPermissionsListener | |
+ ); | |
+ | |
+ // Pending permissions have been sent, the list can be cleared | |
+ pendingPermissions.clear(); | |
} | |
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
+ private PermissionListener webviewPermissionsListener = (requestCode, permissions, grantResults) -> { | |
+ | |
+ permissionsRequestShown = false; | |
+ | |
+ /* | |
+ * As a "pending requests" approach is used, requestCode cannot help to define if the request | |
+ * came from geolocation or camera/audio. This is why shouldAnswerToPermissionRequest is used | |
+ */ | |
+ boolean shouldAnswerToPermissionRequest = false; | |
+ | |
+ for (int i = 0; i < permissions.length; i++) { | |
+ | |
+ String permission = permissions[i]; | |
+ boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED; | |
+ | |
+ if (permission.equals(Manifest.permission.ACCESS_FINE_LOCATION) | |
+ && geolocationPermissionCallback != null | |
+ && geolocationPermissionOrigin != null) { | |
+ | |
+ if (granted) { | |
+ geolocationPermissionCallback.invoke(geolocationPermissionOrigin, true, true); | |
+ } else { | |
+ geolocationPermissionCallback.invoke(geolocationPermissionOrigin, false, false); | |
+ } | |
+ | |
+ geolocationPermissionCallback = null; | |
+ geolocationPermissionOrigin = null; | |
+ } | |
+ | |
+ if (permission.equals(Manifest.permission.RECORD_AUDIO)) { | |
+ if (granted && grantedPermissions != null) { | |
+ grantedPermissions.add(PermissionRequest.RESOURCE_AUDIO_CAPTURE); | |
+ } | |
+ shouldAnswerToPermissionRequest = true; | |
+ } | |
+ | |
+ if (permission.equals(Manifest.permission.CAMERA)) { | |
+ if (granted && grantedPermissions != null) { | |
+ grantedPermissions.add(PermissionRequest.RESOURCE_VIDEO_CAPTURE); | |
+ } | |
+ shouldAnswerToPermissionRequest = true; | |
+ } | |
+ } | |
+ | |
+ if (shouldAnswerToPermissionRequest | |
+ && permissionRequest != null | |
+ && grantedPermissions != null) { | |
+ permissionRequest.grant(grantedPermissions.toArray(new String[0])); | |
+ permissionRequest = null; | |
+ grantedPermissions = null; | |
+ } | |
+ | |
+ if (!pendingPermissions.isEmpty()) { | |
+ requestPermissions(pendingPermissions); | |
+ return false; | |
+ } | |
+ | |
+ return true; | |
+ }; | |
+ | |
protected void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType) { | |
getModule(mReactContext).startPhotoPickerIntent(filePathCallback, acceptType); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment