Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asking Camera Permission For WebView At Runtime

I want to use a QR code scanner within a PWA over Android WebView.

  • The PWA works as intended over the chrome browser.
  • I do have permission for camera set under Apps -> App name -> Permissions
  • minSdkVersion 26 and targetSdkVersion 28
  • Tested on Huawei phone with Android 9

The problem is that the permission seems to be not granted. Additionally, the permission request is repeated multiple times.

Android Code

Manifest

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature
            android:name="android.hardware.camera"
            android:required="true" />

According to answers from previous question I have this code in class WebViewHelper

lateinit var webkitPermissionRequest : PermissionRequest
...
webView.webChromeClient = object : WebChromeClient() {

    override fun onPermissionRequest(request: PermissionRequest) {
        webkitPermissionRequest = request
        val requestedResources = request.resources
        for (r in requestedResources) {
            if (r == PermissionRequest.RESOURCE_VIDEO_CAPTURE) {
                // In this sample, we only accept video capture request.
                val alertDialogBuilder = AlertDialog.Builder(activity)
                    .setTitle("Allow Permission to camera")
                    .setPositiveButton("Allow") { dialog, which ->
                        dialog.dismiss()
                        webkitPermissionRequest.grant(arrayOf(PermissionRequest.RESOURCE_VIDEO_CAPTURE))
                        Log.d(TAG, "Granted")
                    }
                    .setNegativeButton("Deny") { dialog, which ->
                        dialog.dismiss()
                        webkitPermissionRequest.deny()
                        Log.d(TAG, "Denied")
                    }
                val alertDialog = alertDialogBuilder.create()
                alertDialog.show()
                break
            }
        }
    }
    ...
}

Logs: After the permission is granted, it is requested again (multiple times)

D/WebViewHelper: Granted
V/InputMethodManager: Reporting focus gain, without startInput
I/PermissionManager: camera remind result:true
I/CameraManager: open camera: 1, package name: "myApp"
I/BackgroundPermManager: pkgName: "myApp", pid: 31365 ,uidOf3RdApk: 10197 ,permType: 0 ,permCfg: 1
I/HwCameraUtil: notifySurfaceFlingerCameraStatus : isFront = true , isOpend = true
I/HwCameraUtil: notifySurfaceFlingerFrontCameraStatus 8011 transact success!
E/cr_VideoCapture: CameraDevice.StateCallback onOpened
I/WebViewHelper: onPermissionRequest
    onPermissionRequest

This seems to be the problem

I/GRALLOC: LockFlexLayout: baseFormat: 11, yStride: 640, ySize: 307200, uOffset: 307200,  uStride: 640
E/ion: ioctl c0044901 failed with code -1: Invalid argument
I/chromium: "Unhandled rejection", source: "PWA
            "Uncaught (in promise) NotAllowedError: play() can only be initiated by a user gesture.", source: "PWA"

Finally, this error is repeated (indefinitely)

I/GRALLOC: LockFlexLayout: baseFormat: 11, yStride: 640, ySize: 307200, uOffset: 307200,  uStride: 640

JavaScript Code

The code on the JS side works perfectly fine until opened in WebView.

    app.ports.scanQR.subscribe(() => {
        // Delay until page loaded
        setTimeout(function(){
            const video = document.getElementById('media-video');
            function returnResult(result) {
                app.ports.onGotQR.send(result);
                scanner.destroy();
            }
        const scanner = new QrScanner(video, result => returnResult(result));
        scanner.start();
        }, 50);
    });
}

Issue persist if I call getUserMedia directly

if (navigator.mediaDevices.getUserMedia) {
    navigator.mediaDevices.getUserMedia({ video: true })
    .then(function (stream) {
        video.srcObject = stream;
    })
    .catch(function (err0r) {
        console.log("Something went wrong!");
    });
}

Getting the App permission for the camera:

In WebviewHelper.kt

webView.webChromeClient = object : WebChromeClient() {
    override fun onPermissionRequest(request: PermissionRequest) {
        Log.i(TAG, "onPermissionRequest")

        // grants permission for app. video not showing
        if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA)
            != PackageManager.PERMISSION_GRANTED
        ) {
            Log.i(TAG, "Request Permission")
            requestPermissions(activity, arrayOf(Manifest.permission.CAMERA), 1010)
        } else {
            Log.i(TAG, "Permission already granted")
        }
    ...
}

In MainActivity.kt

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        when (requestCode) {
            cameraRequestCode -> {
                Log.d("MainActivity", "onRequestPermissionsResult: Camera Request")
                if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                    Log.d("MainActivity", "Camera Request: Permission granted")
                    // permission was granted, yay!
                } else {
                    // permission denied, boo!
                    Log.d("MainActivity", "Camera Request: Permission denied")
                }
                return
            }
...

Which result in the following log as expected, when App permission for the camera is granted:

D/MainActivity: onRequestPermissionsResult: Camera Request
    Camera Request: Permission granted
like image 661
Noah Studach Avatar asked Apr 25 '19 11:04

Noah Studach


People also ask

Can we access camera in WebView in android?

Embedding in an Android App requires the use of the WebView class. To allow EnableX to access the camera, follow the steps given below: Override WebChromeClient.

How do I give camera permission in WebView?

Like a normal browser, 3D WebView blocks web pages from accessing the device's camera and microphone by default. However, applications can use Web. SetCameraAndMicrophoneEnabled() to enable camera and microphone access. On Windows, macOS, and WebGL, no additional configuration is needed.

How do I ask for runtime permission on Android?

Requesting Android Runtime Permissions For this the following method needs to be called on every permission. checkSelfPermission(String perm); It returns an integer value of PERMISSION_GRANTED or PERMISSION_DENIED.

How do you override a WebView?

If you want to override certain methods, you have to create a custom WebView class which extends WebView . Also, when you are inflating the WebView , make sure you are casting it to the correct type which is CustomWebView . CustomWebView webView = (CustomWebView) findViewById(R. id.


1 Answers

All of your original code is correct inside of the onPermissionRequest(...). The key line of code you're missing was webView.settings.mediaPlaybackRequiresUserGesture = false.

like image 65
dannyskim Avatar answered Sep 30 '22 17:09

dannyskim