I have this activity in which the user can either choose one image from Gallery or just take a picture and (along with other data) upload it to a website.
So far I've encountered 2 different problems:
1) If I try it with a picture from the gallery, I get an IOException with message /external/images/media/2305: open failed: ENOENT (No such file or directory) That happens when it comes to open the file stream.
2) If I try it by taking the picture, it goes ok, but the encoded data string is composed of "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" (really longer, but only A's) and I guess that's not a good sign. This is only a guess since I still cannot properly upload it to the website, but different pictures showing the same data string just smells funny.
The code here
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case TAKE_PICTURE:
if (resultCode == Activity.RESULT_OK) {
//Uri selectedImage = imageUri;
loadImage(imageUri);
}
break;
case SELECT_PHOTO:
if(resultCode == Activity.RESULT_OK){
imageUri = data.getData();
loadImage(imageUri);
}
}
}
This is how I load the image (either pic taken or from the gallery) onto the ImageView. It works ok.
public void loadImage(Uri selectedImage){
mActivity.getContentResolver().notifyChange(selectedImage, null);
ContentResolver cr = mActivity.getContentResolver();
Bitmap bitmap;
try {
bitmap = android.provider.MediaStore.Images.Media
.getBitmap(cr, selectedImage);
ivPicture.setImageBitmap(bitmap);
ivPicture.setVisibility(View.VISIBLE);
mActivity.croutonInfo(selectedImage.toString());
} catch (Exception e) {
mActivity.croutonAlert("Failed to load");
e("Camera " + e.toString());
}
}
This is the method I use to mock the data upload. When I get the API it will have an asynctask to deal with the http transfer, so far it only puts the data into a logicless transfer object
public void uploadTapa() throws IOException{
mActivity.croutonInfo("subiendo tapa ");
d("uploadTapa new ");
TapaUploadParametros tup = new TapaUploadParametros();
d("uploadTapa bar: " + nombreBar);
tup.setBarNombre(etBarName.getText().toString());
d("uploadTapa tapa: " + nombreTapa);
tup.setNombre(etTapaName.getText().toString());
d("uploadTapa municipio: " + municipio);
tup.setLocalidad(municipio);
d("uploadTapa provincia: " + provincia);
tup.setProvincia(provincia);
d("uploadTapa tipologiaId: " + tipologiaId);
tup.setTipo(tipologiaId);
d("uploadTapa precioId: " + precioId);
tup.setPrecio(precioId);
String encodedImage = encodeImgForHTTP(imageUri);
d("uploadTapa encoded image: " + encodedImage);
tup.setPic(encodedImage);
d("uploadTapa direccionBar: " + direccionBar);
tup.setBarDireccion(direccionBar);
}
And this is the method to encode the image for http transfer. Images from gallery fail just after "before opening stream"
private String encodeImgForHTTP (Uri imageUri) throws IOException{
ContentResolver cr = mActivity.getContentResolver();
d("encodeImgForHTTP before opening stream ");
FileInputStream fis = new FileInputStream(imageUri.getPath());
d("encodeImgForHTTP after opening stream ");
// Get binary bytes for encode
byte[] imageBytes = new byte[fis.available()];
d("encodeImgForHTTP after getting byte array ");
// base 64 encode for text transmission (HTTP)
d("encodeImgForHTTP pre 64: " + imageBytes);
String data_string = Base64.encodeToString(imageBytes, Base64.URL_SAFE);
d("encodeImgForHTTP before returning the encoded data string " + data_string);
return data_string;
}
What am I doing wrong with the gallery images? Why does the encoding of different pictures look the same?
I guess I finally got it to work. First I used Emil's advice and saved the image. DCIM_PATH is the path to the DCIM folder.
public void takePhoto() {
String directoryPath = DCIM_PATH;
d("takePhoto directoryPath: " + directoryPath);
this.pictureFileName = Long.toHexString(System.currentTimeMillis())+".jpg";
String filePath = directoryPath + pictureFileName ;
File directory = new File(directoryPath);
if (!directory.exists()) { // in case there's no DCIM folder
directory.mkdirs(); // just create it
}
d("takePhoto filePath: " + filePath);
this.imageUri = Uri.parse(filePath);
d("takePhoto imageUri: " + filePath);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// here's where I tell the intent where to save the file
intent.putExtra(
MediaStore.EXTRA_OUTPUT,Uri.fromFile( new File(filePath) )
);
startActivityForResult(intent, TAKE_PICTURE);
}
I had to use two different methods for loading the picture to the imageview. If it's a picture just taken, I use this one:
public void loadImageJustTaken(Uri selectedImage) {
mActivity.getContentResolver().notifyChange(selectedImage, null);
Bitmap bitmap =
BitmapFactory.decodeFile(imageUri.getPath());
ivPicture.setImageBitmap(bitmap);
ivPicture.setVisibility(View.VISIBLE);
}
But to use one from the gallery I have to use the contentResolver
public void loadImage(Uri selectedImage){
imageUri = selectedImage;
mActivity.getContentResolver().notifyChange(selectedImage, null);
ContentResolver cr = mActivity.getContentResolver();
Bitmap bitmap;
try {
bitmap = android.provider.MediaStore.Images.Media
.getBitmap(cr, imageUri);
ivPicture.setImageBitmap(bitmap);
ivPicture.setVisibility(View.VISIBLE);
mActivity.croutonInfo(imageUri.getPath());
} catch (Exception e) {
e("Camera " + e.toString());
}
}
When I want to upload the image, I have to encode it. This method works as long as you provide it with the right file path
private String encodeImgForHTTP (Uri imageUri) throws IOException{
String realPicPath = getPath(imageUri);
d("encodeImgForHTTP before opening stream " + realPicPath);
FileInputStream fis = new FileInputStream(realPicPath);
d("encodeImgForHTTP after opening stream ");
// Get binary bytes for encode
byte[] imageBytes = IOUtils.toByteArray(fis);
d("encodeImgForHTTP after getting byte array ");
// base 64 encode for text transmission (HTTP)
//String data_string = Base64.encodeToString(data, Base64.URL_SAFE);
d("encodeImgForHTTP pre 64: " + imageBytes);
String data_string = Base64.encodeToString(imageBytes, Base64.URL_SAFE);
d("encodeImgForHTTP before returning the encoded data string " + data_string);
return data_string;
}
And here's how I get the "real path" for the picture:
public String getPath(Uri uri) throws IOException {
String[] projection = { MediaStore.Images.Media.DATA };
Cursor cursor = mActivity.
managedQuery(uri, projection, null, null, null);
if (cursor == null){ // with pictures just taken, the uri returned by the onActivityResult makes cursor to be null. Following method takes care of that
uri = saveMediaEntry(imageUri.getPath(), pictureFileName, "");
d("cursor nulo, segundo cursor con uri " + uri.getPath());
cursor = mActivity.
managedQuery(uri, projection, null, null, null);
}
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
The method saveMediaEntry creates an entry to the device's media database returning its Uri. Using that Uri, the cursor now will point to the picture file we want
private Uri saveMediaEntry(
String imagePath,String title,String description) throws IOException {
ExifInterface exif = new ExifInterface(imagePath);
ContentValues v = new ContentValues();
v.put(Images.Media.TITLE, title);
v.put(Images.Media.DISPLAY_NAME, title);
v.put(Images.Media.DESCRIPTION, description);
v.put(Images.Media.DATE_ADDED, System.currentTimeMillis());
v.put(Images.Media.DATE_TAKEN, exif.getAttribute(ExifInterface.TAG_DATETIME));
//v.put(Images.Media.DATE_MODIFIED, dateTaken) ;
v.put(Images.Media.MIME_TYPE, "image/jpeg");
v.put(Images.Media.ORIENTATION, exif.getAttribute(ExifInterface.TAG_ORIENTATION));
File f = new File(imagePath) ;
File parent = f.getParentFile() ;
String path = parent.toString().toLowerCase() ;
String name = parent.getName().toLowerCase() ;
v.put(Images.ImageColumns.BUCKET_ID, path.hashCode());
v.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, name);
v.put(Images.Media.SIZE,f.length()) ;
f = null ;
v.put(Images.Media.LATITUDE, exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE));
v.put(Images.Media.LONGITUDE, exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE));
v.put("_data",imagePath) ;
ContentResolver c = mActivity.getContentResolver() ;
return c.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, v);
}
After all this, pictures get loaded OK, and the Base64.encodeToString returns are different for different pictures :)
Hope it helps someone :)