基于上一次对Android Hybrid开发中WebView选择图片上传的探讨,我们可以扩展一下需求,假如产品提出要直接从前端调起Android相机拍照呢?Google了下HTML5有没有相关的API,找到一个国外开发者提到的解决方案:

Capturing Audio & Video in HTML5

<input type="file" accept="image/*;capture=camera">
<input type="file" accept="video/*;capture=camcorder">
<input type="file" accept="audio/*;capture=microphone">

  如文中所言,这仅仅是作者自己提议的解决方案,不是HTML5官方认可的API。抱着试一试的心态,通过chrome调试修改html页面,新增上述的input标签,然后在Android端拦截上文(Android Hybrid – WebView上传文件图片)中提到的WebChromeClient#onShowFileChooser接口,查看传递过来的参数。这下好了,accept所有参数都没传递过来…

前端传递参数

  所以,前端正确的解决方案是怎样的?经过一翻摸索,找到了正确的姿势:

<input type="file" accept="image/*" capture="camera">
<!-- 或者 -->
<input type="file" accept="image/*" capture>

  再次通过chrome调试查看WebChromeClient#onShowFileChooser接口接收到的参数,这次accept参数可以接收到了,那么capture参数怎么接收呢?

Android端接收参数

  上文(Android Hybrid – WebView上传文件图片)中提到的WebChromeClient#onShowFileChooser接口参数WebChromeClient.FileChooserParams,我们来一窥庐山真面目(为方便浏览已删减大部分):

/**
 * Parameters used in the {@link #onShowFileChooser} method.
 */
public static abstract class FileChooserParams {

    ...

    /**
     * Returns an array of acceptable MIME types. The returned MIME type
     * could be partial such as audio/*. The array will be empty if no
     * acceptable types are specified.
     */
    public abstract String[] getAcceptTypes();

    /**
     * Returns preference for a live media captured value (e.g. Camera, Microphone).
     * True indicates capture is enabled, false disabled.
     *
     * Use <code>getAcceptTypes</code> to determine suitable capture devices.
     */
    public abstract boolean isCaptureEnabled();

    ...
}

  原来webkit内核已经帮我们做好了参数解析和封装。值得注意的是,capture参数在Android端是boolean类型的,即使前端写成capture=”camera”,其实是Android端也拿不到字符串,这也是为什么上面我们提到前端写成capture也是可以工作的原因。

Android处理拍照请求

  跟选择图片文件一样,拍照也需要开发者自己调起。拍照保存的文件路径需要提前定义好,传递给拍照应用。

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
    // Create the File where the photo should go
    File photoFile = null;
    try {
        File cacheDir = new File(FileUtil.WALLET_IMAGE_CACHE_DIR);
        if (!cacheDir.exists()) {
            //noinspection ResultOfMethodCallIgnored
            cacheDir.mkdirs();
        }
        photoFile = File.createTempFile(FileUtil.generateCacheFilePrefix(), FileUtil.IMAGE_FILE_SUFFIX_JPG, cacheDir);
    } catch (IOException ex) {
        // Error occurred while creating the File
        ex.printStackTrace();
    }
    // Continue only if the File was successfully created
    if (photoFile != null) {
        capturedPhotoUri = Uri.fromFile(photoFile);
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, capturedPhotoUri);
        startActivityForResultSafe(takePictureIntent, START_ACTIVITY_UPLOAD_FILE_REQUEST);
    }
}

前置摄像头

  有些时候,需要直接调起自拍模式,笔者尝试了下,在魅族手机上是可以实现,其他厂商的手机暂未测试:

takePictureIntent.putExtra("android.intent.extras.CAMERA_FACING", android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT);
takePictureIntent.putExtra("android.intent.extras.LENS_FACING_FRONT", 1);
takePictureIntent.putExtra("android.intent.extra.USE_FRONT_CAMERA", true);