もう出ないなんて言わせないWebViewでファイルアップロードするダイアログの表示


この記事は[Android Advent Calendar 2015](http://qiita.com/advent-calendar/2015/android)の9日目です！！

## 追記
4.4.0~4.4.2では動かないようです
http://qiita.com/futabooo/items/4706a0767c8ed2330752#comment-47a26f717450647feaaf

完全にタイトル詐欺でした。

力技で動かす方法もあるようです
http://qiita.com/futabooo/items/4706a0767c8ed2330752#comment-e8ed2bd94273846f686e

## はじめに
AndroidのWebViewは癖があって色々苦労することが多いなぁといつも思っています。先日も知見が多く得られるエントリーがあって、とても参考になりました。
[Android エンジニアが Android の WebView で苦しんだ話]
(http://qiita.com/akitaika_/items/654dafa26e1d1dd0d3dd)

今回は上記の中では触れられていなかったファイルのアップロードで自分がハマったので、その解決策と共にこれだけ書いとけば大丈夫！（なはず＼(^o^)／）
という内容を紹介したいと思います。
最後にサンプルプログラムへのリンクも用意しましたので、実装と動作だけちゃっちゃと確認したい方はそちらをごらんください。

## 実装について
ネットワークの処理を省くために、動作を確認するためのHTMLをassetsに用意しました。
WebViewで下記の内容を表示します。

```HTML:sample.html
<!DOCTYPE html>
<form method="post" action="example.cgi" enctype="multipart/form-data">

    <input type="file" name="ファイル選択"></p>

    <p><input type="submit" value="送信する"></p>

</form>
```

上記のHTMLでファイル選択が押された時に呼ばれるコールバックを宣言している部分を抜き出しました。
Lollipopではメソッド名が変わっています。

```Java:MainActivity.java
  private WebChromeClient MyWebChromeClient() {
    return new WebChromeClient() {
      // For Android < 3.0
      public void openFileChooser(ValueCallback<Uri> uploadFile) {
        openFileChooser(uploadFile, "");
      }

      // For 3.0 <= Android < 4.1
      public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType) {
        openFileChooser(uploadFile, acceptType, "");
      }

      // For 4.1 <= Android < 5.0
      public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
        if(mUploadMessage != null){
          mUploadMessage.onReceiveValue(null);
        }
        mUploadMessage = uploadFile;

        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType(TYPE_IMAGE);

        startActivityForResult(intent, INPUT_FILE_REQUEST_CODE);
      }

      // For Android 5.0+
      @Override public boolean onShowFileChooser(WebView webView,
          ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
        if (mFilePathCallback != null) {
          mFilePathCallback.onReceiveValue(null);
        }
        mFilePathCallback = filePathCallback;

        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType(TYPE_IMAGE);
        startActivityForResult(intent, INPUT_FILE_REQUEST_CODE);

        return true;
      }
    };
  }
```

ファイル選択ダイアログでファイルを選択すると、ダイアログが閉じるタイミングで`onActivityResult`が呼ばれます。
この時もLollipop以上は場合分けをしています。
ファイルは`contents://hoge`の形で渡されます。

```Java:MainActivity.java
  @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode != INPUT_FILE_REQUEST_CODE) {
      super.onActivityResult(requestCode, resultCode, data);
      return;
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      if (mFilePathCallback == null) {
        super.onActivityResult(requestCode, resultCode, data);
        return;
      }
      Uri[] results = null;

      // Check that the response is a good one
      if (resultCode == RESULT_OK) {
        String dataString = data.getDataString();
        if (dataString != null) {
          results = new Uri[] { Uri.parse(dataString) };
        }
      }

      mFilePathCallback.onReceiveValue(results);
      mFilePathCallback = null;
    } else {
      if (mUploadMessage == null) {
        super.onActivityResult(requestCode, resultCode, data);
        return;
      }

      Uri result = null;

      if (resultCode == RESULT_OK) {
        if (data != null) {
          result = data.getData();
        }
      }

      mUploadMessage.onReceiveValue(result);
      mUploadMessage = null;
    }
  }
```


## 動作が確認できた端末
下記端末でファイル選択のダイアログが表示されることを確認できています。
4.4だとopenFileChooserが呼ばれないという情報もあったのですが、動いてました。
もしこれを読んだ方がお持ちの端末でダイアログが表示されるかどうか確認してくれた方がいたら、編集リクエスト頂いたら端末追加します＼(^o^)

- F-10D 4.0.3
- SC-04D 4.2.2
- Nexus5 4.4.4
- Nexus4 5.1
- Nexus5 6.0
- Nexus9 6.0

## 終わりに
ファイル選択のダイアログはこちらのコードで問題なく表示されるようになりました！

しかし要件によっては画像選択だけにしたい時や、テキストファイルの選択だけにしたいとかもあるのではないかと思います。
例えばお問い合わせで画面キャプチャを添付してもらいたい時などは画像ファイルだけの選択ダイアログがでてほしいですよね。
そんな時はIntentへ渡すフラグを変えることで対応できるようです！
僕はこちらの記事を参考にしました！
[Android 2系~5系で動く、intentでの画像取得にハマった（起動アプリ調整）](http://qiita.com/kozyty@github/items/876529226274fc343bcf)


## 課題 
### 4.4だけで起きる現象
ファイル選択ダイアログは4.4でも表示されたのですが、`onActivityResult`でファイルを渡した時に`contents://hoge`のURLではファイル名がうまく表示されませんでした。。。
解決法わかったら更新します！

## サンプルプロジェクト
https://github.com/futabooo/FileUploadTest

## 参考資料
[How can I upload a files using WebView in Android Lollipop?](https://www.quora.com/How-can-I-upload-a-files-using-WebView-in-Android-Lollipop)