Flutter webView增加图片读取功能
背景
有个需求是需要外链一个url进行操作,需要用户进行身份证的扫描读取操作,但是我们目前使用的webview并没有支持这个,所以我们需要在原有的基础上增加这个功能
工具类封装
package io.flutter.plugins.webviewflutter;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import androidx.core.content.FileProvider;
import java.io.File;
public class WebCameraHelper {
private static class SingletonHolder {
static final WebCameraHelper INSTANCE = new WebCameraHelper();
}
public static WebCameraHelper getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* 图片选择回调
*/
public ValueCallback<Uri> mUploadMessage;
public ValueCallback<Uri[]> mUploadCallbackAboveL;
public Uri fileUri;
public static final int TYPE_REQUEST_PERMISSION = 3;
public static final int TYPE_CAMERA = 1;
public static final int TYPE_GALLERY = 2;
public static final int TYPE_VIDEO = 3;
/**
* 包含拍照和相册选择
*/
public void showOptions(final Activity act) {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(act);
alertDialog.setOnCancelListener(new ReOnCancelListener());
alertDialog.setTitle("选择");
alertDialog.setItems(new CharSequence[]{"相机", "相册"},
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
takePhoto(act);
} else {
Intent i = new Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);// 调用android的图库
act.startActivityForResult(i,
TYPE_GALLERY);
}
}
});
alertDialog.show();
}
public void recordVideo(final Activity activity) {
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
//设置视频质量
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
//设置视频时长
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10);
//开启摄像机
activity.startActivityForResult(intent, TYPE_VIDEO);
}
private void takePhoto(Activity context) {
// 步骤一:创建存储照片的文件
String path = context.getFilesDir() + File.separator + "images" + File.separator;
File file = new File(path, "TestBean.jpg");
if (!file.getParentFile().exists())
file.getParentFile().mkdirs();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//步骤二:Android 7.0及以上获取文件 Uri
fileUri = FileProvider.getUriForFile(context, "io.flutter.plugins.webviewflutter.provider", file);
} else {
//步骤三:获取文件Uri
fileUri = Uri.fromFile(file);
}
//步骤四:调取系统拍照
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
context.startActivityForResult(intent, TYPE_CAMERA);
}
/**
* 点击取消的回调
*/
private class ReOnCancelListener implements
DialogInterface.OnCancelListener {
@Override
public void onCancel(DialogInterface dialogInterface) {
if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(null);
mUploadMessage = null;
}
if (mUploadCallbackAboveL != null) {
mUploadCallbackAboveL.onReceiveValue(null);
mUploadCallbackAboveL = null;
}
}
}
/**
* startActivityForResult之后要做的处理
*
* @param requestCode
* @param resultCode
* @param intent
*/
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == TYPE_CAMERA) { // 相册选择
if (resultCode == -1) {//RESULT_OK = -1,拍照成功
if (mUploadCallbackAboveL != null) { //高版本SDK处理方法
Uri[] uris = new Uri[]{fileUri};
mUploadCallbackAboveL.onReceiveValue(uris);
mUploadCallbackAboveL = null;
} else if (mUploadMessage != null) { //低版本SDK 处理方法
mUploadMessage.onReceiveValue(fileUri);
mUploadMessage = null;
} else {
//Toast.makeText(CubeAndroid.this, "无法获取数据", Toast.LENGTH_LONG).show();
}
} else { //拍照不成功,或者什么也不做就返回了,以下的处理非常有必要,不然web页面不会有任何响应
if (mUploadCallbackAboveL != null) {
mUploadCallbackAboveL.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent));
mUploadCallbackAboveL = null;
} else if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(fileUri);
mUploadMessage = null;
} else {
//Toast.makeText(CubeAndroid.this, "无法获取数据", Toast.LENGTH_LONG).show();
}
}
} else if (requestCode == TYPE_GALLERY) {// 相册选择
if (mUploadCallbackAboveL != null) {
mUploadCallbackAboveL.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent));
mUploadCallbackAboveL = null;
} else if (mUploadMessage != null) {
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
} else {
// Toast.makeText(CubeAndroid.this, "无法获取数据", Toast.LENGTH_LONG).show();
}
} else if (requestCode == TYPE_VIDEO) {
if (null == mUploadMessage && null == mUploadCallbackAboveL) {
return;
}
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData();
if (mUploadCallbackAboveL != null) {
if (resultCode == Activity.RESULT_OK) {
mUploadCallbackAboveL.onReceiveValue(new Uri[]{result});
mUploadCallbackAboveL = null;
} else {
mUploadCallbackAboveL.onReceiveValue(new Uri[]{});
mUploadCallbackAboveL = null;
}
} else if (mUploadMessage != null) {
if (resultCode == Activity.RESULT_OK) {
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
} else {
mUploadMessage.onReceiveValue(Uri.EMPTY);
mUploadMessage = null;
}
}
}
}
}
项目引用
- 定义一个interface
interface OnFileChoose{
void recordVideo();
void showOptions();
}
- 暴露给FlutterWebView类
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
WebCameraHelper.getInstance().mUploadCallbackAboveL = filePathCallback;
if (isVideo(fileChooserParams)) {
fileChoose.recordVideo();
} else {
fileChoose.showOptions();
}
return true;
}
- 在WebViewFlutterPlugin实现回调处理
/**
* Java platform implementation of the webview_flutter plugin.
*
* <p>Register this in an add to app scenario to gracefully handle activity and context changes.
*
* <p>Call {@link #registerWith(Registrar)} to use the stable {@code io.flutter.plugin.common}
* package instead.
*/
public class WebViewFlutterPlugin implements FlutterPlugin, PluginRegistry.ActivityResultListener, ActivityAware, OnFileChoose {
private FlutterCookieManager flutterCookieManager;
private Activity activity;
/**
* Add an instance of this to {@link io.flutter.embedding.engine.plugins.PluginRegistry} to
* register it.
*
* <p>THIS PLUGIN CODE PATH DEPENDS ON A NEWER VERSION OF FLUTTER THAN THE ONE DEFINED IN THE
* PUBSPEC.YAML. Text input will fail on some Android devices unless this is used with at least
* flutter/flutter@1d4d63ace1f801a022ea9ec737bf8c15395588b9. Use the V1 embedding with {@link
* #registerWith(Registrar)} to use this plugin with older Flutter versions.
*
* <p>Registration should eventually be handled automatically by v2 of the
* GeneratedPluginRegistrant. https://github.com/flutter/flutter/issues/42694
*/
public WebViewFlutterPlugin() {
}
/**
* Registers a plugin implementation that uses the stable {@code io.flutter.plugin.common}
* package.
*
* <p>Calling this automatically initializes the plugin. However plugins initialized this way
* won't react to changes in activity or context, unlike {@link CameraPlugin}.
*/
@SuppressWarnings("deprecation")
public static void registerWith(final io.flutter.plugin.common.PluginRegistry.Registrar registrar) {
registrar
.platformViewRegistry()
.registerViewFactory(
"plugins.flutter.io/webview",
new WebViewFactory(registrar.messenger(), registrar.view(), null));
new FlutterCookieManager(registrar.messenger());
}
@Override
public void onAttachedToEngine(FlutterPluginBinding binding) {
BinaryMessenger messenger = binding.getBinaryMessenger();
binding
.getPlatformViewRegistry()
.registerViewFactory(
"plugins.flutter.io/webview", new WebViewFactory(messenger, /*containerView=*/ null, this));
flutterCookieManager = new FlutterCookieManager(messenger);
}
@Override
public void onDetachedFromEngine(FlutterPluginBinding binding) {
if (flutterCookieManager == null) {
return;
}
flutterCookieManager.dispose();
flutterCookieManager = null;
}
@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
WebCameraHelper.getInstance().onActivityResult(requestCode, resultCode, data);
return false;
}
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
this.activity = binding.getActivity();
binding.addActivityResultListener(this);
}
@Override
public void onDetachedFromActivityForConfigChanges() {
}
@Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
binding.removeActivityResultListener(this);
}
@Override
public void onDetachedFromActivity() {
}
@Override
public void recordVideo() {
WebCameraHelper.getInstance().recordVideo(activity);
}
@Override
public void showOptions() {
WebCameraHelper.getInstance().showOptions(activity);
}
}
事项
记得把相关权限给到,以及Android的filepath