I am trying to implement the best approach to reduce a file, basicly my app is about plants, i need high quality images, so the first thing i did was use a external library to crop images in thumbnail(so i can mantain aspect ratio) i use 1:1 thumnbnail format.
Every time i take a picture and if everything is ok on activityResult, it calls the builded crop library so i can cut and get the uri, like this:
protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
dateTimer = getTime();
Log.d("codig",String.valueOf(requestCode));
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Log.d("resultOK","resultOK");
CropImage.activity(data.getData())
.setAspectRatio(1,1)
.setInitialCropWindowPaddingRatio(0)
.setActivityTitle("Corte a foto")
.setActivityMenuIconColor(R.color.nephritis)
.setRequestedSize(300, 300, CropImageView.RequestSizeOptions.RESIZE_INSIDE)
.start(this);
}
if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) {
CropImage.ActivityResult result = CropImage.getActivityResult(data);
if (resultCode == RESULT_OK) {
Uri uri = result.getUri();
Bitmap pic = null;
try {
pic = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
} catch (IOException e) {
e.printStackTrace();
}
path = saveToInternalStorage(pic);
Log.d("caminho",path);
showDetailsDialog(data);
} else if (resultCode == CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE) {
Exception error = result.getError();
Log.d("pict12",error.toString());
}
}
}
as you guys can see i have a setRequestSize method there, where i reduce the size of the image (this works, but just sometimes, the text i send as base64 is still to big.
i convert the uri to bitmap and save it on the device file, on next acitivty i get the file
private String saveToInternalStorage(Bitmap bitmapImage){
ContextWrapper cw = new ContextWrapper(getApplicationContext());
// path to /data/data/yourapp/app_data/imageDir
File directory = cw.getDir("imageDir", Context.MODE_PRIVATE);
// Create imageDir
File mypath=new File(directory,"captured.jpg");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mypath);
// Use the compress method on the BitMap object to write image to the OutputStream
bitmapImage.compress(Bitmap.CompressFormat.JPEG, 100, fos);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return directory.getAbsolutePath();
}
so on next activity i get it like this:
private void loadImageFromStorage(String path)
{
try {
File f=new File(path, "captured.jpg");
Log.d("filehe",f.toString());
b = BitmapFactory.decodeStream(new FileInputStream(f));
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
}
i get the b(bitmap) and covert him to byte[] after to base64(what my server asks for)
byte[] capture = encodeImage(b);
String encodedImage = Base64.encodeToString(capture,1);
Log.d("encodedImage",encodedImage);
params.put("base64", encodedImage);
the problem here is that i need to preserve the quality of the image and i can't get the quality without increasing the heavyness on my server, can't i compress it somehow? i do it on the file but doesn't work
Related
hello I'm trying to upload a file to my application and on some devices for uploading from sdcard it gives me an error:java.io.FileNotFoundException: (path in sdcard): open failed: EISDIR (Is a directory). anybody have any idea why?? My code:
browsing file and opening file manager:
private void doBrowseFile() {
Intent chooseFileIntent = new Intent(Intent.ACTION_GET_CONTENT);
chooseFileIntent.setType("application/pdf");
// Only return URIs that can be opened with ContentResolver
chooseFileIntent.addCategory(Intent.CATEGORY_OPENABLE);
chooseFileIntent = Intent.createChooser(chooseFileIntent, "Choose a file");
startActivityForResult(chooseFileIntent, UNIQUE_REQUEST_CODE);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == UNIQUE_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
if (data != null) {
Uri fileUri = data.getData();
Log.i(LOG_TAG, "Uri: " + fileUri);
String filePath = null;
try {
filePath = FileUtils.getPath(this, fileUri);
} catch (Exception e) {
Log.e(LOG_TAG, "Error: " + e);
Toast.makeText(this, "Error: " + e, Toast.LENGTH_SHORT).show();
}
getBase64FromPath(filePath);
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
Encoding file from file path:
#RequiresApi(api = Build.VERSION_CODES.O)
public void getBase64FromPath(String path) {
String base64 = "";
try {
File file = new File(path);
byte[] buffer = new byte[(int) file.length() + 100];
file.getParentFile().mkdirs();
FileInputStream fileInputStream = new FileInputStream(file); //THIS LINE GIVES ME ERROR
#SuppressWarnings("resource")
int length = fileInputStream.read(buffer);
base64 = Base64.encodeToString(buffer, 0, length,
Base64.DEFAULT);
uploadFile(base64);
} catch (IOException e) {
Toast.makeText(this, "error:" + e.getMessage() , Toast.LENGTH_SHORT).show();
}
}
If anybody know any idea why please tell. It gives this error on some devices only. on the others it works perfectly fine. thanks.
Get rid of FileUtils.getPath(), as it will never work reliably.
You can get an InputStream on your content by calling getContentResolver().openInputStream(fileUri). You can then use that InputStream instead of the FileInputStream to read in your content.
Note that your app will crash with an OutOfMemoryError with large PDFs. I strongly recommend that you find some way to upload your content without reading it all into memory at once. For example, you might consider not converting it to base-64.
My attempt does not work at all unfortunately. Weirdly enough, capturing photos from camera works when debugging but does not in production.
#Override
public void onActivityResult(int requestCode, int resultCode, final Intent data) {
if (!Debug.isDebuggerConnected()){
Debug.waitForDebugger();
Log.d("debug", "started"); // Insert a breakpoint at this line!!
}
if (resultCode != RESULT_OK) {
return;
}
File file = null;
Uri path = null;
Bitmap image = null;
switch (requestCode) {
case RequestCodes.REQUEST_IMAGE_CAPTURE:
Bundle extras = data.getExtras();
image = (Bitmap) extras.get("data");
file = ImageUtils.saveToFile(image, "profile_picture", this);
mProfileImageView.setImageBitmap(image);
mCurrentAbsolutePath = file.getAbsolutePath();
break;
case RequestCodes.REQUEST_IMAGE_SELECT:
path = data.getData();
mProfileImageView.setImageURI(path);
mCurrentAbsolutePath = path.getPath();
file = new File(mCurrentAbsolutePath);
image = BitmapFactory.decodeFile(mCurrentAbsolutePath, new BitmapFactory.Options());
break;
default:
break;
}
try {
if(RequestCodes.REQUEST_IMAGE_SELECT == requestCode){
file = File.createTempFile(
"user_picture", /* prefix */
".jpeg", /* suffix */
getExternalFilesDir(Environment.DIRECTORY_PICTURES) /* directory */
);
File pathFile = new File(ImageUtils.getPath(path, this));
GeneralUtils.copy(pathFile, file);
}
} catch (IOException e) {
e.printStackTrace();
}
Bitmap thumbnail = ThumbnailUtils.extractThumbnail(image, 100, 100);
String thumbnailPath = null;
// Edited source: https://stackoverflow.com/a/673014/6519101
FileOutputStream out = null;
try {
// PNG is a lossless format, the compression factor (100) is ignored
thumbnailPath = File.createTempFile(
"user_picture_thumbnail", /* prefix */
".png", /* suffix */
getExternalFilesDir(Environment.DIRECTORY_PICTURES) /* directory */
).getAbsolutePath();
out = new FileOutputStream(thumbnailPath);
thumbnail.compress(Bitmap.CompressFormat.PNG, 100, out); // bmp is your Bitmap instance
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
String finalPath = file.getPath();
UserClient client = new UserClient();
String finalThumbnailPath = thumbnailPath;
client.changeUserPicture(file, FileUtils.getMimeType(this, Uri.fromFile(file)), new ApiListener<Response<ResponseBody>>(this){
#Override
public void onSuccess(Response<ResponseBody> response, int statusCode) {
SharedPreferencesManager preferences = SharedPreferencesManager.getInstance();
preferences.put(SharedPreferencesManager.Key.ACCOUNT_IMAGE_PATH, finalPath);
preferences.put(SharedPreferencesManager.Key.ACCOUNT_IMAGE_THUMBNAIL_PATH, finalThumbnailPath);
super.onSuccess(response, statusCode);
}
});
}
Unfortunately when debugging the from example of a path "/0/4/content://media/external/images/media/54257/ORIGINAL/NONE/1043890606" decoded file end up being null and breaks everything.
What is the best way of both getting from gallery and capturing image from photo?
What you should be using are content providers and resolvers here which can be thought of as databases and accessing databases for easier understanding.
That path you have there is called a URI, which is essentially like a link to a database entry. Both the camera and gallery uses content providers and resolvers actively. When a photo is taken, it is saved but the camera app also lets content provider know a new entry has been added. Now every app who has the content resolvers, such as the gallery app, can find that photo because the URI exist.
So you should be following the guides to implement content resolver if you want to access all photos in gallery.
As an aside, if you use code to copy an image file but does up update the content providers, your other app cannot see that new copied file unless it knows the absolute path. But when you restart your phone, some system does a full recheck for all image files and your content provider could be updated with the newly copied file. So try restarting your phone when testing.
Currently my application, takes a photo, and put's the data into a ImageView.
public void openCamera() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap photo = (Bitmap) data.getExtras().get("data");
imageView.setImageBitmap(photo);
}
}
Now, i want it to do this: When the user clicks the button 'Save Button' I would like it to save to storage. How would i do this? I have my button set up with an OnClick listener.
public void save_btn() {
}
Step #1: Hold onto the Bitmap somewhere
Step #2: When the button is clicked, fork a background thread, AsyncTask, IntentService, etc. to do the work
Step #3: In the background thread (or whatever), call compress() on the Bitmap, providing an OutputStream on whatever file you want to write to
use Bitmap compress method to store bitmap into the android data storage. and if you want to store full size of image then please use intent.putExtra with file uri or even you can create your own content Provider (Which is my fav).
bmp.compress(Bitmap.CompressFormat.PNG, 85, fOut);
http://developer.android.com/training/camera/photobasics.html#TaskPath
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
Try this:
String filename = "somename.jpg"; // your filename with path to sdcard
File file = new File(filename);
OutputStream outStream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream); // your bitmap
outStream.flush();
outStream.close();
Using the code you can save image on sd card :
FileOutputStream outStream = null;
File f=new File(Environment.getExternalStorageDirectory()+"/My Image/");
f.mkdir();
String extStorageDirectory = f.toString();
File file = new File(extStorageDirectory, "image.jpg");
pathOfImage = file.getAbsolutePath();
try {
outStream = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.PNG, 100, outStream);
Toast.makeText(getApplicationContext(), "Saved at "+f.getAbsolutePath(), Toast.LENGTH_LONG).show();
} catch (FileNotFoundException e) {e.printStackTrace();}
try {
outStream.flush();
outStream.close();
} catch (IOException e) {e.printStackTrace();}
I have an app that allow user to upload a photo on wall.
The code works well for the majority of users, but I have reported that the application crashes sometimes when uploading photo.
The problem is not in taking the pictures from the camera, but it is when you have to take the path of the picture.
The version of Android that is causing this problem is 4.4.2, but I do not understand how to fix it.
post some code:
activityResult:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == CAMERA_PIC_REQUEST) {
try {
//picUri is a global variable Uri
picUri = data.getData();
cropImage();
} catch (Exception e) {
e.printStackTrace();
}
}
else if(requestCode == PIC_CROP) {
try{
//thumbnail is a global variable Bitmap
thumbnail = MediaStore.Images.Media.getBitmap(context.getContentResolver(), cropImageUri);
setImage();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}
hot to crop image:
public void cropImage() {
try {
Intent cropIntent = new Intent("com.android.camera.action.CROP");
//indicate image type and Uri
cropIntent.setDataAndType(picUri, "image/*");
//set crop properties
cropIntent.putExtra("crop", "true");
//indicate aspect of desired crop
cropIntent.putExtra("aspectX", 1);
cropIntent.putExtra("aspectY", 1);
cropIntent.putExtra("scale", true);
//indicate output X and Y
cropIntent.putExtra("outputX", 700);
cropIntent.putExtra("outputY", 700);
//retrieve data on return
cropIntent.putExtra("return-data", false);
File f = createNewFile("CROP_");
try{
f.createNewFile();
}
catch (IOException e) {
e.printStackTrace();
}
//cropImageUri is a global variable Uri
cropImageUri = Uri.fromFile(f);
cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, cropImageUri);
//start the activity - we handle returning in onActivityResult
startActivityForResult(cropIntent, PIC_CROP);
}
catch(ActivityNotFoundException anfe){
anfe.printStackTrace();
}
}
create new File:
private File createNewFile(String prefix) {
if (prefix== null) {
prefix="IMG_";
}
else if("".equalsIgnoreCase(prefix)) {
prefix="IMG_";
}
File newDirectory = new File(Environment.getExternalStorageDirectory()+"/mypics/");
if (!newDirectory.exists()) {
if (newDirectory.mkdir()) {
}
}
File file = new File(newDirectory,(prefix+System.currentTimeMillis()+".jpg"));
if (file.exists()) {
file.delete();
try {
file.createNewFile();
}catch (IOException e) {
e.printStackTrace();
}
}
return file;
}
then when a user click on "send" method preUploadImage is called:
public void preUploadImage() {
UploadImage uploadImage = new UploadImage();
Uri newUri = getImageUri(thumbnail);
try{
// System.out.println("uri = "+picUri);
uploadImage.upload(getRealPathFromURI(newUri));
}
catch (IOException e) {
e.printStackTrace();
}
}
public Uri getImageUri(Bitmap inImage) {
String path = MediaStore.Images.Media.insertImage(context.getContentResolver(), inImage, "Title", null);
return Uri.parse(path);
}
and in the last row the error appears.
return Uri.parse(path);
this row cause a NullPointerException
java.lang.NullPointerException: uriString
at android.net.Uri$StringUri.<init>(Uri.java:468)
at android.net.Uri$StringUri.<init>(Uri.java:458)
at android.net.Uri.parse(Uri.java:430)
at com.delsorboilario.verdebio.ScriviDomanda.getImageUri(ScriviDomanda.java:584)
at com.delsorboilario.verdebio.ScriviDomanda.preUploadImage(ScriviDomanda.java:608)
at com.delsorboilario.verdebio.ScriviDomanda$6$4$2.run(ScriviDomanda.java:292)
I have not attempted to use insertImage(), and in this case, it is not quite clear why you would need it.
Primarily, it seems like you are looking to upload the photo. For that, all you need is the File that you created in createNewFile(). If you are uploading it yourself (e.g., HttpUrlConnection, some third-party library), you should be able to just use the File or an InputStream on it. Even if the upload code really needs a Uri, you can try Uri.fromFile() to get a Uri from your File and see if that works.
Where MediaStore does come into play is making your file be indexed and therefore accessible to apps (ones that query the MediaStore for images) and to users (via their MTP connection through their USB cable). MediaScannerConnection and its static scanFile() method are a fairly straightforward way to get the file indexed. Just make sure your file is fully written to disk first (e.g., if you are writing the file yourself, call getFD().sync() on your FileOutputStream after flush() and before close()).
Looks like MediaStore.Images.Media.insertImage(context.getContentResolver(), inImage, "Title", null); return null.
Form the documentation of MediaStore.Images.Media
Insert an image and create a thumbnail for it.
Parameters
cr The content resolver to use
source The stream to use for the image
title The name of the image
description The description of the image
Returns The URL to the newly created image, or null if the image
failed to be stored for any reason
After following part of this tutorial and the second answer of this question in SO, I managed to save a photo that I chose from my gallery to my object in Parse.
The problem is that the photo that I saved has .PNG extension (it was just a screenshot).
When I tried to choose a normal photo from the camera folder, nothing was saved and an exception was occurred.
The extension of ALL the other photos is .jpg NOT .jpeg.
Because of that, i tried to put if statements, so that I can check the type of the photo.
The result of the code that is following is that when I choose a .JPG photo, the data type is NULL.
But, how can I manage to save the .jpg photos in my Parse Object ?
In my Activity I have 2 buttons. when you press the first one ( sign_in ), there is the listener that does correctly all the checks of the other data in my page and then if all data are okay, it calls a function ( postData() ), in which there will be done the saving to parse via objects.
The second button is about adding a photo from gallery. In my .java activity, I have this exact listener:
picture.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
startActivityForResult(new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI), GET_FROM_GALLERY);
}
});
This is the function that it is being called from the onClick function of the button:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//Detects request codes
if(requestCode==GET_FROM_GALLERY && resultCode == Activity.RESULT_OK) {
Uri selectedImage = data.getData();
selectedImageType =data.getType();
Toast.makeText(SignUpActivity.this, "Type: "+selectedImageType,
Toast.LENGTH_SHORT).show();
try {
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), selectedImage);
// Convert it to byte
ByteArrayOutputStream stream = new ByteArrayOutputStream();
// Compress image to lower quality scale 1 - 100
if (selectedImageType == "JPEG"){
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
// bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
image = stream.toByteArray();
}
else if (selectedImageType == "JPG" || selectedImageType == "jpg"){
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
// bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
image = stream.toByteArray();
}
else if (selectedImageType == "PNG") {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
// bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
image = stream.toByteArray();
}
else{
Toast.makeText(SignUpActivity.this, "Please pick a JPEG or PNG photo!",
Toast.LENGTH_SHORT).show();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
And this is the function that saves the data:
public void postData(final String username,final String password,final String email,final String gender,final String age) {
ParseObject user = new ParseObject("users");
user.put("username", username);
user.put("password", password);
user.put("email", email);
user.put("gender", gender);
user.put("age_category", age);
user.put("admin", false);
ParseFile file = null;
if (selectedImageType == "JPEG"){
file = new ParseFile("profile_picture.jpeg", image);
}
else if (selectedImageType == "JPG" || selectedImageType == "jpg"){
file = new ParseFile("profile_picture.jpg", image);
}
else if (selectedImageType == "PNG"){
file = new ParseFile("profile_picture.png", image);
}
else{
// Show a simple toast message
Toast.makeText(SignUpActivity.this, "Please pick a JPEG or PNG photo!",
Toast.LENGTH_SHORT).show();
}
// Upload the image into Parse Cloud
file.saveInBackground();
user.put("photo", file);
// Create the class and the columns
user.saveInBackground();
// Show a simple toast message
Toast.makeText(SignUpActivity.this, "Image Uploaded",
Toast.LENGTH_SHORT).show();
Intent intent = new Intent(SignUpActivity.this, LoginActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.push_down_in, R.anim.push_down_out);
//finish();
}
remove your if statements that try to keep track of the "selectedImageType" throughout the process of bitmap creation and image Post to parse.com.
Once you have a bitmap, you can simply specify all compression to "Bitmap.CompressFormat.JPEG" and then simply post all jpgs to parse.com.
So .jpeg works and .jpg doesn't? How about this then (notice you shouldn't compare strings with ==):
if (selectedImageType.toUpperCase().equals("JPEG") || selectedImageType.toUpperCase().equals("JPG")){
file = new ParseFile("profile_picture.jpeg", image);
}
Also you can consolidate some earlier code:
if (selectedImageType.toUpperCase().equals("JPEG") || selectedImageType.toUpperCase().equals("JPG")){
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
image = stream.toByteArray();
}