質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

Chrome

Google Chromeは携帯、テレビ、デスクトップなどの様々なプラットフォームで利用できるウェブブラウザです。Googleが開発したもので、Blink (レンダリングエンジン) とアプリケーションフレームワークを使用しています。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

アップロード

アップロードは特定のファイルをウェブサーバに送るプロセスのことを指します。

Q&A

解決済

2回答

14670閲覧

androidアプリのWebViewで<input type="file">が叩けない

gittib_gittib

総合スコア102

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

Chrome

Google Chromeは携帯、テレビ、デスクトップなどの様々なプラットフォームで利用できるウェブブラウザです。Googleが開発したもので、Blink (レンダリングエンジン) とアプリケーションフレームワークを使用しています。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

アップロード

アップロードは特定のファイルをウェブサーバに送るプロセスのことを指します。

0グッド

1クリップ

投稿2015/09/16 02:18

現在、WebViewを使ったandroidアプリを開発しています。
その中で、<input type="file">の要素をクリックした時にギャラリーやカメラを起動し、選択/撮影した画像をアップロードしたいのですが、うまくいっていません。
以下のページを参考にWebChromeClientを実装したのですが、onShowFileChooser / openFileChooser が実行されず、その原因が分からない状況です。

【Qiita】LollipopのWebViewでinput type fileで画像アップロード
【Qiita】androidのwebViewでinput type fileに対応する

Android OSのバージョンは5.0です。
また、実装したWebChromeClient拡張クラスは以下になります。

Java

1protected class MyWebChromeClient extends WebChromeClient { 2 3 private static final String TAG = "MyWebChromeClient"; 4 5 /** 6 * JSのアラートをトースト表示にする 7 */ 8 @Override 9 public boolean onJsAlert(WebView view, String url, String message, JsResult result) { 10 Log.d("onJsAlert", url); 11 Log.d("onJsAlert", message); 12 Log.d("onJsAlert", result.toString()); 13 Toast.makeText(context, message, Toast.LENGTH_LONG).show(); 14 //return super.onJsAlert(view, url, message, result); 15 return true; 16 } 17 18 @Override 19 public boolean onConsoleMessage(@NonNull ConsoleMessage cm) { 20 Log.d(TAG, cm.message() + " -- From line " 21 + cm.lineNumber() + " of " 22 + cm.sourceId()); 23 return true; 24 } 25 26 /** 27 * input type="file" 対応 (androidOS 5.0 以上) 28 */ 29 @Override 30 public boolean onShowFileChooser(WebView webView, 31 ValueCallback<Uri[]> filePathCallback, 32 FileChooserParams fileChooserParams) 33 { 34 Log.d(TAG, "onShowFileChooser started."); 35 Toast.makeText(context, "ファイルを選択して下さい", Toast.LENGTH_LONG).show(); 36 super.onShowFileChooser(webView, filePathCallback, fileChooserParams); 37 if( mUploadMessageForAfterLollipop != null) { 38 mUploadMessageForAfterLollipop.onReceiveValue(null); 39 mUploadMessageForAfterLollipop = null; 40 } 41 mUploadMessageForAfterLollipop = filePathCallback; 42 Intent i = fileChooserParams.createIntent(); 43 try { 44 ItemListViewActivity.this.startActivityForResult(i, FILE_CHOOSER_RESULT_CODE); 45 } catch (ActivityNotFoundException e) { 46 mUploadMessageForAfterLollipop = null; 47 return false; 48 } 49 return true; 50 } 51 52 // input type="file" 対応 (androidOS 4.1) 53 // 参考URL : http://qiita.com/masahide318/items/06af79ed8081ef725d76 54 public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) { 55 Log.d(TAG, "openFileChooser start"); 56 Log.d(TAG, "acceptType : " + acceptType); 57 Log.d(TAG, "capture : " + capture); 58 59 mUploadMessage = uploadMsg; 60 Intent i = new Intent(Intent.ACTION_GET_CONTENT); 61 i.addCategory(Intent.CATEGORY_OPENABLE); 62 i.setType("image/*"); 63 startActivityForResult(Intent.createChooser(i, "title"), FILE_CHOOSER_RESULT_CODE); 64 } 65 66 // input type="file" 対応 (androidOS 3.0 以上) 67 public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) { 68 openFileChooser(uploadMsg, acceptType, ""); 69 } 70 71 // input type="file" 対応 (androidOS 3.0 未満) 72 public void openFileChooser(ValueCallback<Uri> uploadMsg) { 73 openFileChooser(uploadMsg, "", ""); 74 } 75}

ご教授いただける事などありましたら、よろしくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

自己解決

どうしても<input type="file" />をキャッチできなかったので、JavascriptInterfaceを提供し、そのメソッドをWebサーバから叩いてもらう形で対応しました。ざっくりとした流れについては、以下のようになります。
0. WebサイトからJavascriptInterfaceを叩いて、ファイルアップロードの要求をキックする
0. 要求を受け取ったらインテントを生成し、カメラまたはギャラリーを起動する
0. onActivityResultで、カメラorギャラリーからの復帰を検知
0. 撮影/選択したファイルパスを取得し、Fileオブジェクトを生成する
0. 生成したFileオブジェクトを後続処理で利用する

実際に作成したコードは以下のようになります。

Java

1 private Uri mCameraCaptureUri; 2 3 /** 4 * 画像選択をさせるためのJavascriptInterface 5 */ 6 private class MyJsInterface { 7 private static final String TAG = "MyJsInterface"; 8 public static final String objectName = "androidJs"; 9 10 /** 11 * JavaScriptからカメラまたはギャラリーを起動し、アップロードするファイルを撮影/選択させる 12 */ 13 @JavascriptInterface 14 public void openFileDialog(String selector) { 15 Log.d(TAG, "openFileDialog start"); 16 17 //カメラの起動Intentの用意 18 String photoName = System.currentTimeMillis() + ".jpg"; 19 ContentValues contentValues = new ContentValues(); 20 contentValues.put(MediaStore.Images.Media.TITLE, photoName); 21 contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); 22 mCameraCaptureUri = getContentResolver() 23 .insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); 24 25 Intent intentCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 26 intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, mCameraCaptureUri); 27 28 // ギャラリー用のIntent作成 29 Intent intentGallery; 30 if (Build.VERSION.SDK_INT < 19) { 31 intentGallery = new Intent(Intent.ACTION_GET_CONTENT); 32 intentGallery.setType("image/*"); 33 } else { 34 intentGallery = new Intent(Intent.ACTION_OPEN_DOCUMENT); 35 intentGallery.addCategory(Intent.CATEGORY_OPENABLE); 36 intentGallery.setType("image/jpeg"); 37 } 38 Intent intent = Intent.createChooser(intentGallery, getString(R.string.select_image)); 39 intent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {intentCamera}); 40 startActivityForResult(intent, JS_FILE_CHOOSER_RESULT_CODE); 41 } 42 } 43 44 45 @Override 46 protected void onActivityResult(int requestCode, int resultCode, Intent intent) { 47 // input type="file" でファイルを指定した時呼ばれる 48 49 if (resultCode == Activity.RESULT_CANCELED) { 50 // ファイル選択をキャンセルされた場合 51 if (mCameraCaptureUri != null) { 52 getContentResolver().delete(mCameraCaptureUri, null, null); 53 mCameraCaptureUri = null; 54 } 55 return; 56 } 57 58 Log.d(TAG, "onActivityResult start"); 59 Log.d(TAG, "requestCode : " + requestCode); 60 Log.d(TAG, "resultCode : " + resultCode); 61 Log.d(TAG, "intent : " + ((intent == null) ? "null" : intent.toString())); 62 63 /** 64 * 画像ファイルの読込に失敗した場合に投げる専用の例外クラス 65 */ 66 class ImageLoadFailureException extends RuntimeException { 67 public ImageLoadFailureException(String s) { 68 super(s); 69 } 70 } 71 72 try { 73 switch (requestCode) { 74 case JS_FILE_CHOOSER_RESULT_CODE: 75 Log.d(TAG, "androidJSのJavascriptInterfaceからのコール"); 76 String path = getImagePathFromIntent(intent); 77 if (path.equals("")) { 78 throw new ImageLoadFailureException("[JsInterface] file path get failure."); 79 } 80 // 取得したファイルパスを用いて画像ファイルを開く 81 File file = new File(path); 82 Log.d(TAG, path); 83 Log.d(TAG, files.toString()); 84 858687 (開いたファイルを使って後続処理) 888990 91 break; 92 } 93 } catch (RuntimeException e) { 94 e.printStackTrace(); 95 Toast.makeText(context, R.string.image_load_failure, Toast.LENGTH_LONG).show(); 96 } 97 } 98 99 100 /** 101 * ギャラリーやカメラから与えられた画像を取得し、その情報を返す 102 * 103 * @param intent 画像提供元のIntent 104 * 105 * @return String 画像情報文字列。取得失敗した場合は空文字列""を返す 106 */ 107 private String getImagePathFromIntent (Intent intent) { 108 Log.d(TAG, "getImagePathFromIntent start"); 109 110 // intentがnullだったらカメラで撮影したパターンのはず 111 boolean bCamera = (intent == null); 112 113 String sImage = ""; 114 Uri uri = intent != null ? intent.getData() : null; 115 if (uri == null) { 116 if (mCameraCaptureUri == null) { 117 // カメラ画像のUriもNULLだった 118 Log.d(TAG, "uri ぬるぽ"); 119 return ""; 120 } 121 uri = mCameraCaptureUri; 122 bCamera = true; 123 } else { 124 if (mCameraCaptureUri != null) { 125 getContentResolver().delete(mCameraCaptureUri, null, null); 126 mCameraCaptureUri = null; 127 } 128 } 129 Log.d(TAG, "画像Uri : " + uri.toString()); 130 131 Bitmap captureImage; 132 try { 133 captureImage = getBitmapFromUri(uri); 134 if (captureImage == null) { 135 throw new IOException("captureImage == null. 画像情報ロード失敗"); 136 } 137 } catch (IOException e) { 138 e.printStackTrace(); 139 return ""; 140 } 141 if (bCamera) { 142 Log.d(TAG, "カメラで撮影されたのでギャラリーへスキャンを促す"); 143 MediaScannerConnection.scanFile( 144 this, 145 new String[]{uri.getPath()}, 146 new String[]{"image/jpeg"}, 147 null 148 ); 149 } 150 Log.d(TAG, captureImage.toString()); 151 152 Cursor cur = null; 153 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !bCamera) { 154 try { 155 String id = DocumentsContract.getDocumentId(uri); 156 String selection = "_id=?"; 157 String[] selectionArgs = new String[]{id.split(":")[1]}; 158 cur = getContentResolver().query( 159 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 160 new String[]{MediaStore.MediaColumns.DATA}, 161 selection, 162 selectionArgs, 163 null); 164 } catch (IllegalArgumentException e) { 165 e.printStackTrace(); 166 throw e; 167 } 168 } 169 170 if (cur == null) { 171 // カメラで撮った場合または旧いOS向けの処理 172 String[] projection = {MediaStore.Images.Media.DATA}; 173 cur = getContentResolver().query(uri, projection, null, null, null); 174 } else { 175 if (cur.moveToFirst()) { 176 sImage = cur.getString(0); 177 } 178 cur.close(); 179 } 180 181 if (sImage == null) { 182 Log.w(TAG, "sImage is null!!!"); 183 sImage = ""; 184 } 185 Log.d(TAG, "sImage : " + sImage); 186 187 return sImage; 188 }

投稿2015/12/25 03:57

gittib_gittib

総合スコア102

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ふと思ったのですが、「input type="file"」に対して動くメソッドが2つあるときはどちらが起動するのでしょうか。「順番に両方動く」、「片方動く」、「両方動かない」
いろいろなサイトを巡ったところ片方しか書いていないところが多いので…

投稿2015/11/11 06:51

yona

総合スコア18155

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問