title: Asking Camera Permission For WebView At Runtime
body: <p>I want to use a QR code scanner within a PWA over Android WebView. </p>
  
  <ul>
  <li>The PWA works as intended over the chrome browser.</li>
  <li>I do have permission for camera set under Apps -> App name -> Permissions</li>
  <li><code>minSdkVersion 26</code> and <code>targetSdkVersion 28</code></li>
  <li>Tested on Huawei phone with Android 9</li>
  </ul>
  
  <p>The problem is that the permission seems to be not granted. Additionally, the permission request is repeated multiple times.</p>
  
  <h3>Android Code</h3>
  
  <p>Manifest</p>
  
  <pre><code>    &lt;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /&gt;
      &lt;uses-permission android:name="android.permission.INTERNET" /&gt;
      &lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&gt;
      &lt;uses-permission android:name="android.permission.CAMERA" /&gt;
      &lt;uses-feature
              android:name="android.hardware.camera"
              android:required="true" /&gt;
  </code></pre>
  
  <p>According to answers from <a href="https://stackoverflow.com/questions/40659198/how-to-access-the-camera-from-within-a-webview">previous question</a> I have this code in class WebViewHelper</p>
  
  <pre><code>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 -&gt;
                          dialog.dismiss()
                          webkitPermissionRequest.grant(arrayOf(PermissionRequest.RESOURCE_VIDEO_CAPTURE))
                          Log.d(TAG, "Granted")
                      }
                      .setNegativeButton("Deny") { dialog, which -&gt;
                          dialog.dismiss()
                          webkitPermissionRequest.deny()
                          Log.d(TAG, "Denied")
                      }
                  val alertDialog = alertDialogBuilder.create()
                  alertDialog.show()
                  break
              }
          }
      }
      ...
  }
  </code></pre>
  
  <p>Logs:
  After the permission is granted, it is requested again (multiple times)</p>
  
  <pre><code>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
  </code></pre>
  
  <p>This seems to be the problem</p>
  
  <pre><code>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"
  </code></pre>
  
  <p>Finally, this error is repeated (indefinitely)</p>
  
  <pre><code>I/GRALLOC: LockFlexLayout: baseFormat: 11, yStride: 640, ySize: 307200, uOffset: 307200,  uStride: 640
  </code></pre>
  
  <h3>JavaScript Code</h3>
  
  <p>The code on the JS side works perfectly fine until opened in WebView.</p>
  
  <pre><code>    app.ports.scanQR.subscribe(() =&gt; {
          // 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 =&gt; returnResult(result));
          scanner.start();
          }, 50);
      });
  }
  </code></pre>
  
  <p>Issue persist if I call getUserMedia directly</p>
  
  <pre><code>if (navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices.getUserMedia({ video: true })
      .then(function (stream) {
          video.srcObject = stream;
      })
      .catch(function (err0r) {
          console.log("Something went wrong!");
      });
  }
  </code></pre>
  
  <h3>Getting the App permission for the camera:</h3>
  
  <p>In WebviewHelper.kt</p>
  
  <pre><code>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")
          }
      ...
  }
  </code></pre>
  
  <p>In MainActivity.kt</p>
  
  <pre><code>override fun onRequestPermissionsResult(requestCode: Int, permissions: Array&lt;String&gt;, grantResults: IntArray) {
          when (requestCode) {
              cameraRequestCode -&gt; {
                  Log.d("MainActivity", "onRequestPermissionsResult: Camera Request")
                  if ((grantResults.isNotEmpty() &amp;&amp; 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
              }
  ...
  </code></pre>
  
  <p>Which result in the following log as expected, when App permission for the camera is granted:</p>
  
  <pre><code>D/MainActivity: onRequestPermissionsResult: Camera Request
      Camera Request: Permission granted
  </code></pre>