Skip to content

Commit 179f509

Browse files
Merge pull request #2 from ava-labs/cp-9266
CP-9266: show alertDialog when webview requests for permissions
2 parents df8b47f + bb66886 commit 179f509

File tree

2 files changed

+107
-53
lines changed

2 files changed

+107
-53
lines changed

android/src/main/java/com/reactnativecommunity/webview/RNCWebChromeClient.java

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
import android.Manifest;
44
import android.annotation.TargetApi;
55
import android.app.Activity;
6+
import android.app.AlertDialog;
7+
import android.content.DialogInterface;
68
import android.content.pm.PackageManager;
79
import android.net.Uri;
810
import android.os.Build;
11+
import android.os.Handler;
912
import android.os.Message;
1013
import android.view.Gravity;
1114
import android.view.View;
@@ -17,6 +20,7 @@
1720
import android.webkit.WebChromeClient;
1821
import android.webkit.WebView;
1922
import android.webkit.WebViewClient;
23+
import android.widget.Button;
2024
import android.widget.FrameLayout;
2125

2226
import androidx.annotation.RequiresApi;
@@ -146,15 +150,18 @@ public void onProgressChanged(WebView webView, int newProgress) {
146150
public void onPermissionRequest(final PermissionRequest request) {
147151

148152
grantedPermissions = new ArrayList<>();
149-
150153
ArrayList<String> requestedAndroidPermissions = new ArrayList<>();
154+
ArrayList<String> requestPermissionIdentifiers = new ArrayList<>();
155+
151156
for (String requestedResource : request.getResources()) {
152157
String androidPermission = null;
153158

154159
if (requestedResource.equals(PermissionRequest.RESOURCE_AUDIO_CAPTURE)) {
155160
androidPermission = Manifest.permission.RECORD_AUDIO;
161+
requestPermissionIdentifiers.add("microphone");
156162
} else if (requestedResource.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {
157163
androidPermission = Manifest.permission.CAMERA;
164+
requestPermissionIdentifiers.add("camera");
158165
} else if(requestedResource.equals(PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID)) {
159166
if (mAllowsProtectedMedia) {
160167
grantedPermissions.add(requestedResource);
@@ -167,49 +174,86 @@ public void onPermissionRequest(final PermissionRequest request) {
167174
* Find more details here: https://github.com/react-native-webview/react-native-webview/pull/2732
168175
*/
169176
androidPermission = PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID;
170-
} }
177+
}
178+
}
179+
171180
// TODO: RESOURCE_MIDI_SYSEX, RESOURCE_PROTECTED_MEDIA_ID.
172181
if (androidPermission != null) {
173-
if (ContextCompat.checkSelfPermission(this.mWebView.getThemedReactContext(), androidPermission) == PackageManager.PERMISSION_GRANTED) {
182+
if (ContextCompat.checkSelfPermission(this.mWebView.getContext(), androidPermission) == PackageManager.PERMISSION_GRANTED) {
174183
grantedPermissions.add(requestedResource);
175184
} else {
176185
requestedAndroidPermissions.add(androidPermission);
177186
}
178187
}
179188
}
180189

181-
// If all the permissions are already granted, send the response to the WebView synchronously
182-
if (requestedAndroidPermissions.isEmpty()) {
183-
request.grant(grantedPermissions.toArray(new String[0]));
184-
grantedPermissions = null;
190+
if (requestedAndroidPermissions.isEmpty() && grantedPermissions.isEmpty()) {
185191
return;
186192
}
187193

188-
// Otherwise, ask to Android System for native permissions asynchronously
189-
190-
this.permissionRequest = request;
191-
192-
requestPermissions(requestedAndroidPermissions);
194+
String identifierStr = String.join(", ", requestPermissionIdentifiers);
195+
String alertMessage = String.format("Allow this app to use your " + identifierStr + "?");
196+
AlertDialog.Builder builder = new AlertDialog.Builder(this.mWebView.getContext());
197+
builder.setMessage(alertMessage);
198+
builder.setCancelable(false);
199+
builder.setPositiveButton("Allow", (dialog, which) -> {
200+
if (!requestedAndroidPermissions.isEmpty()) {
201+
// ask to Android System for native permissions asynchronously
202+
this.permissionRequest = request;
203+
requestPermissions(requestedAndroidPermissions);
204+
return;
205+
}
206+
if (!grantedPermissions.isEmpty()) {
207+
// If all the permissions are already granted, send the response to the WebView synchronously
208+
final String[] grantedPermissionsArray = grantedPermissions.toArray(new String[0]);
209+
request.grant(grantedPermissionsArray);
210+
grantedPermissions = null;
211+
}
212+
});
213+
builder.setNegativeButton("Don't allow", (dialog, which) -> {
214+
request.deny();
215+
});
216+
AlertDialog alertDialog = builder.create();
217+
alertDialog.show();
218+
//Delay making `allow` clickable for 500ms to avoid unwanted presses.
219+
Button posButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
220+
posButton.setEnabled(false);
221+
this.runDelayed(() -> posButton.setEnabled(true), 500);
193222
}
194223

224+
private void runDelayed(Runnable function, long delayMillis) {
225+
Handler handler = new Handler();
226+
handler.postDelayed(function, delayMillis);
227+
}
195228

196229
@Override
197230
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
198-
199-
if (ContextCompat.checkSelfPermission(this.mWebView.getThemedReactContext(), Manifest.permission.ACCESS_FINE_LOCATION)
231+
String alertMessage = String.format("Allow this app to use your location?");
232+
AlertDialog.Builder builder = new AlertDialog.Builder(this.mWebView.getContext());
233+
builder.setMessage(alertMessage);
234+
builder.setCancelable(false);
235+
builder.setPositiveButton("Allow", (dialog, which) -> {
236+
if (ContextCompat.checkSelfPermission(this.mWebView.getContext(), Manifest.permission.ACCESS_FINE_LOCATION)
200237
!= PackageManager.PERMISSION_GRANTED) {
201-
202-
/*
203-
* Keep the trace of callback and origin for the async permission request
204-
*/
205-
geolocationPermissionCallback = callback;
206-
geolocationPermissionOrigin = origin;
207-
208-
requestPermissions(Collections.singletonList(Manifest.permission.ACCESS_FINE_LOCATION));
209-
210-
} else {
211-
callback.invoke(origin, true, false);
212-
}
238+
/*
239+
* Keep the trace of callback and origin for the async permission request
240+
*/
241+
geolocationPermissionCallback = callback;
242+
geolocationPermissionOrigin = origin;
243+
requestPermissions(Collections.singletonList(Manifest.permission.ACCESS_FINE_LOCATION));
244+
} else {
245+
callback.invoke(origin, true, false);
246+
}
247+
});
248+
builder.setNegativeButton("Don't allow", (dialog, which) -> {
249+
callback.invoke(origin, false, false);
250+
});
251+
AlertDialog alertDialog = builder.create();
252+
alertDialog.show();
253+
//Delay making `allow` clickable for 500ms to avoid unwanted presses.
254+
Button posButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
255+
posButton.setEnabled(false);
256+
this.runDelayed(() -> posButton.setEnabled(true), 500);
213257
}
214258

215259
private PermissionAwareActivity getPermissionAwareActivity() {
@@ -369,4 +413,4 @@ public void setAllowsProtectedMedia(boolean enabled) {
369413
public void setHasOnOpenWindowEvent(boolean hasEvent) {
370414
mHasOnOpenWindowEvent = hasEvent;
371415
}
372-
}
416+
}

android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.reactnativecommunity.webview
22

3+
import android.app.AlertDialog
34
import android.app.DownloadManager
5+
import android.content.DialogInterface
46
import android.content.pm.ActivityInfo
57
import android.graphics.Bitmap
68
import android.graphics.Color
@@ -106,34 +108,42 @@ class RNCWebViewManagerImpl {
106108

107109
val downloadMessage = "Downloading $fileName"
108110

109-
//Attempt to add cookie, if it exists
110-
var urlObj: URL? = null
111-
try {
112-
urlObj = URL(url)
113-
val baseUrl = urlObj.protocol + "://" + urlObj.host
114-
val cookie = CookieManager.getInstance().getCookie(baseUrl)
115-
request.addRequestHeader("Cookie", cookie)
116-
} catch (e: MalformedURLException) {
117-
Log.w(TAG, "Error getting cookie for DownloadManager", e)
118-
}
111+
val builder = AlertDialog.Builder(webView.context)
112+
builder.setMessage("Do you want to download \n$fileName?")
113+
builder.setCancelable(false)
114+
builder.setPositiveButton("Download") { _, _ ->
115+
//Attempt to add cookie, if it exists
116+
var urlObj: URL? = null
117+
try {
118+
urlObj = URL(url)
119+
val baseUrl = urlObj.protocol + "://" + urlObj.host
120+
val cookie = CookieManager.getInstance().getCookie(baseUrl)
121+
request.addRequestHeader("Cookie", cookie)
122+
} catch (e: MalformedURLException) {
123+
Log.w(TAG, "Error getting cookie for DownloadManager", e)
124+
}
119125

120-
//Finish setting up request
121-
request.addRequestHeader("User-Agent", userAgent)
122-
request.setTitle(fileName)
123-
request.setDescription(downloadMessage)
124-
request.allowScanningByMediaScanner()
125-
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
126-
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
127-
module.setDownloadRequest(request)
128-
if (module.grantFileDownloaderPermissions(
129-
getDownloadingMessageOrDefault(),
130-
getLackPermissionToDownloadMessageOrDefault()
131-
)
132-
) {
133-
module.downloadFile(
134-
getDownloadingMessageOrDefault()
135-
)
126+
//Finish setting up request
127+
request.addRequestHeader("User-Agent", userAgent)
128+
request.setTitle(fileName)
129+
request.setDescription(downloadMessage)
130+
request.allowScanningByMediaScanner()
131+
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
132+
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
133+
module.setDownloadRequest(request)
134+
if (module.grantFileDownloaderPermissions(
135+
getDownloadingMessageOrDefault(),
136+
getLackPermissionToDownloadMessageOrDefault()
137+
)
138+
) {
139+
module.downloadFile(
140+
getDownloadingMessageOrDefault()
141+
)
142+
}
136143
}
144+
builder.setNegativeButton("Cancel") { _: DialogInterface?, _: Int -> }
145+
val alertDialog = builder.create()
146+
alertDialog.show()
137147
})
138148
return RNCWebViewWrapper(context, webView)
139149
}

0 commit comments

Comments
 (0)