Java Android 4.4 WebView 文件选择器打不开?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23568792/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android 4.4 WebView file chooser not opening?
提问by MikeCon94
We are creating an app which uses the webview and will access a page where the user needs to upload a file. We are experiencing problems with Android 4.4 where the file chooser does not open and clicking the upload button causes nothing to happen. This functionality works with earlier versions using the openFileChooser method like so:
我们正在创建一个使用 webview 的应用程序,并将访问用户需要上传文件的页面。我们在 Android 4.4 中遇到问题,其中文件选择器无法打开并且单击上传按钮不会发生任何事情。此功能适用于使用 openFileChooser 方法的早期版本,如下所示:
webview.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/*");
MainActivity.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("*/*");
MainActivity.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/*");
MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), MainActivity.FILECHOOSER_RESULTCODE);
}
});
I have spent a good amount of time searching for a way to do it on 4.4 but have had no luck. Has anyone managed to do this?
我花了很多时间寻找在 4.4 上执行此操作的方法,但没有运气。有没有人设法做到这一点?
采纳答案by magmike
WebView is working as intended
If I understand correctly what the above link says , you (me and probably some hundreds more developers) are looking for a hack
如果我正确理解上述链接的意思,您(我和可能还有数百名开发人员)正在寻找黑客
回答by A.Salam Alasaadi
I have tried the File Chooser in a webview using the latest Android release 4.4.3, and it's working fine. I guess Google has fixed the issue.
我已经使用最新的 Android 版本 4.4.3 在 webview 中尝试了文件选择器,它工作正常。我猜谷歌已经解决了这个问题。
回答by Thaichor Seng
回答by ViliusK
Found a solution that worked for me. I've added one more rule in proguard-android.txt
:
找到了一个对我有用的解决方案。我在 中添加了另一条规则proguard-android.txt
:
-keepclassmembers class * extends android.webkit.WebChromeClient {
public void openFileChooser(...);
}
回答by Tito Leiva
I was working on this issue too, and the problem and here is my solution
我也在研究这个问题,这个问题是我的解决方案
private class MyWebChromeClient extends WebChromeClient {
/**
* This is the method used by Android 5.0+ to upload files towards a web form in a Webview
*
* @param webView
* @param filePathCallback
* @param fileChooserParams
* @return
*/
@Override
public boolean onShowFileChooser(
WebView webView, ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
if (mFilePathCallback != null) {
mFilePathCallback.onReceiveValue(null);
}
mFilePathCallback = filePathCallback;
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");
Intent[] intentArray = getCameraIntent();
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Seleccionar Fuente");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);
return true;
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
mProgressBar.setVisibility(View.VISIBLE);
WebActivity.this.setValue(newProgress);
super.onProgressChanged(view, newProgress);
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d("LogTag", message);
result.confirm();
return true;
}
/**
* Despite that there is not a Override annotation, this method overrides the open file
* chooser function present in Android 3.0+
*
* @param uploadMsg
* @author Tito_Leiva
*/
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = getChooserIntent(getCameraIntent(), getGalleryIntent("image/*"));
i.addCategory(Intent.CATEGORY_OPENABLE);
WebActivity.this.startActivityForResult(Intent.createChooser(i, "Selecciona la imagen"), FILECHOOSER_RESULTCODE);
}
public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
mUploadMessage = uploadMsg;
Intent i = getChooserIntent(getCameraIntent(), getGalleryIntent("*/*"));
i.addCategory(Intent.CATEGORY_OPENABLE);
WebActivity.this.startActivityForResult(
Intent.createChooser(i, "Selecciona la imagen"),
FILECHOOSER_RESULTCODE);
}
/**
* Despite that there is not a Override annotation, this method overrides the open file
* chooser function present in Android 4.1+
*
* @param uploadMsg
* @author Tito_Leiva
*/
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
mUploadMessage = uploadMsg;
Intent i = getChooserIntent(getCameraIntent(), getGalleryIntent("image/*"));
WebActivity.this.startActivityForResult(Intent.createChooser(i, "Selecciona la imagen"), FILECHOOSER_RESULTCODE);
}
private Intent[] getCameraIntent() {
// Determine Uri of camera image to save.
Intent takePictureIntent = new Intent(WebActivity.this, CameraActivity.class);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
} catch (IOException ex) {
// Error occurred while creating the File
Log.e(TAG, "Unable to create Image File", ex);
}
// Continue only if the File was successfully created
if (photoFile != null) {
mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}
Intent[] intentArray;
if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else {
intentArray = new Intent[0];
}
return intentArray;
}
private Intent getGalleryIntent(String type) {
// Filesystem.
final Intent galleryIntent = new Intent();
galleryIntent.setType(type);
galleryIntent.addCategory(Intent.CATEGORY_OPENABLE);
galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
return galleryIntent;
}
private Intent getChooserIntent(Intent[] cameraIntents, Intent galleryIntent) {
// Chooser of filesystem options.
final Intent chooserIntent = Intent.createChooser(galleryIntent, "Seleccionar Fuente");
// Add the camera options.
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents);
return chooserIntent;
}
}
One problem that I solved is the uri delivered for the onActivityResult()
method does not have extension. To solve this I use this method
我解决的一个问题是为该onActivityResult()
方法提供的 uri没有扩展名。为了解决这个问题,我使用这种方法
public static Uri savePicture(Context context, Bitmap bitmap, int maxSize) {
int cropWidth = bitmap.getWidth();
int cropHeight = bitmap.getHeight();
if (cropWidth > maxSize) {
cropHeight = cropHeight * maxSize / cropWidth;
cropWidth = maxSize;
}
if (cropHeight > maxSize) {
cropWidth = cropWidth * maxSize / cropHeight;
cropHeight = maxSize;
}
bitmap = ThumbnailUtils.extractThumbnail(bitmap, cropWidth, cropHeight, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
File mediaStorageDir = new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
context.getString(R.string.app_name)
);
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
return null;
}
}
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile = new File(
mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg"
);
// Saving the bitmap
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
FileOutputStream stream = new FileOutputStream(mediaFile);
stream.write(out.toByteArray());
stream.close();
} catch (IOException exception) {
exception.printStackTrace();
}
// Mediascanner need to scan for the image saved
Intent mediaScannerIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri fileContentUri = Uri.fromFile(mediaFile);
mediaScannerIntent.setData(fileContentUri);
context.sendBroadcast(mediaScannerIntent);
return fileContentUri;
}
Finally, onActivityResult()
method is
最后,onActivityResult()
方法是
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
if (resultCode == RESULT_OK) {
// This is for Android 4.4.4- (JellyBean & KitKat)
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage) {
super.onActivityResult(requestCode, resultCode, intent);
return;
}
final boolean isCamera;
if (intent == null) {
isCamera = true;
} else {
final String action = intent.getAction();
if (action == null) {
isCamera = false;
} else {
isCamera = action.equals(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
}
}
Uri selectedImageUri;
if (isCamera) {
selectedImageUri = mOutputFileUri;
mUploadMessage.onReceiveValue(selectedImageUri);
mUploadMessage = null;
return;
} else {
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), intent.getData());
selectedImageUri = intent == null ? null : ImageUtility.savePicture(this, bitmap, 1400);
mUploadMessage.onReceiveValue(selectedImageUri);
mUploadMessage = null;
return;
} catch (IOException e) {
e.printStackTrace();
}
}
// And this is for Android 5.0+ (Lollipop)
} else if (requestCode == INPUT_FILE_REQUEST_CODE) {
Uri[] results = null;
// Check that the response is a good one
if (resultCode == Activity.RESULT_OK) {
if (intent == null) {
// If there is not data, then we may have taken a photo
if (mCameraPhotoPath != null) {
results = new Uri[]{Uri.parse(mCameraPhotoPath)};
}
} else {
Bitmap bitmap = null;
try {
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), intent.getData());
} catch (IOException e) {
e.printStackTrace();
}
Uri dataUri = ImageUtility.savePicture(this, bitmap, 1400);
if (dataUri != null) {
results = new Uri[]{dataUri};
}
}
}
mFilePathCallback.onReceiveValue(results);
mFilePathCallback = null;
return;
}
} else {
super.onActivityResult(requestCode, resultCode, intent);
return;
}
}
回答by Mimo
What I have done to fix this problem it is to implement CrossWalk into my project. I know the size of CrossWalk is significant (adds Chromium to your project) but I really needed this to work fine inside a native app. I documented how I implemented this in here: http://maurizionapoleoni.de/blog/implementing-a-file-input-with-camera-support-in-android-with-crosswalk/
我为解决这个问题所做的是在我的项目中实施 CrossWalk。我知道 CrossWalk 的大小很重要(将 Chromium 添加到您的项目中),但我真的需要它在本机应用程序中正常工作。我在这里记录了我是如何实现的:http: //maurizionapoleoni.de/blog/implementing-a-file-input-with-camera-support-in-android-with-crosswalk/
回答by Salman Nazir
This Code worked for me.
此代码对我有用。
private class MyWebChromeClient extends 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 uploadMsg, String acceptType) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
MainActivity1.this.startActivityForResult(Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE);
}
//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("*/*");
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("*/*");
startActivityForResult(Intent.createChooser(i, "File Chooser"), 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 = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
intent = fileChooserParams.createIntent();
}
try {
startActivityForResult(intent, REQUEST_SELECT_FILE);
} catch (ActivityNotFoundException e) {
uploadMessage = null;
Toast.makeText(getApplicationContext(), "Cannot Open File Chooser", Toast.LENGTH_LONG).show();
return false;
}
return true;
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d("LogTag", message);
result.confirm();
return true;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SELECT_FILE) {
if (uploadMessage == null) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
}
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 = data == null || resultCode != MainActivity.RESULT_OK ? null : data.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
}
回答by Daniel Nyamasyo
You need to implement a WebviewClient
class. You can check these example.
你需要实现一个WebviewClient
类。您可以查看这些示例。
webview.setWebViewClient(new myWebClient());
The web.setWebChromeClient(new WebChromeClient() {
//
}
Create class called mywebClient and add the following code to implement onPageStarted()
function, shouldOvverideLoading()
function and onActivityresult()
function as shown below.
创建名为 mywebClient 的类并添加以下代码以实现如下所示的onPageStarted()
功能,shouldOvverideLoading()
功能和onActivityresult()
功能。
public class myWebClient extends WebViewClient {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
}
//flipscreen not loading again
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
@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;
}
}
回答by Sazzad Hissain Khan
Android Kotlin,
安卓科特林,
MyWebViewChromeClient class:
MyWebViewChromeClient 类:
class MyWebViewChromeClient(private val mContext: MyMainActivity): WebChromeClient() {
override fun onShowFileChooser(webView: WebView, filePathCallback: ValueCallback<Array<Uri>>, fileChooserParams: FileChooserParams): Boolean {
mContext.mUploadMessageArray?.onReceiveValue(null)
mContext.mUploadMessageArray = filePathCallback
val contentSelectionIntent = Intent(Intent.ACTION_GET_CONTENT)
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE)
contentSelectionIntent.type = "*/*"
val intentArray: Array<Intent?> = arrayOfNulls(0)
val chooserIntent = Intent(Intent.ACTION_CHOOSER)
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent)
chooserIntent.putExtra(Intent.EXTRA_TITLE, "File Chooser")
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray)
mContext.startActivityForResult(chooserIntent, mContext.FILECHOOSER_RESULTCODE)
return true
}
MyMainActivity class:
MyMainActivity 类:
class MyMainActivity : MyBaseActivity() {
val FILECHOOSER_RESULTCODE = 1001
var mUploadMessageArray: ValueCallback<Array<Uri>>? = null
private lateinit var mWebView: MyWebView
private lateinit var mContext: Context
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == FILECHOOSER_RESULTCODE) {
if (mUploadMessageArray == null) {
return
}
val result = if (intent == null || resultCode != Activity.RESULT_OK) null else data?.data
result?.let {
val uriArray: Array<Uri> = arrayOf(it)
mUploadMessageArray?.onReceiveValue(uriArray)
mUploadMessageArray = null
} ?: kotlin.run {
mUploadMessageArray?.onReceiveValue(null)
mUploadMessageArray = null
}
}
}
}