参考资料:
深坑之Webview,解决H5调用android相机拍照和录像
除了没有适配6.0以上的动态申请以及拦截h5的方法不同,其余均参考以上文章
因为app和银行合作,在banner页投放了银行调用摄像进行人脸识别的功能,发现点击拉起摄像的功能无效,但是在内置浏览器却可以,由此怀疑需要对webview进行相关适配处理。后来查阅多方文档,发现上述文档比较详尽,同时总结下我自己的理解:
- 在h5页面调用原生的相机,自己的产品开发过程平时一般都是和前端沟通好,会在本地进行url的拦截,根据url判断。但是如果前端页面链接不含有特定字段同时都是js调用怎么办呢?所以如果业务需要,是要在webview里进行适配操作,就是复写下面提到的几个方法,说白了就是前端调用完相机,我们要复写下面的方法,并将原生相机的回调,图片的回调通过ValueCallback回调给webview,然后前端才可以通过方法接收到回调的参数进行校验。这样我们要做的事情就很简单了。根据版本不同复写方法,并且传入获取图片以后的回调即可。
这种问题解决办法在于复写openFileChoosers和onShowFileChoosers的方法:
可以先看下介绍
[Android开发深入理解WebChromeClient之onShowFileChooser或openFileChooser使用说明]
(http://teachcourse.cn/2224.html)
在所有发布的SDK版本中,openFileChooser是一个隐藏的方法,使用onShowFileChooser代替,但是最好同时复写WebChromeClient类里的showFileChooser和openFileChooser方法,Android 4.4.X以上的系统回调onShowFileChooser方法,低于或等于Android 4.4.X的系统回调openFileChooser方法,只重写onShowFileChooser或openFileChooser造成在有的系统可以正常回调,在有的系统点击没有反应。
1、前端调用摄像头的代码
前端调用摄像头拍照
<input type="file" accept="image/*" capture="camera">
前端调用摄像头录像
<input type="file" accept="video/*" capture="camera">
一般第三方的h5调用原生的摄像都是这两个方法,我们可以通过拦截accept字段来进行判断(openFileChooser的复写方法里提供了),当然,如果链接里有其他字段,也可以通过onShouldOverrideUrlLoading进行链接的字段拦截。
2、复写方法
@Override
protected void openFileChoosers(ValueCallback<Uri> valueCallback) {
LogUtil.e("czz", "运行方法 onShowFileChooser");
mUploadMessage = valueCallback;
take();
}
@Override
protected void openFileChoosers(ValueCallback<Uri> valueCallback, String acceptType) {
LogUtil.e("czz", "运行方法 onShowFileChooser2");
mUploadMessage = valueCallback;
mAcceptType = acceptType;
take();
}
@Override
protected void openFileChoosers(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
LogUtil.e("czz", "运行方法 onShowFileChooser3");
mUploadMessage = valueCallback;
mAcceptType = acceptType;
take();
}
@Override
protected void onShowFileChoosers(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
LogUtil.e("czz", "运行方法 onShowFileChooser4");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (fileChooserParams.getAcceptTypes().length > 0) {
mAcceptType = fileChooserParams.getAcceptTypes()[0];
}
}
mUploadCallbackAboveL = filePathCallback;
take();
}
/**
* 拍照
*/
private void take() {
/*打开摄像头拍照通水进行动态权限申请,权限判定是自己封装的方法,仅供参考。*/
RxPermissions rxPermissions = new RxPermissions(this);
rxPermissions.request(Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE)
.subscribe(aBoolean -> {
if (aBoolean) {
try {
if ("image/*".equals(mAcceptType)) {
takePhoto();
}
/*打开摄像头录像*/
if ("video/*".equals(mAcceptType)) {
recordVideo();
}
} catch (Exception e) {
ApToast.showLong(mContext, mContext.getString(R.string.run_camera_error));
}
}
});
}
/**
* 摄像
*/
private void takePhoto() {
File fileUri = new File(Environment.getExternalStorageDirectory().getPath() + "/" + SystemClock.currentThreadTimeMillis() + ".jpg");
imageUri = Uri.fromFile(fileUri);
if (Build.VERSION.SDK_INT >= 24) {
imageUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", fileUri);//通过FileProvider创建一个content类型的Uri
}
takePicture(this, imageUri, PHOTO_REQUEST);
}
/**
* 录像
*/
private void recordVideo() {
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
//限制时长
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10);
//开启摄像机
startActivityForResult(intent, VIDEO_REQUEST);
}
public static void takePicture(Activity activity, Uri imageUri, int requestCode) {
//调用系统相机
Intent intentCamera = new Intent();
if (Build.VERSION.SDK_INT >= 24) {
intentCamera.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时授权该Uri所代表的文件
}
intentCamera.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
//将拍照结果保存至photo_file的Uri中,不保留在相册中
intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
if (activity != null) {
activity.startActivityForResult(intentCamera, requestCode);
}
}
回调处理
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
LogUtil.e("requestCode = " + requestCode + "\n" + "resultCode = " + resultCode);
if (requestCode == PHOTO_REQUEST) {
LogUtil.e("PHOTO_REQUEST1");
if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
if (mUploadCallbackAboveL != null) {
LogUtil.e("PHOTO_REQUEST2");
onActivityResultAboveL(requestCode, resultCode, data);
} else if (mUploadMessage != null) {
LogUtil.e("PHOTO_REQUEST3");
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
} else if (requestCode == VIDEO_REQUEST) {
if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
if (mUploadCallbackAboveL != null) {
if (resultCode == RESULT_OK) {
mUploadCallbackAboveL.onReceiveValue(new Uri[]{result});
mUploadCallbackAboveL = null;
} else {
mUploadCallbackAboveL.onReceiveValue(new Uri[]{});
mUploadCallbackAboveL = null;
}
} else if (mUploadMessage != null) {
if (resultCode == RESULT_OK) {
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
} else {
mUploadMessage.onReceiveValue(Uri.EMPTY);
mUploadMessage = null;
}
}
}
}
//这个回调主要是处理5.0以上的回调上传
private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) {
if (requestCode != PHOTO_REQUEST || mUploadCallbackAboveL == null) {
LogUtil.e("onActivityResultAboveL1");
return;
}
Uri[] results = null;
if (resultCode == Activity.RESULT_OK) {
if (data == null) {
LogUtil.e("onActivityResultAboveL2");
results = new Uri[]{imageUri};
} else {
LogUtil.e("onActivityResultAboveL3");
String dataString = data.getDataString();
ClipData clipData = data.getClipData();
if (clipData != null) {
results = new Uri[clipData.getItemCount()];
for (int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i);
results[i] = item.getUri();
}
}
if (dataString != null)
results = new Uri[]{Uri.parse(dataString)};
}
}
LogUtil.e("onActivityResultAboveL1" + results.toString());
mUploadCallbackAboveL.onReceiveValue(results);
mUploadCallbackAboveL = null;
}