Android WebView拍照和上传文件

1. 前言

拍照上传或者上传图片、视频、文档等文件,这种需求对于移动端来说是很基本的。Android原生实现这种需求没什么特别大的难度。但如果是嵌套的H5页面的话,就需要踩点坑了。Android 的这个 WebView 真是个不让人省心的控件啊!

2. 解决方案

废话不多说,直接上关键代码。首先,重写 WebView 的 WebChromeClient 中的 openFileChooser() 方法。

mWebView.setWebChromeClient(new WebChromeClient() {

    // For Android < 3.0
    public void openFileChooser(ValueCallback<Uri> uploadMsg) {
        mAcceptType = "*/*";
        toOpenFileChooser(uploadMsg);
    }

    // For Android >= 3.0
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
        if (TextUtils.isEmpty(acceptType)) {
            mAcceptType = "*/*";
        }
        mAcceptType = acceptType;
        toOpenFileChooser(uploadMsg);
    }

    // For Android >= 4.1.1
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        if (TextUtils.isEmpty(acceptType)) {
            mAcceptType = "*/*";
        }
        mAcceptType = acceptType;
        toOpenFileChooser(uploadMsg);
    }

    // For Android >= 5.0
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
        String[] acceptTypes = fileChooserParams.getAcceptTypes();
        if (acceptTypes.length > 0) {
            if (TextUtils.isEmpty(acceptTypes[0])) {
                mAcceptType = "*/*";
            } else {
                mAcceptType = acceptTypes[0];
            }
        } else {
            mAcceptType = "*/*";
        }
        toOpenFileChooserAnother(filePathCallback);
        return true;
    }
});

Android的碎片化真严重,得重写4个方法,但根据参数的情况,只有2种处理方案。这里其实有一个大坑,就是Android 5.0以下的部分设备,会触发不了 openFileChooser() 方法。而且这个大坑没有任何解决方法,好在现在都2020年了,大多数人用的系统都是5.0以上的。

toOpenFileChooser(uploadMsg) 和 toOpenFileChooserAnother(filePathCallback) 这两个方法的具体代码如下所示:

private static final int REQUEST_CODE_FILE_CHOOSER = 0;
private static final int REQUEST_CODE_FILE_CHOOSER_ANOTHER = 1;
/**
 * 拍照上传的请求码
 */
private static final int REQUEST_CODE_PHOTO_UPLOAD = 3;

/**
 * 打开文件选择器,适用于Android 5.0 以下
 *
 * @param uploadMsg 加载文件的信息
 */
private void toOpenFileChooser(ValueCallback<Uri> uploadMsg) {
    mUploadMessage = uploadMsg;
    Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    i.addCategory(Intent.CATEGORY_OPENABLE);
    i.setType(mAcceptType);
    startActivityForResult(Intent.createChooser(i, getString(R.string.choose_app)), REQUEST_CODE_FILE_CHOOSER);
}

/**
 * 打开文件选择器,适用于Android 5.0 及以上
 *
 * @param uploadMsg 加载文件的信息
 */
private void toOpenFileChooserAnother(ValueCallback<Uri[]> uploadMsg) {
    mUploadMessageAnother = uploadMsg;
    if ((mAcceptType.equals("*/*") || mAcceptType.contains("image"))
      && ContextCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) == PackageManager.PERMISSION_DENIED) {
        long oldTime = PreferencesUtils.getLong(mContext, Constant.PREF_REQUEST_CAMERA_TIME);
        long newTime = System.currentTimeMillis();

        if (oldTime == -1) { // 首次要提示申请权限
            PreferencesUtils.putLong(mContext, Constant.PREF_REQUEST_CAMERA_TIME, newTime);
            toRequestCameraPermission();
            return;
        } else {
            if (newTime - oldTime >= 7 * 24 * 60 * 60 * 1000) { // 7天后再提示申请权限
                PreferencesUtils.putLong(mContext, Constant.PREF_REQUEST_CAMERA_TIME, newTime);
                toRequestCameraPermission();
                return;
            }
        }
    }
    toOpenFileChooserAnother();
}

/**
 * 打开文件选择器,适用于Android 5.0 及以上
 */
private void toOpenFileChooserAnother() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if ((mAcceptType.equals("*/*") || mAcceptType.contains("image"))
        && takePictureIntent.resolveActivity(mContext.getPackageManager()) != null) {
        File photoFile = null;
        try {
            if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
                String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date());
                String imageFileName = "PIC_" + timeStamp + "_";
                File storageDir = new File(DirUtils.getPictures());
                if (!storageDir.exists()) {
                    storageDir.mkdirs();
                }
                photoFile = File.createTempFile(imageFileName, ".jpg", storageDir);
            }
        } catch (Exception e) {
            Log.d(Constant.TAG, mClassName + " Unable to create Image File", e);
        }

        if (photoFile != null) {
            mCameraPhotoPath = photoFile.getAbsolutePath();
            Log.d(Constant.TAG, "图片路径 : " + mCameraPhotoPath);
            takePictureIntent.putExtra("PhotoPath", "file:" + mCameraPhotoPath);
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
        } else {
            takePictureIntent = null;
        }
    } else {
        takePictureIntent = null;
    }

    Intent[] intentArray;
    if (takePictureIntent != null) {
        intentArray = new Intent[]{takePictureIntent};
    } else {
        intentArray = new Intent[0];
    }

    Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
    contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
    contentSelectionIntent.setType(mAcceptType);

    Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
    chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
    chooserIntent.putExtra(Intent.EXTRA_TITLE, getString(R.string.choose_app));
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);

    startActivityForResult(chooserIntent, REQUEST_CODE_FILE_CHOOSER_ANOTHER);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    switch (requestCode) {
        case REQUEST_CODE_FILE_CHOOSER:
            if (mUploadMessage == null) {
                return;
            }
            Uri result = (intent == null || resultCode != RESULT_OK) ? null : intent.getData();
            mUploadMessage.onReceiveValue(result);
            mUploadMessage = null;
            break;
        case REQUEST_CODE_FILE_CHOOSER_ANOTHER: // Android 5.0 及以上
            if (mUploadMessageAnother == null) {
                return;
            }

            Uri[] results = null;
            if (resultCode == RESULT_OK) {
                if (intent != null) {
                    results = new Uri[]{intent.getData()};
                } else {
                    if (mCameraPhotoPath != null) {
                        results = new Uri[]{Uri.parse("file:" + mCameraPhotoPath)};
                    }
                }
            } else {
                if (!TextUtils.isEmpty(mCameraPhotoPath)) {
                    File file = new File(mCameraPhotoPath);
                    file.delete();
                }
            }

            mUploadMessageAnother.onReceiveValue(results);
            mUploadMessageAnother = null;
            break;
        case REQUEST_CODE_PHOTO_UPLOAD:
            toOpenFileChooserAnother();
            break;
        default:
            super.onActivityResult(requestCode, resultCode, intent);
            break;
    }
}

拍照上传,是需要申请“相机权限”的,上面的 toRequestCameraPermission() 方法就是申请权限的,具体代码我就不贴出来了。如果想看完整的demo,可以查看我放在GitHub上面的项目 AndroidWebView

如果想进一步交流和学习的同学,可以加一下QQ群哦!

发布了49 篇原创文章 · 获赞 43 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/Fantasy_Lin_/article/details/104447253