3
3
import android .Manifest ;
4
4
import android .annotation .TargetApi ;
5
5
import android .app .Activity ;
6
+ import android .app .AlertDialog ;
7
+ import android .content .DialogInterface ;
6
8
import android .content .pm .PackageManager ;
7
9
import android .net .Uri ;
8
10
import android .os .Build ;
11
+ import android .os .Handler ;
9
12
import android .os .Message ;
10
13
import android .view .Gravity ;
11
14
import android .view .View ;
17
20
import android .webkit .WebChromeClient ;
18
21
import android .webkit .WebView ;
19
22
import android .webkit .WebViewClient ;
23
+ import android .widget .Button ;
20
24
import android .widget .FrameLayout ;
21
25
22
26
import androidx .annotation .RequiresApi ;
@@ -146,15 +150,18 @@ public void onProgressChanged(WebView webView, int newProgress) {
146
150
public void onPermissionRequest (final PermissionRequest request ) {
147
151
148
152
grantedPermissions = new ArrayList <>();
149
-
150
153
ArrayList <String > requestedAndroidPermissions = new ArrayList <>();
154
+ ArrayList <String > requestPermissionIdentifiers = new ArrayList <>();
155
+
151
156
for (String requestedResource : request .getResources ()) {
152
157
String androidPermission = null ;
153
158
154
159
if (requestedResource .equals (PermissionRequest .RESOURCE_AUDIO_CAPTURE )) {
155
160
androidPermission = Manifest .permission .RECORD_AUDIO ;
161
+ requestPermissionIdentifiers .add ("microphone" );
156
162
} else if (requestedResource .equals (PermissionRequest .RESOURCE_VIDEO_CAPTURE )) {
157
163
androidPermission = Manifest .permission .CAMERA ;
164
+ requestPermissionIdentifiers .add ("camera" );
158
165
} else if (requestedResource .equals (PermissionRequest .RESOURCE_PROTECTED_MEDIA_ID )) {
159
166
if (mAllowsProtectedMedia ) {
160
167
grantedPermissions .add (requestedResource );
@@ -167,49 +174,86 @@ public void onPermissionRequest(final PermissionRequest request) {
167
174
* Find more details here: https://github.com/react-native-webview/react-native-webview/pull/2732
168
175
*/
169
176
androidPermission = PermissionRequest .RESOURCE_PROTECTED_MEDIA_ID ;
170
- } }
177
+ }
178
+ }
179
+
171
180
// TODO: RESOURCE_MIDI_SYSEX, RESOURCE_PROTECTED_MEDIA_ID.
172
181
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 ) {
174
183
grantedPermissions .add (requestedResource );
175
184
} else {
176
185
requestedAndroidPermissions .add (androidPermission );
177
186
}
178
187
}
179
188
}
180
189
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 ()) {
185
191
return ;
186
192
}
187
193
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 );
193
222
}
194
223
224
+ private void runDelayed (Runnable function , long delayMillis ) {
225
+ Handler handler = new Handler ();
226
+ handler .postDelayed (function , delayMillis );
227
+ }
195
228
196
229
@ Override
197
230
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 )
200
237
!= 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 );
213
257
}
214
258
215
259
private PermissionAwareActivity getPermissionAwareActivity () {
@@ -369,4 +413,4 @@ public void setAllowsProtectedMedia(boolean enabled) {
369
413
public void setHasOnOpenWindowEvent (boolean hasEvent ) {
370
414
mHasOnOpenWindowEvent = hasEvent ;
371
415
}
372
- }
416
+ }
0 commit comments