AndroidのWebViewの「ファイルを選択」ボタン(input typ=”file”)で写真を撮るかファイルを選択する

AndroidのWebViewで「ファイルを選択」ボタン(input typ=”file”)を押した時に、カメラアプリかファイルアプリから画像をアップロードする方法。

対象はAndroid5以上。

HTMLでは、「ファイルを選択」ボタンで選択された画像をimgタグに表示する。

<input type="file" accept="image/*" capture="camera" id="camera">
<img id="frame">
<script>
const camera = document.getElementById('camera');
const frame = document.getElementById('frame');
camera.addEventListener('change', function(e) {
    console.log(e.target.files);
    if (e.target.files.length > 0) {
        frame.src = URL.createObjectURL(e.target.files[0]);
    } else {
        frame.src = '';
    }
});
</script>

このHTMLをAndroidのWebViewで表示して、選択した画像を表示できるようにする。

以下、Androidアプリの作成手順。

AndroidManifest.xmlにインターネットとカメラの権限を追加する。

<manifest ... >
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CAMERA" />
    ...
</manifest>

Android9以上で、HTTP通信(http://〜)をする場合は、AndroidManifest.xmlに「android:usesCleartextTraffic=”true”」を追加する。


<application android:usesCleartextTraffic="true"

カメラアプリで撮影した写真はFileProviderを使って共有するため、FileProviderを設定する。

<application
    ...>
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_path"/>
    </provider>

app/src/main/res/xml/provider_path.xmlを作成する。

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

カメラの撮影に必要な権限を要求するクラスを作成する。

public class CameraPermission {
    /**
     * 権限があるか確認し、権限がなければ要求する
     *
     * @return 権限があるときはtrue
     */
    public static boolean checkAndRequestPermissions(final @NonNull Activity activity, final @IntRange(from = 0) int requestCode) {
        String[] permissionNeeded = permissionNeeded(activity, new String[]{
                Manifest.permission.CAMERA,
        });
        if (permissionNeeded.length > 0) {
            ActivityCompat.requestPermissions(activity, permissionNeeded, requestCode);
            return false;
        }
        return true;
    }

    private static String[] permissionNeeded(final @NonNull Activity activity, String[] permissions) {
        List<String> listPermissionsNeeded = new ArrayList<>();
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
                listPermissionsNeeded.add(permission);
            }
        }
        return listPermissionsNeeded.toArray(new String[0]);
    }
}

MainActivityにWebViewを表示するコードを追加する。
「ファイルを選択」ボタンが押された時、showFileChooser()メソッドを呼ぶ。

public class MainActivity extends AppCompatActivity {

    private ValueCallback<Uri[]> filePathCallback;

    @SuppressLint("SetJavaScriptEnabled")
    private void setupWebView() {
        WebView webView = new WebView(this);
        setContentView(webView);
        webView.loadUrl("http://xxx.xxx.xxx.xxx/");
        // JavaScriptを有効にする
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebChromeClient(new WebChromeClient() {
            /**
             * 「ファイルを選択」ボタンが押された時
             * For Android > 5.0
             * @param webView
             * @param filePathCallback
             * @param fileChooserParams
             * @return
             */
            @TargetApi(Build.VERSION_CODES.LOLLIPOP)
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
                showFileChooser(filePathCallback, fileChooserParams);
                return true;
            }
        });
    }
}

「ファイルを選択」ボタンが押された時の処理。
権限がなければ権限を要求する。
カメラアプリとファイルアプリを選択するインテントを作成する。

private void showFileChooser(ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
    // 完了していない処理があれば完了する
    if (this.filePathCallback != null) {
        this.filePathCallback.onReceiveValue(null);
    }
    this.filePathCallback = filePathCallback;

    // 権限がないときは、権限を要求する
    if (!CameraPermission.checkAndRequestPermissions(this, REQUEST_PERMISSIONS)) {
        this.filePathCallback.onReceiveValue(null);
        this.filePathCallback = null;
        return;
    }

    // カメラとファイルのインテントを作成する
    Intent chooserIntent = Intent.createChooser(fileChooserParams.createIntent(), "写真の選択");
    try {
        mImageUri = createImageFile();
        Intent imageCaptureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        imageCaptureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{imageCaptureIntent});
    } catch (IOException ex) {
        mImageUri = null;
    }
    startActivityForResult(chooserIntent, REQUEST_SELECT_FILE_CODE);
}

撮影した写真を保存するファイルのURIを作成するメソッドを作成する。

private Uri createImageFile() throws IOException {
    File folder = getExternalFilesDir(Environment.DIRECTORY_DCIM);
    String date = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
    String fileName = String.format("MyApp_%s.jpg", date);
    File cameraFile = new File(folder, fileName);

    return FileProvider.getUriForFile(
            this,
            getApplicationContext().getPackageName() + ".fileprovider",
            cameraFile);
}

選択されたファイルをWebViewに返す。

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == REQUEST_SELECT_FILE_CODE) {
        if (filePathCallback != null) {
            if (resultCode == RESULT_OK) {
                Uri[] result = WebChromeClient.FileChooserParams.parseResult(resultCode, data);
                if (result == null) {
                    result = new Uri[]{mImageUri};
                }
                filePathCallback.onReceiveValue(result);
            } else {
                filePathCallback.onReceiveValue(null);
            }
            filePathCallback = null;
        }
    }
}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください