Glide saving picture to file - Bitmap recycled - android

I want to use Glide to save a bitmap to a file and then share the picture with a nice Share button. I've used some code I've found in the docs, just like this:
Glide.with(getApplicationContext())
.load(imageUrl)
.asBitmap()
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap b, GlideAnimation<? super Bitmap> glideAnimation) {
File file = getExternalFilesDir(null);
try {
URI uri = new URI(imageUrl);
String path = uri.getPath();
String nameOfFile = file + "/" + path.substring(path.lastIndexOf('/') + 1);
OutputStream os = new FileOutputStream(nameOfFile);
b.compress(Bitmap.CompressFormat.JPEG, 100, os);
os.flush();
os.close();
Intent share = new Intent(Intent.ACTION_SEND);
share.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + nameOfFile));
share.setType("image/*");
startActivity(Intent.createChooser(share, getResources().getString(R.string.action_share)));
} catch (IOException | URISyntaxException e) {
e.printStackTrace();
}
}
});
The thing is that randomly, the app crashes because the Bitmap b is recycled before I compress it. How can I avoid this? Is there a better way?

I have a similar issue with you.
maybe you generate a bitmap use a same imageUrl in other place, then you invoke bitmap.recycled() manually. so you called asBitmap() may cause Bitmap recycled issue.
you can replace asBitmap() with asFile();
.asFile()
.into(new SimpleTarget<File>()
or don't invoke bitmap.recycled()

Related

Correctly managing color of bitmap image without creating anomalies on android device

Working on Android fashion app and downloading various images from AWS Cloudfront, then storing them locally on internal storage.
The same image looks different if I show it from my app or from the gallery app. It's not about display, I'm sure of this because I tested with the Photoshop color picker.
I guess it may depend on compression but I have maximum value (100). Also, I tried to write directly array byte to my memory and decode the array without any compression.
Look this image to better understand:
I know that Android handles different color profile and I've tried many of those, with no effects.
Also, I use Glide library to load the images and I set no cache and ARGB8888 color profile.
Then I tried to use Picasso, but nothing changed.
That's how I download and save the images:
#Override
protected Bitmap doInBackground(String... strings) {
try {
return BitmapFactory.decodeStream((InputStream)new URL(strings[0]).getContent());
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (bitmap != null) {
try {
String filename = colorImage.getUniqueId() + "_zoom.jpg";
// I tried this
File file = new File(getContext().getFilesDir(), filename);
FileOutputStream fos = new FileOutputStream(file, false);
// Writing the bitmap to the output stream
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
// But also this
// File file = new File(getContext().getFilesDir(), filename);
// FileOutputStream fos = new FileOutputStream(file, false);
// FileUtils.writeByteArrayToFile(file, array);
fos.close();
} catch (Exception e) {
Log.e("mylog", "saveInternalStorageError(): " + e.getMessage());
}
}
}
That's how I show the image (after I read all issues referenced here https://github.com/bumptech/glide/issues/515):
Glide.with(this)
.load(imageUriF)
.asBitmap()
.encoder(new BitmapEncoder(Bitmap.CompressFormat.JPEG, 100))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(firstImage);
I also tried to show the image without Glide with same results
Bitmap myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
img.setImageBitmap(myBitmap);
And Picasso too, with same results.
Have you got any idea about this issue?

Image quality is bad after saving using Glide Library

hi i'm trying to save picture downloaded in device storage i have this methods for save pictures in storage but after saved i find that the picture quality is bad please help me i want to save the picture with the same orignal quality
Glide.with(mContext)
.load("YOUR_URL")
.asBitmap()
.into(new SimpleTarget<Bitmap>(100,100) {
#Override
public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) {
saveImage(resource);
}});
private String saveImage(Bitmap image) {
String savedImagePath = null;
String imageFileName = "JPEG_" + "FILE_NAME" + ".jpg";
File storageDir = new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
+ "/YOUR_FOLDER_NAME");
boolean success = true;
if (!storageDir.exists()) {
success = storageDir.mkdirs();
}
if (success) {
File imageFile = new File(storageDir, imageFileName);
savedImagePath = imageFile.getAbsolutePath();
try {
OutputStream fOut = new FileOutputStream(imageFile);
image.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
fOut.close();
} catch (Exception e) {
e.printStackTrace();
}
// Add the image to the system gallery
galleryAddPic(savedImagePath);
Toast.makeText(mContext, "IMAGE SAVED"), Toast.LENGTH_LONG).show();
}
return savedImagePath;
}
private void galleryAddPic(String imagePath) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(imagePath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
sendBroadcast(mediaScanIntent);
}
This line:
.into(new SimpleTarget<Bitmap>(100,100)
Literally means that you want the image with a width of 100px and height of 100px, which is really really small, and i'm 99.99% sure this is what you mean with "bad quality".
If you want the 100% original image, you should use this:
.into(new SimpleTarget<Bitmap>(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
"Target" is a class within Glide, and it has the "SIZE_ORIGINAL" constant.
That'll give you the full image, in original quality, which you can then save.
Your Bitmap.compress is already in the max quality, you can change the format to PNG, but you´ll have no compress for the image, because PNG is a lossless format.
You can change the dimens of your image too, change SimpleTarget<Bitmap>(100,100) to the original one.

take screenshot from layout and share via android

I use below code to take screen shot from my layout and share it via android intent but the captured screen shot in the selected app is not showing any thing.
#Override
public void onClick(View view) {
shareBitmap(this,takeScreenshot());
}
public Bitmap takeScreenshot() {
try{
View rootView = getWindow().getDecorView().findViewById(R.id.lyt_main_report_activity);
rootView.setDrawingCacheEnabled(true);
return rootView.getDrawingCache();
}catch (Exception ex){
ex.printStackTrace();
}
return null;
}
public static void shareBitmap(Context context, Bitmap bitmap){
//save to sd card
try {
File cachePath = new File(context.getCacheDir(), "images");
cachePath.mkdirs(); // don't forget to make the directory
FileOutputStream stream = new FileOutputStream(cachePath + "/image.png"); // overwrites this image every time
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try{
//start share activity
File imagePath = new File(context.getCacheDir(), "images");
File newFile = new File(imagePath, "image.png");
Uri contentUri = Uri.fromFile(newFile); //FileProvider.getUriForFile(context, "com.persianswitch.apmb.app.fileprovider", newFile);
if (contentUri != null) {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
//shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file
// shareIntent.setDataAndType(contentUri, context. getContentResolver().getType(contentUri));
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
shareIntent.setType("image/*");
context.startActivity(Intent.createChooser(shareIntent, context.getResources().getString(R.string.share_using)));
}
}catch (Exception ex){
ex.printStackTrace();
}
}
Please use this code this is tested code :
public static void takeScreenshot(Context context, View view) {
String path = Environment.getExternalStorageDirectory().toString() +
"/" + "test.png";
View v = view.findViewById(android.R.id.content).getRootView();
v.setDrawingCacheEnabled(true);
v.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(v.getDrawingCache());
v.setDrawingCacheEnabled(false);
OutputStream out = null;
File imageFile = new File(path);
try {
out = new FileOutputStream(imageFile);
// choose JPEG format
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
} catch (FileNotFoundException e) {
// manage exception
} catch (IOException e) {
// manage exception
} finally {
try {
if (out != null) {
out.close();
}
} catch (Exception exc) {}
}
// onPauseVideo();
Intent share = new Intent(Intent.ACTION_SEND);
share.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(imageFile));
share.setType("image/png");
((Activity) context).startActivityForResult(
Intent.createChooser(share, "Share Drawing"), 111);
}
Since DrawingCache() deprecated above 28 so using Canvas will sort the issues for many. below answer can be used for share Screenshot including text without requesting for permissions.
To take the Screenshot
private Bitmap takeScreenShot(View view) {
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
To share the Screenshot
private void shareContent(Bitmap bitmap) {
String bitmapPath = MediaStore.Images.Media.insertImage(
binding.getRoot().getContext().getContentResolver(), bitmap, "title", "");
Uri uri = Uri.parse(bitmapPath);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/*");
shareIntent.putExtra(Intent.EXTRA_SUBJECT, "App");
shareIntent.putExtra(Intent.EXTRA_TEXT, "Currently a new version of KiKi app is available.");
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
binding.getRoot().getContext().startActivity(Intent.createChooser(shareIntent, "Share"));
}
if nothing happens it means your Bitmap is null kindly check that. or you could also fall on View.buildDrawingCache(); which will draw the View to Bitmap and then call your View.getDrawingCache();
also When hardware acceleration is turned on, enabling the drawing cache has no effect on rendering because the system uses a different mechanism for acceleration which ignores the flag. If you want to use a Bitmap for the view, even when hardware acceleration is enabled, see setLayerType(int, android.graphics.Paint) for information on how to enable software and hardware layers.
the quote was taken from the documented page
hope it helps
Tipp:The checked answer works when your device has external storage. On the Samsung 6 for example, it doesnt. Therefore you need to work with fileprovider.
Just incase somebody fell into this trap like me. The problem is that you are putting the name of the image twice.
FileOutputStream stream = new FileOutputStream(cachePath + "/image.png");
and than again.
File newFile = new File(imagePath, "image.png");
Change The seconed one to
File newFile = new File(imagePath);
Otherwise the contentUri give you the bitmap of your screenshot. I hope this helps someone. Took me 3 hours to figure it out :)

How to share an image from the Internet without saving it?

I'm trying to share an image from the internet using share intents. This doesn't work because I think the image needs to be downloaded first:
intent.putExtra(Intent.EXTRA_STREAM, imageUri);
So I'm downloading an image and then sharing it:
#Background
protected void tryToShareImage(Intent intent) {
try {
URL url = new URL(mImageUrl);
Bitmap image = BitmapFactory.decodeStream(url.openConnection().getInputStream());
intent.putExtra(Intent.EXTRA_STREAM, getImageUri(mActivity, image));
} catch (Exception e) {
e.printStackTrace();
}
startActivity(Intent.createChooser(intent, "Share using..."));
}
public Uri getImageUri(Context inContext, Bitmap inImage) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
return Uri.parse(path);
}
However, there's one problem: the image stays in the gallery after I don't need it anymore.
Question: Is there a way to share an image from the Internet without saving it first?

why screenshots have low quality?

I'm taking screenshot of my Layout with this code:
View u = findViewById(R.id.mainL);
u.setDrawingCacheEnabled(true);
LinearLayout z = (LinearLayout) findViewById(R.id.mainL);
int totalHeight = z.getHeight();
int totalWidth = z.getWidth();
u.layout(0, 0, totalWidth, totalHeight);
u.buildDrawingCache(true);
Bitmap b = Bitmap.createBitmap(u.getDrawingCache());
u.setDrawingCacheEnabled(false);
obviously I'm not doing anything on quality but the result bitmap (that is a jpeg image) has a very bad quality.
what am I doing wrong?
I thing I should change it to a PNG Image somehow for better quality but I don't know how.
thanks.
Edit:
for those who ask where am I saving the bitmap I have to say that I'm not saving it at all. I just share it with this code:
String pathofBmp = MediaStore.Images.Media.insertImage(getContentResolver(), b , "title", null);
Uri bmpUri = Uri.parse(pathofBmp);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
shareIntent.putExtra(Intent.EXTRA_STREAM, bmpUri);
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(Intent.createChooser(shareIntent, "Share"));
I have to say that I'm not saving it at all
Yes, you are. Otherwise, you would not have pathofBmp. Now, in your case, you are delegating everything about saving the bitmap to the MediaStore. I have never used insertImage() and I have no idea what it does in terms of file formats, quality percentage, etc.
If you want more control over saving the image, use compress() on Bitmap to write it to a file yourself.
please try this:
View v = view.getRootView();
v.setDrawingCacheEnabled(true);
Bitmap b = v.getDrawingCache();
String extr = Environment.getExternalStorageDirectory().toString();
File myPath = new File(extr, getString(R.string.free_tiket)+".jpg");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(myPath);
b.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
MediaStore.Images.Media.insertImage(getContentResolver(), b, "Screen", "screen");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
I think problem is compress 100 is fix your problem.
reference

Categories

Resources