AndroidDay 9

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

More than 3 years have passed since last update.

この記事はAndroid Advent Calendar 2015の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 で苦しんだ話

今回は上記の中では触れられていなかったファイルのアップロードで自分がハマったので、その解決策と共にこれだけ書いとけば大丈夫!(なはず\(^o^)/)

という内容を紹介したいと思います。

最後にサンプルプログラムへのリンクも用意しましたので、実装と動作だけちゃっちゃと確認したい方はそちらをごらんください。


実装について

ネットワークの処理を省くために、動作を確認するためのHTMLをassetsに用意しました。

WebViewで下記の内容を表示します。


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ではメソッド名が変わっています。


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の形で渡されます。


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での画像取得にハマった(起動アプリ調整)


課題


4.4だけで起きる現象

ファイル選択ダイアログは4.4でも表示されたのですが、onActivityResultでファイルを渡した時にcontents://hogeのURLではファイル名がうまく表示されませんでした。。。

解決法わかったら更新します!


サンプルプロジェクト

https://github.com/futabooo/FileUploadTest


参考資料

How can I upload a files using WebView in Android Lollipop?