我想通过Android WebView在PWA中使用QR码扫描仪。
minSdkVersion 26
和targetSdkVersion 28
问题是似乎未授予该权限。此外,权限请求会重复多次。
清单
<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" />
根据previous question的回答,我在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
}
}
}
...
}
日志: 授予权限后,将再次请求(多次)
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
这似乎是问题所在
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"
最后,无限期地重复此错误
I/GRALLOC: LockFlexLayout: baseFormat: 11, yStride: 640, ySize: 307200, uOffset: 307200, uStride: 640
在WebView中打开之前,JS端的代码可以正常工作。
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);
});
}
如果我直接致电getUserMedia,问题仍然存在
if (navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({ video: true })
.then(function (stream) {
video.srcObject = stream;
})
.catch(function (err0r) {
console.log("Something went wrong!");
});
}
在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")
}
...
}
在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
}
...
在授予摄像机“应用程序”权限后,将按预期结果显示以下日志:
D/MainActivity: onRequestPermissionsResult: Camera Request
Camera Request: Permission granted
答案 0 :(得分:0)
由于跨源角色,我可以使其在stackoverflow上工作。试试这个,必须工作。
const captureVideoButton = document.querySelector('#capture-button');
const screenshotButton = document.querySelector('#screenshot-button');
const img = document.querySelector('#image');
const video = document.querySelector('#video');
const canvas = document.querySelector('#canvas');
const constraints = {
audio: true,
video: true
};
captureVideoButton.onclick = function() {
navigator.mediaDevices.getUserMedia(constraints).
then(handleSuccess).catch(handleError);
};
screenshotButton.onclick = video.onclick = function() {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0);
// Other browsers will fall back to image/png
img.src = canvas.toDataURL('image/webp');
};
function handleError(error) {
console.error('Error: ', error);
}
function handleSuccess(stream) {
screenshotButton.disabled = false;
video.srcObject = stream;
}
<meta http-equiv="Content-Security-Policy" content="default-src * gap:; script-src * 'unsafe-inline' 'unsafe-eval'; connect-src *; img-src * data: blob: android-webview-video-poster:; style-src * 'unsafe-inline';">
<video id="video" autoplay crossorigin="anonymous"></video>
<img id="image" src="" crossorigin="anonymous">
<canvas id="canvas" style="display:none;"></canvas>
<button id="capture-button">Capture video</button>
<button id="screenshot-button">Capture video</button>
也尝试添加meta http-equiv =“ Content-Security-Policy”标签
答案 1 :(得分:0)
我使用本机QR扫描仪模块解决了此问题。
但是,我最终还需要访问Geolocation。所以我找到了这个解决方案: WebViewActivity with permission request
我只需要以下部分来请求权限:
此外,您还需要一些提示功能和变量以及一个清单权限条目。
由于此方法适用于地理位置,因此我认为如果进行了相应的调整,它也应适用于Camera。
科特林版本(简称):
// in webview settings add
webSettings.apply {
setGeolocationEnabled(true)
}
webView.webChromeClient = object : WebChromeClient() {
override fun onGeolocationPermissionsShowPrompt(
origin: String,
callback: GeolocationPermissions.Callback
) {
// Always grant permission since the app itself requires location
// permission and the user has therefore already granted it
callback.invoke(origin, true, false)
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS -> {
val perms = HashMap<String, Int>()
// Initial
perms[Manifest.permission.ACCESS_FINE_LOCATION] = PackageManager.PERMISSION_GRANTED
// Fill with results
for (i in permissions.indices)
perms[permissions[i]] = grantResults[i]
// Check for ACCESS_FINE_LOCATION
if (perms[Manifest.permission.ACCESS_FINE_LOCATION] == PackageManager.PERMISSION_GRANTED) {
// All Permissions Granted
} else {
// Permission Denied
finish()
}
}
else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
@TargetApi(Build.VERSION_CODES.M)
internal fun fuckMarshMallow() {
val permissionsNeeded = ArrayList<String>()
val permissionsList = ArrayList<String>()
if (!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION))
permissionsNeeded.add("Show Location")
if (permissionsList.size > 0) {
if (permissionsNeeded.size > 0) {
// Need Rationale
var message = "App need access to " + permissionsNeeded[0]
for (i in 1 until permissionsNeeded.size)
message = message + ", " + permissionsNeeded[i]
showMessageOKCancel(message,
DialogInterface.OnClickListener { _, _ ->
requestPermissions(
permissionsList.toTypedArray(),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS
)
})
return
}
requestPermissions(
permissionsList.toTypedArray(),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS
)
return
}
}
private fun showMessageOKCancel(message: String, okListener: DialogInterface.OnClickListener) {
AlertDialog.Builder(this@WebViewActivity)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", null)
.create()
.show()
}
@TargetApi(Build.VERSION_CODES.M)
private fun addPermission(permissionsList: MutableList<String>, permission: String): Boolean {
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permission)
// Check for Rationale Option
if (!shouldShowRequestPermissionRationale(permission))
return false
}
return true
}
不要忘记清单:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
答案 2 :(得分:0)
您所有的原始代码在onPermissionRequest(...)
内都是正确的。您缺少的关键代码行是webView.settings.mediaPlaybackRequiresUserGesture = false
。