私はここ数日からWebViewからファイルをアップロードするのに苦労しており、進展はありません。私はすべての提案されたソリューションをグーグルで実装しましたが、動作しません:提案されたソリューション here など。
問題:ファイルをアップロードするための次のコードを含むHTMLページがあります。 Firefoxなどのデスクトップブラウザーやエミュレーター/ AVDの組み込みブラウザーで正常に動作します。つまり、要素によってレンダリングされた[参照...]ボタンをクリックすると、ブラウザーがアップロードするファイルを選択できるダイアログボックスを開きます。
ただし、Android 3.0エミュレータ/ AVDでは、[ファイルの選択]をクリックしても何も起こりません。ファイルダイアログは開きません!!!
<form method="POST" enctype="multipart/form-data">
File to upload: <input type="file" name="uploadfile">
<input type="submit" value="Press to Upload..."> to upload the file!
</form>
できるだけ早く解決策を提案してください。
これはすべてのAndroidバージョンの完全なソリューションであり、これにも苦労しました。
public class MyWb extends Activity {
/** Called when the activity is first created. */
WebView web;
ProgressBar progressBar;
private ValueCallback<Uri> mUploadMessage;
private final static int FILECHOOSER_RESULTCODE=1;
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
if(requestCode==FILECHOOSER_RESULTCODE)
{
if (null == mUploadMessage) return;
Uri result = intent == null || resultCode != RESULT_OK ? null
: intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
web = (WebView) findViewById(R.id.webview01);
progressBar = (ProgressBar) findViewById(R.id.progressBar1);
web = new WebView(this);
web.getSettings().setJavaScriptEnabled(true);
web.loadUrl("http://www.script-tutorials.com/demos/199/index.html");
web.setWebViewClient(new myWebClient());
web.setWebChromeClient(new WebChromeClient()
{
//The undocumented magic method override
//Eclipse will swear at you if you try to put @Override here
// For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
MyWb.this.startActivityForResult(Intent.createChooser(i,"File Chooser"), FILECHOOSER_RESULTCODE);
}
// For Android 3.0+
public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
MyWb.this.startActivityForResult(
Intent.createChooser(i, "File Browser"),
FILECHOOSER_RESULTCODE);
}
//For Android 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
MyWb.this.startActivityForResult( Intent.createChooser( i, "File Chooser" ), MyWb.FILECHOOSER_RESULTCODE );
}
});
setContentView(web);
}
public class myWebClient extends WebViewClient
{
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
// TODO Auto-generated method stub
super.onPageStarted(view, url, favicon);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// TODO Auto-generated method stub
view.loadUrl(url);
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
// TODO Auto-generated method stub
super.onPageFinished(view, url);
progressBar.setVisibility(View.GONE);
}
}
//flipscreen not loading again
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
// To handle "Back" key press event for WebView to go back to previous screen.
/*@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if ((keyCode == KeyEvent.KEYCODE_BACK) && web.canGoBack()) {
web.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}*/
}
また、この例のような「アップロードページ」は画像プレビュー機能を備えているため、4バージョン未満では機能しません。プレビューせずにシンプルなphpアップロードを使用したい場合は追加します。
更新:
Lollipopデバイスの解決策を見つけてください こちら と gauntface に感謝
更新2:
oreo here および これはより高度なバージョンです までのすべてのAndroidデバイスの完全なソリューションです。詳しく調べる必要があります。
HONEYCOMB(API 11)からOreo(API 27)への作業方法
[Pie 9.0ではテストされていません]
static WebView mWebView;
private ValueCallback<Uri> mUploadMessage;
public ValueCallback<Uri[]> uploadMessage;
public static final int REQUEST_SELECT_FILE = 100;
private final static int FILECHOOSER_RESULTCODE = 1;
変更済みonActivityResult()
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop)
{
if (requestCode == REQUEST_SELECT_FILE)
{
if (uploadMessage == null)
return;
uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent));
uploadMessage = null;
}
}
else if (requestCode == FILECHOOSER_RESULTCODE)
{
if (null == mUploadMessage)
return;
// Use MainActivity.RESULT_OK if you're implementing WebView inside Fragment
// Use RESULT_OK only if you're implementing WebView inside an Activity
Uri result = intent == null || resultCode != MainActivity.RESULT_OK ? null : intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
else
Toast.makeText(getActivity().getApplicationContext(), "Failed to Upload Image", Toast.LENGTH_LONG).show();
}
ここでonCreate()
またはonCreateView()
に次のコードを貼り付けます
WebSettings mWebSettings = mWebView.getSettings();
mWebSettings.setJavaScriptEnabled(true);
mWebSettings.setSupportZoom(false);
mWebSettings.setAllowFileAccess(true);
mWebSettings.setAllowFileAccess(true);
mWebSettings.setAllowContentAccess(true);
mWebView.setWebChromeClient(new WebChromeClient()
{
// For 3.0+ Devices (Start)
// onActivityResult attached before constructor
protected void openFileChooser(ValueCallback uploadMsg, String acceptType)
{
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE);
}
// For Lollipop 5.0+ Devices
public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams)
{
if (uploadMessage != null) {
uploadMessage.onReceiveValue(null);
uploadMessage = null;
}
uploadMessage = filePathCallback;
Intent intent = fileChooserParams.createIntent();
try
{
startActivityForResult(intent, REQUEST_SELECT_FILE);
} catch (ActivityNotFoundException e)
{
uploadMessage = null;
Toast.makeText(getActivity().getApplicationContext(), "Cannot Open File Chooser", Toast.LENGTH_LONG).show();
return false;
}
return true;
}
//For Android 4.1 only
protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)
{
mUploadMessage = uploadMsg;
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(Intent.createChooser(intent, "File Browser"), FILECHOOSER_RESULTCODE);
}
protected void openFileChooser(ValueCallback<Uri> uploadMsg)
{
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}
});
これは私が見つけた唯一の解決策です!
WebView webview;
private ValueCallback<Uri> mUploadMessage;
private final static int FILECHOOSER_RESULTCODE = 1;
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
Uri result = intent == null || resultCode != RESULT_OK ? null
: intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
}
// Next part
class MyWebChromeClient extends WebChromeClient {
// The undocumented magic method override
// Eclipse will swear at you if you try to put @Override here
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
Cv5appActivity.this.startActivityForResult(
Intent.createChooser(i, "Image Browser"),
FILECHOOSER_RESULTCODE);
}
}
5.0 Lollipopで、Googleは公式メソッド WebChromeClient.onShowFileChooser を追加しました。これらは、入力受け入れMIMEタイプを使用するように、ファイル選択インテントを自動的に生成する方法も提供します。
public class MyWebChromeClient extends WebChromeClient {
// reference to activity instance. May be unnecessary if your web chrome client is member class.
private MyActivity activity;
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
// make sure there is no existing message
if (myActivity.uploadMessage != null) {
myActivity.uploadMessage.onReceiveValue(null);
myActivity.uploadMessage = null;
}
myActivity.uploadMessage = filePathCallback;
Intent intent = fileChooserParams.createIntent();
try {
myActivity.startActivityForResult(intent, MyActivity.REQUEST_SELECT_FILE);
} catch (ActivityNotFoundException e) {
myActivity.uploadMessage = null;
Toast.makeText(myActivity, "Cannot open file chooser", Toast.LENGTH_LONG).show();
return false;
}
return true;
}
}
public class MyActivity extends ... {
public static final int REQUEST_SELECT_FILE = 100;
public ValueCallback<Uri[]> uploadMessage;
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if (requestCode == REQUEST_SELECT_FILE) {
if (uploadMessage == null) return;
uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
uploadMessage = null;
}
}
}
}
KitKatより前のAndroidバージョンでは、他の回答に記載されているプライベートメソッドが機能します。 KitKat(4.4)の適切な回避策は見つかりませんでした。
Androidのさまざまなバージョンを処理するには、3つのインターフェイス定義が必要であることがわかりました。
public void openFileChooser(ValueCallback < Uri > uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
FreeHealthTrack.this.startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILECHOOSER_RESULTCODE);
}
public void openFileChooser(ValueCallback < Uri > uploadMsg, String acceptType) {
openFileChooser(uploadMsg);
}
public void openFileChooser(ValueCallback < Uri > uploadMsg, String acceptType, String capture) {
openFileChooser(uploadMsg);
}
このソリューションは、ハニカムとアイスクリームのサンドイッチにも使用できます。 Googleはクールな新機能(accept属性)を導入し、後方互換性のためにオーバーロードを実装するのを忘れていたようです。
protected class CustomWebChromeClient extends WebChromeClient
{
// For Android 3.0+
public void openFileChooser( ValueCallback<Uri> uploadMsg, String acceptType )
{
context.mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
context.startActivityForResult( Intent.createChooser( i, "File Chooser" ), MainActivity.FILECHOOSER_RESULTCODE );
}
// For Android < 3.0
public void openFileChooser( ValueCallback<Uri> uploadMsg )
{
openFileChooser( uploadMsg, "" );
}
}
hifarrerの完全なソリューションは私にとって非常に役立ちます。
しかし、私は他の多くの問題に遭遇しました-他のMIMEタイプのサポート、キャプチャデバイス(カメラ、ビデオ、オーディオレコーダ)のリスト、キャプチャデバイスの即時オープン(例:<input accept = "image/*; capture">)...
そこで、デフォルトのWebブラウザーアプリとまったく同じように機能するソリューションを作成しました。
Android-4.4.3_r1/src/com/Android/browser/UploadHandler.Javaを使用しました。 (Rupert Rawnsleyに感謝)
package org.mospi.agatenativewebview;
import Java.io.File;
import Java.lang.reflect.Method;
import Java.net.URL;
import Android.app.Activity;
import Android.app.AlertDialog;
import Android.content.ActivityNotFoundException;
import Android.content.DialogInterface;
import Android.content.Intent;
import Android.net.Uri;
import Android.os.Bundle;
import Android.os.Environment;
import Android.provider.MediaStore;
import Android.view.View;
import Android.webkit.JsResult;
import Android.webkit.ValueCallback;
import Android.webkit.WebChromeClient;
import Android.webkit.WebSettings;
import Android.webkit.WebView;
import Android.webkit.WebSettings.PluginState;
import Android.widget.Toast;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webView = (WebView) findViewById(R.id.webView1);
initWebView(webView);
webView.loadUrl("http://google.com"); // TODO input your url
}
private final static Object methodInvoke(Object obj, String method, Class<?>[] parameterTypes, Object[] args) {
try {
Method m = obj.getClass().getMethod(method, new Class[] { boolean.class });
m.invoke(obj, args);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private void initWebView(WebView webView) {
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setAllowFileAccess(true);
settings.setDomStorageEnabled(true);
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
settings.setLoadWithOverviewMode(true);
settings.setUseWideViewPort(true);
settings.setSupportZoom(true);
// settings.setPluginsEnabled(true);
methodInvoke(settings, "setPluginsEnabled", new Class[] { boolean.class }, new Object[] { true });
// settings.setPluginState(PluginState.ON);
methodInvoke(settings, "setPluginState", new Class[] { PluginState.class }, new Object[] { PluginState.ON });
// settings.setPluginsEnabled(true);
methodInvoke(settings, "setPluginsEnabled", new Class[] { boolean.class }, new Object[] { true });
// settings.setAllowUniversalAccessFromFileURLs(true);
methodInvoke(settings, "setAllowUniversalAccessFromFileURLs", new Class[] { boolean.class }, new Object[] { true });
// settings.setAllowFileAccessFromFileURLs(true);
methodInvoke(settings, "setAllowFileAccessFromFileURLs", new Class[] { boolean.class }, new Object[] { true });
webView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
webView.clearHistory();
webView.clearFormData();
webView.clearCache(true);
webView.setWebChromeClient(new MyWebChromeClient());
// webView.setDownloadListener(downloadListener);
}
UploadHandler mUploadHandler;
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == Controller.FILE_SELECTED) {
// Chose a file from the file picker.
if (mUploadHandler != null) {
mUploadHandler.onResult(resultCode, intent);
}
}
super.onActivityResult(requestCode, resultCode, intent);
}
class MyWebChromeClient extends WebChromeClient {
public MyWebChromeClient() {
}
private String getTitleFromUrl(String url) {
String title = url;
try {
URL urlObj = new URL(url);
String Host = urlObj.getHost();
if (Host != null && !Host.isEmpty()) {
return urlObj.getProtocol() + "://" + Host;
}
if (url.startsWith("file:")) {
String fileName = urlObj.getFile();
if (fileName != null && !fileName.isEmpty()) {
return fileName;
}
}
} catch (Exception e) {
// ignore
}
return title;
}
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
String newTitle = getTitleFromUrl(url);
new AlertDialog.Builder(MainActivity.this).setTitle(newTitle).setMessage(message).setPositiveButton(Android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
}).setCancelable(false).create().show();
return true;
// return super.onJsAlert(view, url, message, result);
}
@Override
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
String newTitle = getTitleFromUrl(url);
new AlertDialog.Builder(MainActivity.this).setTitle(newTitle).setMessage(message).setPositiveButton(Android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
}).setNegativeButton(Android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
}).setCancelable(false).create().show();
return true;
// return super.onJsConfirm(view, url, message, result);
}
// Android 2.x
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
openFileChooser(uploadMsg, "");
}
// Android 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
openFileChooser(uploadMsg, "", "filesystem");
}
// Android 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
mUploadHandler = new UploadHandler(new Controller());
mUploadHandler.openFileChooser(uploadMsg, acceptType, capture);
}
// Android 4.4, 4.4.1, 4.4.2
// openFileChooser function is not called on Android 4.4, 4.4.1, 4.4.2,
// you may use your own Java script interface or other hybrid framework.
// Android 5.0.1
public boolean onShowFileChooser(
WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
String acceptTypes[] = fileChooserParams.getAcceptTypes();
String acceptType = "";
for (int i = 0; i < acceptTypes.length; ++ i) {
if (acceptTypes[i] != null && acceptTypes[i].length() != 0)
acceptType += acceptTypes[i] + ";";
}
if (acceptType.length() == 0)
acceptType = "*/*";
final ValueCallback<Uri[]> finalFilePathCallback = filePathCallback;
ValueCallback<Uri> vc = new ValueCallback<Uri>() {
@Override
public void onReceiveValue(Uri value) {
Uri[] result;
if (value != null)
result = new Uri[]{value};
else
result = null;
finalFilePathCallback.onReceiveValue(result);
}
};
openFileChooser(vc, acceptType, "filesystem");
return true;
}
};
class Controller {
final static int FILE_SELECTED = 4;
Activity getActivity() {
return MainActivity.this;
}
}
// copied from Android-4.4.3_r1/src/com/Android/browser/UploadHandler.Java
//////////////////////////////////////////////////////////////////////
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.Apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// package com.Android.browser;
//
// import Android.app.Activity;
// import Android.content.ActivityNotFoundException;
// import Android.content.Intent;
// import Android.net.Uri;
// import Android.os.Environment;
// import Android.provider.MediaStore;
// import Android.webkit.ValueCallback;
// import Android.widget.Toast;
//
// import Java.io.File;
// import Java.util.Vector;
//
// /**
// * Handle the file upload callbacks from WebView here
// */
// public class UploadHandler {
class UploadHandler {
/*
* The Object used to inform the WebView of the file to upload.
*/
private ValueCallback<Uri> mUploadMessage;
private String mCameraFilePath;
private boolean mHandled;
private boolean mCaughtActivityNotFoundException;
private Controller mController;
public UploadHandler(Controller controller) {
mController = controller;
}
String getFilePath() {
return mCameraFilePath;
}
boolean handled() {
return mHandled;
}
void onResult(int resultCode, Intent intent) {
if (resultCode == Activity.RESULT_CANCELED && mCaughtActivityNotFoundException) {
// Couldn't resolve an activity, we are going to try again so skip
// this result.
mCaughtActivityNotFoundException = false;
return;
}
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null
: intent.getData();
// As we ask the camera to save the result of the user taking
// a picture, the camera application does not return anything other
// than RESULT_OK. So we need to check whether the file we expected
// was written to disk in the in the case that we
// did not get an intent returned but did get a RESULT_OK. If it was,
// we assume that this result has came back from the camera.
if (result == null && intent == null && resultCode == Activity.RESULT_OK) {
File cameraFile = new File(mCameraFilePath);
if (cameraFile.exists()) {
result = Uri.fromFile(cameraFile);
// Broadcast to the media scanner that we have a new photo
// so it will be added into the gallery for the user.
mController.getActivity().sendBroadcast(
new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
}
}
mUploadMessage.onReceiveValue(result);
mHandled = true;
mCaughtActivityNotFoundException = false;
}
void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
final String imageMimeType = "image/*";
final String videoMimeType = "video/*";
final String audioMimeType = "audio/*";
final String mediaSourceKey = "capture";
final String mediaSourceValueCamera = "camera";
final String mediaSourceValueFileSystem = "filesystem";
final String mediaSourceValueCamcorder = "camcorder";
final String mediaSourceValueMicrophone = "microphone";
// According to the spec, media source can be 'filesystem' or 'camera' or 'camcorder'
// or 'microphone' and the default value should be 'filesystem'.
String mediaSource = mediaSourceValueFileSystem;
if (mUploadMessage != null) {
// Already a file picker operation in progress.
return;
}
mUploadMessage = uploadMsg;
// Parse the accept type.
String params[] = acceptType.split(";");
String mimeType = params[0];
if (capture.length() > 0) {
mediaSource = capture;
}
if (capture.equals(mediaSourceValueFileSystem)) {
// To maintain backwards compatibility with the previous implementation
// of the media capture API, if the value of the 'capture' attribute is
// "filesystem", we should examine the accept-type for a MIME type that
// may specify a different capture value.
for (String p : params) {
String[] keyValue = p.split("=");
if (keyValue.length == 2) {
// Process key=value parameters.
if (mediaSourceKey.equals(keyValue[0])) {
mediaSource = keyValue[1];
}
}
}
}
//Ensure it is not still set from a previous upload.
mCameraFilePath = null;
if (mimeType.equals(imageMimeType)) {
if (mediaSource.equals(mediaSourceValueCamera)) {
// Specified 'image/*' and requested the camera, so go ahead and launch the
// camera directly.
startActivity(createCameraIntent());
return;
} else {
// Specified just 'image/*', capture=filesystem, or an invalid capture parameter.
// In all these cases we show a traditional picker filetered on accept type
// so launch an intent for both the Camera and image/* OPENABLE.
Intent chooser = createChooserIntent(createCameraIntent());
chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(imageMimeType));
startActivity(chooser);
return;
}
} else if (mimeType.equals(videoMimeType)) {
if (mediaSource.equals(mediaSourceValueCamcorder)) {
// Specified 'video/*' and requested the camcorder, so go ahead and launch the
// camcorder directly.
startActivity(createCamcorderIntent());
return;
} else {
// Specified just 'video/*', capture=filesystem or an invalid capture parameter.
// In all these cases we show an intent for the traditional file picker, filtered
// on accept type so launch an intent for both camcorder and video/* OPENABLE.
Intent chooser = createChooserIntent(createCamcorderIntent());
chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(videoMimeType));
startActivity(chooser);
return;
}
} else if (mimeType.equals(audioMimeType)) {
if (mediaSource.equals(mediaSourceValueMicrophone)) {
// Specified 'audio/*' and requested microphone, so go ahead and launch the sound
// recorder.
startActivity(createSoundRecorderIntent());
return;
} else {
// Specified just 'audio/*', capture=filesystem of an invalid capture parameter.
// In all these cases so go ahead and launch an intent for both the sound
// recorder and audio/* OPENABLE.
Intent chooser = createChooserIntent(createSoundRecorderIntent());
chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(audioMimeType));
startActivity(chooser);
return;
}
}
// No special handling based on the accept type was necessary, so trigger the default
// file upload chooser.
startActivity(createDefaultOpenableIntent());
}
private void startActivity(Intent intent) {
try {
mController.getActivity().startActivityForResult(intent, Controller.FILE_SELECTED);
} catch (ActivityNotFoundException e) {
// No installed app was able to handle the intent that
// we sent, so fallback to the default file upload control.
try {
mCaughtActivityNotFoundException = true;
mController.getActivity().startActivityForResult(createDefaultOpenableIntent(),
Controller.FILE_SELECTED);
} catch (ActivityNotFoundException e2) {
// Nothing can return us a file, so file upload is effectively disabled.
Toast.makeText(mController.getActivity(), R.string.uploads_disabled,
Toast.LENGTH_LONG).show();
}
}
}
private Intent createDefaultOpenableIntent() {
// Create and return a chooser with the default OPENABLE
// actions including the camera, camcorder and sound
// recorder where available.
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
Intent chooser = createChooserIntent(createCameraIntent(), createCamcorderIntent(),
createSoundRecorderIntent());
chooser.putExtra(Intent.EXTRA_INTENT, i);
return chooser;
}
private Intent createChooserIntent(Intent... intents) {
Intent chooser = new Intent(Intent.ACTION_CHOOSER);
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents);
chooser.putExtra(Intent.EXTRA_TITLE,
mController.getActivity().getResources()
.getString(R.string.choose_upload));
return chooser;
}
private Intent createOpenableIntent(String type) {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType(type);
return i;
}
private Intent createCameraIntent() {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File externalDataDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM);
File cameraDataDir = new File(externalDataDir.getAbsolutePath() +
File.separator + "browser-photos");
cameraDataDir.mkdirs();
mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator +
System.currentTimeMillis() + ".jpg";
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));
return cameraIntent;
}
private Intent createCamcorderIntent() {
return new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
}
private Intent createSoundRecorderIntent() {
return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
}
}
}
res/values/string.xmlの追加の文字列リソース:
<string name="uploads_disabled">File uploads are disabled.</string>
<string name="choose_upload">Choose file for upload</string>
Proguardを使用している場合、proguard-project.txtで以下のオプションが必要になる場合があります。
-keepclassmembers class * extends Android.webkit.WebChromeClient {
public void openFileChooser(...);
}
更新#1(2015.09.09)
Android 5.0.1互換性のコードを追加します。
Android 4.1でpublic void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)
を定義する必要があることがわかりました。その後、Michel Olivierのソリューションに従いました。
私は実際にファイルピッカーをキットカットに表示し、画像を選択し、アクティビティ結果でファイルパスを取得することができましたが、「修正」できない(この回避策が原因)唯一のことは、入力ファイルを埋めるようにすることですファイルデータを出力します。
アクティビティから入力フィールドにアクセスする方法を知っている人はいますか?この例を使用しています comment 。この最後のピース、壁の最後のレンガはちょうど正しい場所に置く必要があります(コードから直接画像ファイルのアップロードをトリガーできます)。
ハードコアAndroid devはありませんので、初心者レベルでコードを表示します。既存のアクティビティで新しいアクティビティを作成しています
マニフェスト部
<uses-permission Android:name="Android.permission.INTERNET"/>
<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE"/>
<application Android:label="TestApp">
<activity Android:name=".BrowseActivity"></activity>
</application>
この例からBrowseActivityクラスを作成しています answer 。 WebChromeClient()インスタンスは、最後の部分を除いて基本的に同じように見え、ピッカーUIパーツをトリガーします...
private final static int FILECHOOSER_RESULTCODE=1;
private final static int KitKat_RESULTCODE = 2;
...
// The new WebChromeClient() looks pretty much the same, except one piece...
WebChromeClient chromeClient = new WebChromeClient(){
// For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg) { /* Default code */ }
// For Android 3.0+
public void openFileChooser( ValueCallback uploadMsg, String acceptType ) { /* Default code */ }
//For Android 4.1, also default but it'll be as example
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
BrowseActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), BrowseActivity.FILECHOOSER_RESULTCODE);
}
// The new code
public void showPicker( ValueCallback<Uri> uploadMsg ){
// Here is part of the issue, the uploadMsg is null since it is not triggered from Android
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
BrowseActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), BrowseActivity.KitKat_RESULTCODE);
}}
そして、さらにいくつかのもの
web = new WebView(this);
// Notice this part, setting chromeClient as js interface is just lazy
web.getSettings().setJavaScriptEnabled(true);
web.addJavascriptInterface(chromeClient, "jsi" );
web.getSettings().setAllowFileAccess(true);
web.getSettings().setAllowContentAccess(true);
web.clearCache(true);
web.loadUrl( "http://as3breeze.com/upload.html" );
web.setWebViewClient(new myWebClient());
web.setWebChromeClient(chromeClient);
@Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
Log.d("Result", "("+requestCode+ ") - (" +resultCode + ") - (" + intent + ") - " + mUploadMessage);
if (null == intent) return;
Uri result = null;
if(requestCode==FILECHOOSER_RESULTCODE)
{
Log.d("Result","Old Android");
if (null == mUploadMessage) return;
result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
} else if (requestCode == KitKat_RESULTCODE) {
Log.d("Result","KitKat Android");
result = intent.getData();
final int takeFlags = intent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
String path = getPath( this, result);
File selectedFile = new File(path);
//I used you example with a bit of editing so thought i would share, here i added a method to upload the file to the webserver
File selectedFile = new File(path);
UploadFile(selectedFile);
//mUploadMessage.onReceiveValue( Uri.parse(selectedFile.toString()) );
// Now we have the file but since mUploadMessage was null, it gets errors
}
}
public void UploadFile(File selectedFile)
{
Random rnd = new Random();
String sName = "File" + rnd.nextInt(999999) + selectedFile.getAbsolutePath().substring(selectedFile.getAbsolutePath().lastIndexOf("."));
UploadedFileName = sName;
uploadFile = selectedFile;
if (progressBar != null && progressBar.isShowing())
{
progressBar.dismiss();
}
// prepare for a progress bar dialog
progressBar = new ProgressDialog(mContext);
progressBar.setCancelable(true);
progressBar.setMessage("Uploading File");
progressBar.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressBar.show();
new Thread() {
public void run()
{
int serverResponseCode;
String serverResponseMessage;
HttpURLConnection connection = null;
DataOutputStream outputStream = null;
DataInputStream inputStream = null;
String pathToOurFile = uploadFile.getAbsolutePath();
String urlServer = "http://serveraddress/Scripts/UploadHandler.php?name" + UploadedFileName;
String lineEnd = "\r\n";
String twoHyphens = "--";
String boundary = "*****";
int bytesRead, bytesAvailable, bufferSize;
byte[] buffer;
int maxBufferSize = 1*1024*1024;
try
{
FileInputStream fileInputStream = new FileInputStream(uploadFile);
URL url = new URL(urlServer);
connection = (HttpURLConnection) url.openConnection();
Log.i("File", urlServer);
// Allow Inputs & Outputs.
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
// Set HTTP method to POST.
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "multipart/form-data;boundary="+boundary);
Log.i("File", "Open conn");
outputStream = new DataOutputStream( connection.getOutputStream() );
outputStream.writeBytes(twoHyphens + boundary + lineEnd);
outputStream.writeBytes("Content-Disposition: form-data; name=\"uploadedfile\";filename=\"" + pathToOurFile +"\"" + lineEnd);
outputStream.writeBytes(lineEnd);
Log.i("File", "write bytes");
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
Log.i("File", "available: " + fileInputStream.available());
// Read file
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
Log.i("file", "Bytes Read: " + bytesRead);
while (bytesRead > 0)
{
outputStream.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
outputStream.writeBytes(lineEnd);
outputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
// Responses from the server (code and message)
serverResponseCode = connection.getResponseCode();
serverResponseMessage = connection.getResponseMessage();
Log.i("file repsonse", serverResponseMessage);
//once the file is uploaded call a javascript function to verify the user wants to save the image
progressBar.dismiss();
runOnUiThread(new Runnable()
{
@Override
public void run()
{
Log.i("start", "File name: " + UploadedFileName);
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("javascript:CheckImage('" + UploadedFileName + "')");
}
});
fileInputStream.close();
outputStream.flush();
outputStream.close();
}
catch (Exception ex)
{
Log.i("exception", "Error: " + ex.toString());
}
}
}.start();
}
最後に、実際のファイルパスを取得するためのコード、SOで見つかったコード、投稿のURLもコメントに追加しました。これにより、作者は自分の作品の功績が認められます。
/**
* Get a file path from a Uri. This will get the the path for Storage Access
* Framework Documents, as well as the _data field for the MediaStore and
* other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @author paulburke
* @source https://stackoverflow.com/a/20559175
*/
@TargetApi(Build.VERSION_CODES.KitKat)
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KitKat;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
* @source https://stackoverflow.com/a/20559175
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
* @source https://stackoverflow.com/a/20559175
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.Android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
* @source https://stackoverflow.com/a/20559175
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.Android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
* @source https://stackoverflow.com/a/20559175
*/
public static boolean isMediaDocument(Uri uri) {
return "com.Android.providers.media.documents".equals(uri.getAuthority());
}
最後に、HTMLページはshowPickerの新しいメソッドをトリガーする必要があります(特にA4.4の場合)
<form id="form-upload" method="post" enctype="multipart/form-data">
<input id="fileupload" name="fileupload" type="file" onclick="javascript:prepareForPicker();"/>
</form>
<script type="text/javascript">
function getAndroidVersion() {
var ua = navigator.userAgent;
var match = ua.match(/Android\s([0-9\.]*)/);
return match ? match[1] : false;
};
function prepareForPicker(){
if(getAndroidVersion().indexOf("4.4") != -1){
window.jsi.showPicker();
return false;
}
}
function CheckImage(name)
{
//Check to see if user wants to save I used some ajax to save the file if necesarry
}
</script>
import Android.Manifest;
import Android.annotation.SuppressLint;
import Android.app.Activity;
import Android.content.Intent;
import Android.content.pm.PackageManager;
import Android.content.res.Configuration;
import Android.net.Uri;
import Android.os.Build;
import Android.os.Bundle;
import Android.os.Environment;
import Android.provider.MediaStore;
import Android.support.annotation.NonNull;
import Android.support.v4.app.ActivityCompat;
import Android.support.v4.content.ContextCompat;
import Android.support.v7.app.AppCompatActivity;
import Android.util.Log;
import Android.view.KeyEvent;
import Android.view.View;
import Android.webkit.ValueCallback;
import Android.webkit.WebChromeClient;
import Android.webkit.WebSettings;
import Android.webkit.WebView;
import Android.webkit.WebViewClient;
import Android.widget.Toast;
import Java.io.File;
import Java.io.IOException;
import Java.text.SimpleDateFormat;
import Java.util.Date;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private final static int FCR = 1;
WebView webView;
private String mCM;
private ValueCallback<Uri> mUM;
private ValueCallback<Uri[]> mUMA;
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if (Build.VERSION.SDK_INT >= 21) {
Uri[] results = null;
//Check if response is positive
if (resultCode == Activity.RESULT_OK) {
if (requestCode == FCR) {
if (null == mUMA) {
return;
}
if (intent == null) {
//Capture Photo if no image available
if (mCM != null) {
results = new Uri[]{Uri.parse(mCM)};
}
} else {
String dataString = intent.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
}
}
}
mUMA.onReceiveValue(results);
mUMA = null;
} else {
if (requestCode == FCR) {
if (null == mUM) return;
Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
mUM.onReceiveValue(result);
mUM = null;
}
}
}
@SuppressLint({"SetJavaScriptEnabled", "WrongViewCast"})
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= 23 && (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, 1);
}
webView = (WebView) findViewById(R.id.ifView);
assert webView != null;
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccess(true);
if (Build.VERSION.SDK_INT >= 21) {
webSettings.setMixedContentMode(0);
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
} else if (Build.VERSION.SDK_INT >= 19) {
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
} else if (Build.VERSION.SDK_INT < 19) {
webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
webView.setWebViewClient(new Callback());
webView.loadUrl("https://infeeds.com/");
webView.setWebChromeClient(new WebChromeClient() {
//For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUM = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), FCR);
}
// For Android 3.0+, above method not supported in some Android 3+ versions, in such case we use this
public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
mUM = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
MainActivity.this.startActivityForResult(
Intent.createChooser(i, "File Browser"),
FCR);
}
//For Android 4.1+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
mUM = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), MainActivity.FCR);
}
//For Android 5.0+
public boolean onShowFileChooser(
WebView webView, ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
if (mUMA != null) {
mUMA.onReceiveValue(null);
}
mUMA = filePathCallback;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(MainActivity.this.getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCM);
} catch (IOException ex) {
Log.e(TAG, "Image file creation failed", ex);
}
if (photoFile != null) {
mCM = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("*/*");
Intent[] intentArray;
if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else {
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, FCR);
return true;
}
});
}
// Create an image file
private File createImageFile() throws IOException {
@SuppressLint("SimpleDateFormat") String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "img_" + timeStamp + "_";
File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
return File.createTempFile(imageFileName, ".jpg", storageDir);
}
@Override
public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (webView.canGoBack()) {
webView.goBack();
} else {
finish();
}
return true;
}
}
return super.onKeyDown(keyCode, event);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
public class Callback extends WebViewClient {
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Toast.makeText(getApplicationContext(), "Failed loading app!", Toast.LENGTH_SHORT).show();
}
}
}
Google独自のブラウザは、この問題に対する包括的なソリューションを提供し、独自のクラスを保証します。
SOLUTIONが見つかりました。ファイルproguard-Android.txt
にもう1つのルールを追加します。
-keepclassmembers class * extends Android.webkit.WebChromeClient {
public void openFileChooser(...);
}
Android 8のKotlinソリューション:
private var mUploadMessage: ValueCallback<Uri>? = null
private var uploadMessage: ValueCallback<Array<Uri>>? = null
定数:
const val FILECHOOSER_RESULTCODE = 1
const val REQUEST_SELECT_FILE = 100
WebViewのセットアップ:
webView.webChromeClient = object : WebChromeClient() {
override fun onPermissionRequest(request: PermissionRequest?) {
Log.d("MainActivity", "onPermissionRequest")
requestPermission(request)
}
// For Android 3.0+
fun openFileChooser(uploadMsg: ValueCallback<*>, acceptType: String) {
mUploadMessage = uploadMsg as ValueCallback<Uri>
val i = Intent(Intent.ACTION_GET_CONTENT)
i.addCategory(Intent.CATEGORY_OPENABLE)
i.type = "*/*"
[email protected](
Intent.createChooser(i, "File Browser"),
FILECHOOSER_RESULTCODE)
}
//For Android 4.1
fun openFileChooser(uploadMsg: ValueCallback<Uri>, acceptType: String, capture: String) {
mUploadMessage = uploadMsg
val i = Intent(Intent.ACTION_GET_CONTENT)
i.addCategory(Intent.CATEGORY_OPENABLE)
i.type = "image/*"
[email protected](Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE)
}
protected fun openFileChooser(uploadMsg: ValueCallback<Uri>) {
mUploadMessage = uploadMsg
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
startActivityForResult(Intent.createChooser(intent, "File Chooser"), FILECHOOSER_RESULTCODE)
}
override fun onShowFileChooser(webView: WebView?, filePathCallback: ValueCallback<Array<Uri>>?, fileChooserParams: FileChooserParams?): Boolean {
uploadMessage?.onReceiveValue(null)
uploadMessage = null
uploadMessage = filePathCallback
val intent = fileChooserParams!!.createIntent()
try {
startActivityForResult(intent, REQUEST_SELECT_FILE)
} catch (e: ActivityNotFoundException) {
uploadMessage = null
Toast.makeText(applicationContext, "Cannot Open File Chooser", Toast.LENGTH_LONG).show()
return false
}
return true
}
}
OnAcrtivityResultパート:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
if (requestCode === REQUEST_SELECT_FILE) {
if (uploadMessage == null)
return
print("result code = " + resultCode)
var results: Array<Uri>? = WebChromeClient.FileChooserParams.parseResult(resultCode, data)
uploadMessage?.onReceiveValue(results)
uploadMessage = null
}
} else if (requestCode === FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return
// Use MainActivity.RESULT_OK if you're implementing WebView inside Fragment
// Use RESULT_OK only if you're implementing WebView inside an Activity
val result = if (intent == null || resultCode !== RESULT_OK) null else intent.data
mUploadMessage?.onReceiveValue(result)
mUploadMessage = null
} else
Toast.makeText(applicationContext, "Failed to Upload Image", Toast.LENGTH_LONG).show()
}
「データ」というインテント変数に注意してください。
このリンクにアクセスしましたか? http://groups.google.com/group/Android-developers/browse_thread/thread/dcaf8b2fdd8a90c4/62d5e2ffef31ebdb
http://moazzam-khan.com/blog/?tag=Android-upload-file
http://evgenyg.wordpress.com/2010/05/01/uploading-files-multipart-post-Apache/
Java lib Apache Commonsを介したファイルアップロードの簡潔な例
私はあなたがこれから助けを得ると思います
Webview-単一および複数のファイルを選択
このコードを実装するには2分かかります。
build.gradle
implementation 'com.github.angads25:filepicker:1.1.1'
Javaコード:
import Android.annotation.SuppressLint;
import Android.app.Activity;
import Android.content.DialogInterface;
import Android.content.Intent;
import Android.content.pm.PackageManager;
import Android.graphics.Bitmap;
import Android.net.Uri;
import Android.os.Build;
import Android.os.Bundle;
import Android.support.annotation.NonNull;
import Android.util.Log;
import Android.view.KeyEvent;
import Android.view.View;
import Android.webkit.ValueCallback;
import Android.webkit.WebChromeClient;
import Android.webkit.WebSettings;
import Android.webkit.WebView;
import Android.webkit.WebViewClient;
import Android.widget.ProgressBar;
import Android.widget.Toast;
import com.bivoiclient.utils.Constants;
import com.github.angads25.filepicker.controller.DialogSelectionListener;
import com.github.angads25.filepicker.model.DialogConfigs;
import com.github.angads25.filepicker.model.DialogProperties;
import com.github.angads25.filepicker.view.FilePickerDialog;
import Java.io.File;
public class WebBrowserScreen extends Activity {
private WebView webView;
private ValueCallback<Uri[]> mUploadMessage;
private FilePickerDialog dialog;
private String LOG_TAG = "DREG";
private Uri[] results;
@SuppressLint("SetJavaScriptEnabled")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_complain);
webView = findViewById(R.id.webview);
WebSettings webSettings = webView.getSettings();
webSettings.setAppCacheEnabled(true);
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
webSettings.setJavaScriptEnabled(true);
webSettings.setLoadWithOverviewMode(true);
webSettings.setAllowFileAccess(true);
webView.setWebViewClient(new PQClient());
webView.setWebChromeClient(new PQChromeClient());
if (Build.VERSION.SDK_INT >= 19) {
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
} else {
webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
webView.loadUrl(Constants.COMPLAIN_URL);
}
private void openFileSelectionDialog() {
if (null != dialog && dialog.isShowing()) {
dialog.dismiss();
}
//Create a DialogProperties object.
final DialogProperties properties = new DialogProperties();
//Instantiate FilePickerDialog with Context and DialogProperties.
dialog = new FilePickerDialog(WebBrowserScreen.this, properties);
dialog.setTitle("Select a File");
dialog.setPositiveBtnName("Select");
dialog.setNegativeBtnName("Cancel");
properties.selection_mode = DialogConfigs.MULTI_MODE; // for multiple files
// properties.selection_mode = DialogConfigs.SINGLE_MODE; // for single file
properties.selection_type = DialogConfigs.FILE_SELECT;
//Method handle selected files.
dialog.setDialogSelectionListener(new DialogSelectionListener() {
@Override
public void onSelectedFilePaths(String[] files) {
results = new Uri[files.length];
for (int i = 0; i < files.length; i++) {
String filePath = new File(files[i]).getAbsolutePath();
if (!filePath.startsWith("file://")) {
filePath = "file://" + filePath;
}
results[i] = Uri.parse(filePath);
Log.d(LOG_TAG, "file path: " + filePath);
Log.d(LOG_TAG, "file uri: " + String.valueOf(results[i]));
}
mUploadMessage.onReceiveValue(results);
mUploadMessage = null;
}
});
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
if (null != mUploadMessage) {
if (null != results && results.length >= 1) {
mUploadMessage.onReceiveValue(results);
} else {
mUploadMessage.onReceiveValue(null);
}
}
mUploadMessage = null;
}
});
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialogInterface) {
if (null != mUploadMessage) {
if (null != results && results.length >= 1) {
mUploadMessage.onReceiveValue(results);
} else {
mUploadMessage.onReceiveValue(null);
}
}
mUploadMessage = null;
}
});
dialog.show();
}
public class PQChromeClient extends WebChromeClient {
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
// Double check that we don't have any existing callbacks
if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(null);
}
mUploadMessage = filePathCallback;
openFileSelectionDialog();
return true;
}
}
//Add this method to show Dialog when the required permission has been granted to the app.
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
switch (requestCode) {
case FilePickerDialog.EXTERNAL_READ_PERMISSION_GRANT: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (dialog != null) {
openFileSelectionDialog();
}
} else {
//Permission has not been granted. Notify the user.
Toast.makeText(WebBrowserScreen.this, "Permission is Required for getting list of files", Toast.LENGTH_SHORT).show();
}
}
}
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Check if the key event was the Back button and if there's history
if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
webView.goBack();
return true;
}
// If it wasn't the Back key or there's no web page history, bubble up to the default
// system behavior (probably exit the activity)
return super.onKeyDown(keyCode, event);
}
public class PQClient extends WebViewClient {
ProgressBar progressDialog;
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// If url contains mailto link then open Mail Intent
if (url.contains("mailto:")) {
// Could be cleverer and use a regex
//Open links in new browser
view.getContext().startActivity(
new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
// Here we can open new activity
return true;
} else {
// Stay within this webview and load url
view.loadUrl(url);
return true;
}
}
// Show loader on url load
public void onPageStarted(WebView view, String url, Bitmap favicon) {
// Then show progress Dialog
// in standard case YourActivity.this
if (progressDialog == null) {
progressDialog = findViewById(R.id.progressBar);
progressDialog.setVisibility(View.VISIBLE);
}
}
// Called when all page resources loaded
public void onPageFinished(WebView view, String url) {
webView.loadUrl("javascript:(function(){ " +
"document.getElementById('Android-app').style.display='none';})()");
try {
// Close progressDialog
progressDialog.setVisibility(View.GONE);
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
}
私はこの問題をほぼ1か月間調査しました。すべてが失敗しました。複数のWebサイトで見られるすべてのコードはうまく機能しません。しかし、ここに最良の解決策があります
https://github.com/chiclaim/Android-webview-upload-file
1)クローンまたはダウンロードをクリックします
2)ローカルディレクトリでZipファイルを取得する
3)Zipファイルを解凍します
4)Android studioを開きます
5)ファイルに移動---->開く--->コンテンツを解凍したディレクトリに移動します。
6)webView.loadUrl( "your url hre");で必要なWeb URLを変更します。 MainActivity.Javaで
7)バージョンAndroid studio 3.4.2で正常に動作します
KitKatでは、Storage Access Frameworkを使用できます。