trabajar - manual de programacion android pdf
Cargar una imagen de la cámara o galería en WebView (5)
WebView en esta aplicación abre una página con el botón de carga.
A continuación se muestra el bloque de código que permite abrir un cuadro de diálogo para cargar imágenes de la galería o 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;
}
}
En 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 de cámara, galería de imágenes y explorador de archivos al hacer clic en el botón Cargar.
El Explorador de archivos y la Galería funcionan como se esperaba. El problema es que, cuando tomo una foto con la cámara, no se carga en la opción "elegir archivo" que muestra el estado "No se eligió ningún archivo".
AL SELECCIONAR LA CÁMARA:
AL TOMAR UNA INSTANTÁNEA CON LA CÁMARA: aparecen las opciones de volver y verificar.
AL SELECCIONAR CHECK MARK:
EL ARCHIVO NO SE CARGA :( EN LA OPCIÓN "ELEGIR ARCHIVO"
LO QUE ES ESPERADO:
Comprobé que tenía el permiso de escritura adecuado y, por lo tanto, se generó un directorio llamado "MiAplicación" y la imagen se almacena dentro de ella (si se toma al invocar la cámara después de hacer clic en el botón Cargar en la página web).
¿Cómo decirle programáticamente a la aplicación que elija una foto tomada desde la cámara (que se almacenó en el directorio MyApp) después de presionar la marca de verificación?
Asegúrate de NO tener android:launchMode="singleInstance"
en el archivo de manifiesto
Después de luchar mucho, encontré un código que funciona para sacar los archivos de la galera y la cámara de los dispositivos 5.0+.
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 inicialización y configuración 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 dispositivos que también ejecutan 3.0+, pero nunca los probé; el código anterior funcionaba con los emuladores Lolipop, Marshmallow y Nougat. Una cosa más, si ves e ícono del sistema Android en lugar de la cámara, significa que tienes muchas aplicaciones disponibles en tu dispositivo para manejar la cámara.
Lo siento, mi inglés.
Esta es la solución.
El primero, define un miembro de archivo como este.
public File mTempFile;
your openFileChooser está bien.
onActivityResult
método onActivityResult
es tan importante.
la aplicación de cámara no devuelve la url, pero ValueCallback debe tener url.
Obtenga 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 existe que se llamó cámara, otro caso de la galería.
Supongo que realmente se llama al método onActivityResult
, pero el tercer Intent intent
es nulo. Parece que es un error de los teléfonos Nexus.
Pero puede guardar el uri de la imagen de salida en la variable privada y usarlo 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 18/06: Esto no parece funcionar en Samsung Galaxy S2 con Android 4.2.1. Esto funcionó bien en HTC One X + con 4.1.2. Ten cuidado
Me enfrenté al mismo problema. Aquí está el problema y cómo lo resolví.
Problema:
Cuando se openFileChooser
se openFileChooser
el objeto de devolución de llamada ValueCallback<Uri>
. 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, hace clic en una imagen, la guarda y vuelve a la actividad de la vista web, nuestra actividad puede reciclarse, lo que significa que realmente perdemos el objeto de devolución de llamada mUploadMessage
. Y, por lo tanto, el archivo no se puede pasar de nuevo a la vista web para cargarlo.
Fijar:
La reparación implica la ejecución de algunos javascript, por lo tanto, habilite javascript en webview. Básicamente, obtendremos otro objeto de devolución de llamada si perdimos 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 desde la cámara, se onActivityResult
a onActivityResult
. Si se reconstruye la actividad, mUploadMessage es nulo. Por lo tanto, mUploadFileOnLoad
los parámetros en los campos y estableceremos mUploadFileOnLoad
en true y regresaremos. La parte else 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://.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 la identificación del elemento <input>
en la página web.
<input type="file" id="my_file">
Entonces, en resumen, lo que hicimos fue: cuando no tenemos un objeto de devolución de llamada, guardamos los datos recibidos de otra actividad y configuramos mUploadFileOnLoad
en verdadero y esperamos a que cargue la página. Cuando la página se carga, usamos Javascript para invocar el selector de archivos para que obtengamos un objeto de devolución de llamada. Como ya tenemos resultados, invocamos a onActivityResult
y lo devolvemos. onActivityResult ahora tiene una devolución de llamada desde la vista web.