Android 4.4的WebView文件select器不能打开?

我们正在创build一个使用webview的应用程序,并将访问用户需要上传文件的页面。 我们遇到Android 4.4的问题,文件select器没有打开,点击上传button不会导致任何事情发生。 这个function可以在早期版本中使用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); } }); 

我花了大量的时间寻找一种方法来做到这一点,但没有运气。 有没有人设法做到这一点?

Solutions Collecting From Web of "Android 4.4的WebView文件select器不能打开?"

WebView正在按预期工作

如果我正确地理解了上面的链接说的是什么,那么你(我可能还有几百个开发者)正在寻找黑客

看看这个 。 由于api 19 4.4,webview已经迁移到使用Chromium,而不是WebChromeClient了。

我也在研究这个问题,问题在这里是我的解决scheme

 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; } } 

我解决的一个问题是uri交付的onActivityResult()方法没有扩展名。 为了解决这个问题我使用这个方法

 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; } 

最后, 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; } } 

find了适合我的解决scheme。 我在proguard-android.txt增加了一个规则:

 -keepclassmembers class * extends android.webkit.WebChromeClient { public void openFileChooser(...); } 

这个代码为我工作。

  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; } } 

我已经做了什么来解决这个问题,就是在我的项目中实现CrossWalk。 我知道CrossWalk的大小是重要的(将Chromium添加到您的项目中),但是我真的需要在本地应用程序内正常工作。 我logging了我是如何在这里实现的: http : //maurizionapoleoni.de/blog/implementing-a-file-input-with-camera-support-in-android-with-crosswalk/

你需要实现一个WebviewClient类。 你可以检查这些例子

 webview.setWebViewClient(new myWebClient()); The web.setWebChromeClient(new WebChromeClient() { // } 

创build类名为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; } } 

下载演示

我已经使用最新的Android版本4.4.3在webview中尝试了文件select器,并且工作正常。 我猜Google已经解决了这个问题。