Subir una imagen de cámara o galería en WebView

WebView en esta aplicación abre una página con el botón de subir.

Página en la vista web con botón de subir

A continuación se muestra el bloque de código que permite abrir un cuadro de diálogo para cargar la imagen desde la galería o la cámara.

Dentro de mi actividad tengo:

private WebView wv; //make HTML upload button work in 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; } } 

Dentro de onCreate tengo lo siguiente:

  wv.setWebChromeClient(new WebChromeClient() { private Uri imageUri; public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType ) { File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyApp"); // Create the storage directory if it does not exist if (! imageStorageDir.exists()){ imageStorageDir.mkdirs(); } File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg"); imageUri = Uri.fromFile(file); final List<Intent> cameraIntents = new ArrayList<Intent>(); final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); final PackageManager packageManager = getPackageManager(); final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0); for(ResolveInfo res : listCam) { final String packageName = res.activityInfo.packageName; final Intent i = new Intent(captureIntent); i.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name)); i.setPackage(packageName); i.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); cameraIntents.add(i); } mUploadMessage = uploadMsg; Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType("image/*"); Intent chooserIntent = Intent.createChooser(i,"Image Chooser"); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{})); MainActivity.this.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE); } 

Puedo ver la opción para la cámara, la galería de la imagen y el explorador del archivo encendido pulsando el botón de la carga por teletratamiento. Opción de carga de cámara, galería y explorador de archivos

El Explorador de archivos y la Galería funcionan como se esperaba. El problema es que, cuando tomo una foto usando la cámara, no se carga en la opción "elegir archivo" que muestra el estado "Sin archivo elegido".

SELECCIONAR LA CÁMARA:

cámara

ON TAKING INSTANTÁNEO USANDO LA CÁMARA: las opciones de la parte posterior y de la verificación aparecen.

Instantánea con cámara

EN ELECCION DE LA MARCA DE CONTROL:

EL ARCHIVO NO ESTÁ CARGADO 🙁 EN "CHOOSE FILE" OPTION

Introduzca aquí la descripción de la imagen

LO QUE SE ESPERA:

Imagen cargada

Comprobé que tengo el permiso de escritura adecuado y por lo tanto un directorio llamado "MyApp" se genera y la imagen se almacena dentro de ella (si se toma por invocar cámara después de hacer clic en el botón de carga en la página web).

¿Cómo programaticamente decir a la aplicación para elegir la foto tomada de la cámara (que se almacenó en el directorio MyApp) después de golpear la marca de verificación?

Supongo que el método onActivityResult es realmente llamado, pero el Intent intent del tercer parámetro es nulo. Parece que es un error de los teléfonos Nexus.

Pero puede guardar la imagen de salida uri en la variable privada y usarla en lugar de la intención:

 private Uri imageUri; private void showAttachmentDialog(ValueCallback<Uri> uploadMsg) { this.mUploadMessage = uploadMsg; File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "TestApp"); if (!imageStorageDir.exists()) { imageStorageDir.mkdirs(); } File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg"); this.imageUri = Uri.fromFile(file); // save to the private variable final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType("image/*"); Intent chooserIntent = Intent.createChooser(i, "Image Chooser"); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[] { captureIntent }); this.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { if (requestCode == FILECHOOSER_RESULTCODE) { if (null == this.mUploadMessage) { return; } Uri result; if (resultCode != RESULT_OK) { result = null; } else { result = intent == null ? this.imageUri : intent.getData(); // retrieve from the private variable if the intent is null } this.mUploadMessage.onReceiveValue(result); this.mUploadMessage = null; } } 

En este código agregué la variable imageUri a la actividad y la usé en ambos métodos.

Actualización 6/18: Esto no parece funcionar en Samsung Galaxy S2 con Android 4.2.1. Esto funcionó bien en HTC One X + con 4.1.2. Sea advertido.


Me enfrenté al mismo problema. Aquí está el problema y cómo lo solucioné.

Problema:

Cuando openFileChooser se llama, el objeto de devolución de llamada ValueCallback<Uri> se está pasando. Esta es la llamada real a la vista web cuando tenemos un archivo listo para ello. Guardamos este objeto en mUploadMessage y usamos la función mUploadMessage.onReceiveValue() en onActivityResult para devolver el archivo a Webview. Mientras elige la cámara, haga clic en una imagen, guárdela y vuelva a la actividad de la vista web, nuestra actividad se puede reciclar, lo que significa que en realidad perderemos el objeto de devolución de llamada mUploadMessage . Y por lo tanto, el archivo no se puede pasar de nuevo a webview para cargar.

Fijar:

Corrección implica la ejecución de algunos javascript, por lo que habilitar javascript en webview. Básicamente, obtendremos otro objeto de devolución de llamada si perdemos el anterior.

Necesitamos crear un campo booleano ' mUploadFileOnLoad ' y tres campos.

  private int mReturnCode; private int mResultCode; private Intent mResultIntent; private boolean mUploadFileOnLoad = false; 

Cuando onActivityResult a nuestra actividad de cámara, onActivityResult será llamado. Si se reconstruye la actividad, mUploadMessage es null. Por lo tanto, mUploadFileOnLoad los parámetros en campos y estableceremos mUploadFileOnLoad como true y devolveremos. La otra parte es muy importante.

  @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { //if the callback object has been recycled if(null==mUploadMessage) { //Save the result mReturnCode = requestCode; mResultCode = resultCode; mResultIntent = intent; //remember to invoke file upload using Javascript mUploadFileOnLoad = true; return; }else mUploadFileOnLoad = false; //rest of the code } 

Las partes importantes de esta solución están en WebViewClient y WebChromeClient

  new WebChromeClient() { //Other overloaded functions //See http://stackoverflow.com/a/15423907/375093 for full explanation //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) { //If we lost the callback object if(mUploadFileOnLoad) { mUploadMessage = uploadMsg; //use the saved result objects to invoke onActivityResult onActivityResult(mReturnCode, mResultCode, mResultIntent); return; } //Rest of the code.... } 

y

  new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { if(mUploadFileOnLoad) { webview.loadUrl("javascript:document.getElementById('my_file').click()"); } } 

En lo anterior, my_file es el id del elemento <input> en la página web.

 <input type="file" id="my_file"> 

Por lo tanto, en resumen, lo que hicimos es – Cuando no tenemos un objeto de devolución de llamada, guardamos los datos recibidos de otra actividad y establecemos mUploadFileOnLoad como true y esperamos a que la página se cargue. Cuando carga la página, usamos Javascript para invocar el selector de archivos para que obtengamos un objeto de devolución de llamada. Como ya tenemos resultados, invocamos onActivityResult y devuelve. OnActivityResult ahora tiene una devolución de llamada desde la webview.

Después de luchar mucho encontré un código que funciona para tomar archivos de galera y cámara de 5.0 + dispositivos

  private ValueCallback<Uri> mUploadMessage; private Uri mCapturedImageURI = null; private ValueCallback<Uri[]> mFilePathCallback; private String mCameraPhotoPath; private static final int INPUT_FILE_REQUEST_CODE = 1; private static final int FILECHOOSER_RESULTCODE = 1; private File createImageFile() throws IOException { // Create an image file name String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES); File imageFile = File.createTempFile( imageFileName, /* prefix */ ".jpg", /* suffix */ storageDir /* directory */ ); return imageFile; } 

Esto es la inicialización y la configuración de webview

  mWebView= (WebView) findViewById(R.id.webview); mWebView.getSettings().setJavaScriptEnabled(true); mWebView.getSettings().setPluginState(WebSettings.PluginState.OFF); mWebView.getSettings().setLoadWithOverviewMode(true); mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); mWebView.getSettings().setUseWideViewPort(true); mWebView.getSettings().setUserAgentString("Android Mozilla/5.0 AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"); mWebView.getSettings().setAllowFileAccess(true); mWebView.getSettings().setAllowFileAccess(true); mWebView.getSettings().setAllowContentAccess(true); mWebView.getSettings().supportZoom(); mWebView.loadUrl(Common.adPostUrl); mWebView.setWebViewClient(new WebViewClient() { public boolean shouldOverrideUrlLoading(WebView view, String url) { // do your handling codes here, which url is the requested url // probably you need to open that url rather than redirect: if ( url.contains(".pdf")){ Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse(url), "application/pdf"); try{ view.getContext().startActivity(intent); } catch (ActivityNotFoundException e) { //user does not have a pdf viewer installed } } else { mWebView.loadUrl(url); } return false; // then it is not handled by default action } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { Log.e("error",description); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { //show progressbar here super.onPageStarted(view, url, favicon); } @Override public void onPageFinished(WebView view, String url) { //hide progressbar here } }); mWebView.setWebChromeClient(new ChromeClient()); 

Y aquí está mi método ChomeClient ()

 public class ChromeClient extends WebChromeClient { // For Android 5.0 public boolean onShowFileChooser(WebView view, ValueCallback<Uri[]> filePath, WebChromeClient.FileChooserParams fileChooserParams) { // Double check that we don't have any existing callbacks if (mFilePathCallback != null) { mFilePathCallback.onReceiveValue(null); } mFilePathCallback = filePath; Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 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(Common.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 contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT); contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE); contentSelectionIntent.setType("image/*"); 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, INPUT_FILE_REQUEST_CODE); return true; } // openFileChooser for Android 3.0+ public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) { mUploadMessage = uploadMsg; // Create AndroidExampleFolder at sdcard // Create AndroidExampleFolder at sdcard File imageStorageDir = new File( Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES) , "AndroidExampleFolder"); if (!imageStorageDir.exists()) { // Create AndroidExampleFolder at sdcard imageStorageDir.mkdirs(); } // Create camera captured image file path and name File file = new File( imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg"); mCapturedImageURI = Uri.fromFile(file); // Camera capture image intent final Intent captureIntent = new Intent( android.provider.MediaStore.ACTION_IMAGE_CAPTURE); captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedImageURI); Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType("image/*"); // Create file chooser intent Intent chooserIntent = Intent.createChooser(i, "Image Chooser"); // Set camera intent to file chooser chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS , new Parcelable[] { captureIntent }); // On select image call onActivityResult method of activity startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE); } // openFileChooser for Android < 3.0 public void openFileChooser(ValueCallback<Uri> uploadMsg) { openFileChooser(uploadMsg, ""); } //openFileChooser for other Android versions public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) { openFileChooser(uploadMsg, acceptType); } } 

// aquí está mi método onActivityResult para manejar los datos de la galería o la intención de la cámara

 @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (requestCode != INPUT_FILE_REQUEST_CODE || mFilePathCallback == null) { super.onActivityResult(requestCode, resultCode, data); return; } Uri[] results = null; // Check that the response is a good one if (resultCode == Activity.RESULT_OK) { if (data == null) { // If there is not data, then we may have taken a photo if (mCameraPhotoPath != null) { results = new Uri[]{Uri.parse(mCameraPhotoPath)}; } } else { String dataString = data.getDataString(); if (dataString != null) { results = new Uri[]{Uri.parse(dataString)}; } } } mFilePathCallback.onReceiveValue(results); mFilePathCallback = null; } else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { if (requestCode != FILECHOOSER_RESULTCODE || mUploadMessage == null) { super.onActivityResult(requestCode, resultCode, data); return; } if (requestCode == FILECHOOSER_RESULTCODE) { if (null == this.mUploadMessage) { return; } Uri result = null; try { if (resultCode != RESULT_OK) { result = null; } else { // retrieve from the private variable if the intent is null result = data == null ? mCapturedImageURI : data.getData(); } } catch (Exception e) { Toast.makeText(getApplicationContext(), "activity :" + e, Toast.LENGTH_LONG).show(); } mUploadMessage.onReceiveValue(result); mUploadMessage = null; } } return; } 

Y aquí están los permisos necesarios para abrir la cámara

 <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA2" /> // for new versions api 21+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> 

Nota: Mi código también contiene el código para los dispositivos que ejecutan 3.0+ también pero nunca los probé, el código anterior funcionó en los emuladores Lolipop, Marshmallow y Nougat. Una cosa más, si usted ve y el icono del sistema Android en lugar de la cámara, significa que tiene muchas aplicaciones disponibles en su dispositivo para manejar la cámara.

Asegúrese de no tener android:launchMode="singleInstance" en el archivo de manifiesto

Lo siento mi ingles

Esta es la solución.

La primera, se define miembro de archivo como este.

public File mTempFile;

Su openFileChooser está bien.

onActivityResult es tan importante.

Camera app no ​​url de retorno, pero ValueCallback debe tener url.

Obtener uri de mTempFile.

Esto es trabajo

Yo uso así.

 if ( mTempFile.exists() ) { mUploadMessage.onReceiveValue(Uri.fromFile(mTempFile)); mUploadMessage = null; } else { mUploadMessage.onReceiveValue(result); mUploadMessage = null; } 

Si mTempFile existe que se llamó cámara, otro caso de la galería.

  • Vídeo incrustado HTML5 de Android
  • Acceso web automático de Webview de Android al sitio Web de https estableciendo cookies de token
  • WebView en Android 4.4 y escala inicial
  • Use caracteres especiales en strings.xml para cargar en WebView
  • Para mostrar una página html estática en android
  • Buscando un menú desplegable en la vista web de Android que esté funcionando
  • Android No se permite cargar recursos locales: file: /// android_asset
  • WebView de Android, escala de imagen para adaptarse a la pantalla
  • Cargando HTML local con $ .load
  • ¿Cómo puedo borrar el contenido de WebView anterior antes de cargar el siguiente WebView?
  • Utilizar Chrome como motor de renderizado para la vista web de Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.