Ok, I tried this code:
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);
}
and looks like it can't do the job. When I log it it prints:
content://media/external/images/media/25756
which doesn't helps me because I need it for new File(Uri). Strangely, getImageUri method returns Uri, but File doesn't seem to recognize it. Anyone has method to retrieve Uri from freshly made bitmap?
P.S. As far as I read, it doesn't work since KitKat.
edit
This code works and from this I want to retrieve Uri:
croppedBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
cropImageView = (CropImageView) findViewById(R.id.CropImageView);
cropImageView.setImageBitmap(croppedBitmap);
Button crop = ...;
crop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
cropContainer.setVisibility(View.GONE);
mImageView.setVisibility(View.VISIBLE);
Bitmap croppedImage = cropImageView.getCroppedImage();
mImageView.setImageBitmap(croppedImage);
paramsContener.setVisibility(View.VISIBLE);
}
});
and looks like it can't do the job
That would depend upon what "the job" is.
When I log it it prints: content://media/external/images/media/25756 which doesn't helps me because I need it for new File(Uri)
First, if you want to save a bitmap to a file, use a FileOutputStream rather than a ByteArrayOutputStream. Java file I/O has been around for ~20 years.
Second, if you are expecting to get file paths from a ContentProvider like MediaStore, you are going to be disappointed. You get back a Uri, like the content:// one in your question. A Uri is not a file. Use a ContentResolver and methods like openInputStream() to read in the data identified by that Uri. The concept of a "URI" representing an opaque handle to something you get via a stream has been around at least as long as has HTTP, which has been around for ~25 years.
Strangely, getImageUri method returns Uri, but File doesn't seem to recognize it.
That is because a Uri is not a file.
Okay, I got it thanks to #CommonsWare - made something like this. Hope will help it another people as well:
public String makeBitmapPath(Bitmap bmp){
// TimeStamp
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String path = Environment.getExternalStorageDirectory().toString();
OutputStream fOut = null;
File file = new File(path, "Photo"+ timeStamp +".jpg"); // the File to save to
try {
fOut = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
fOut.flush();
fOut.close(); // do not forget to close the stream
MediaStore.Images.Media.insertImage(getContentResolver(),file.getAbsolutePath(),file.getName(),file.getName());
} catch (IOException e){
// whatever
}
return file.getPath();
}
Related
I need to share image from my app to another app, I've image bitmap and would need to save it temporarily so as to share it via share intent. For Android 10 and 11, I used below code. But the issue is, it create multiple copies each time I share the image(i.e saves it permanently on device). Is there a way I can save the image temporarily, or maybe in a location which is not visible to the user?
Please ping here, if I couldn't make myself clear.
public Uri saveImageToGallery(Bitmap bitmap){
OutputStream fos;
Uri imageUri=null;
final String timestamp= new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
final String imageFileName= "/JPEG_YourContent_"+timestamp+ ".jpg";
try {
ContentResolver resolver=getContentResolver();
ContentValues contentValues=new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME,imageFileName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES+ File.separator+"YourContent");
imageUri=resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
fos=resolver.openOutputStream(Objects.requireNonNull(imageUri));
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
Objects.requireNonNull(fos);
}catch (Exception e){
// Log.d("Exception in saving", e.toString());
}
return imageUri;
}
I followed the answers of Saving an image from ImageView into internal storage but I still can't save anything.. My code is here :
public void buttonPickImage(View view) {
FileOutputStream fos;
bitmap = ((BitmapDrawable)imageView.getDrawable()).getBitmap();
Random rng = new Random();
int n = rng.nextInt(1000);
try {
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File(sdCard.getAbsolutePath() + "/BAC");
bool = dir.mkdir();
File file = new File(dir, "BAC_"+n+".jpg");
fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG,100,fos);
fos.flush();
fos.close();
Toast.makeText(getApplicationContext(),"Image sauvegardée"+bool,Toast.LENGTH_SHORT).show();
}catch (java.io.IOException e){
e.printStackTrace();
Toast.makeText(getApplicationContext(),"IOException: " + e.getMessage(),Toast.LENGTH_SHORT).show();
}
}
With this method I get the IOExeception with messae : java.io.FileNotFoundException: /storage/emulated/0/BAC/BAC_396.jpg: open failed: ENOENT (No such file or directory)
I also tried this to save it to internal storage but its not working for me :
https://www.tutorialspoint.com/how-to-write-an-image-file-in-internal-storage-in-android
With this method, program runs but boolean mkdir gives me false.
Thanks for helping me
Finally got it working using Media Store instead of getExternalStorageDirectory as
This method was deprecated in API level 29.
To improve user privacy, direct access to shared/external storage devices is deprecated. When an app targets Build.VERSION_CODES.Q, the path returned from this method is no longer directly accessible to apps. Apps can continue to access content stored on shared/external storage by migrating to alternatives such as Context#getExternalFilesDir(String), MediaStore, or Intent#ACTION_OPEN_DOCUMENT.
MediaStore is also useful as it allows you to get the image in your android gallery app.
So my solution is :
ImageView imageView = findViewById(R.id.image);
Bitmap bitmap = ((BitmapDrawable)imageView.getDrawable()).getBitmap();
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, "any_picture_name");
values.put(MediaStore.Images.Media.BUCKET_ID, "test");
values.put(MediaStore.Images.Media.DESCRIPTION, "test Image taken");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
OutputStream outstream;
try {
outstream = getContentResolver().openOutputStream(uri);
bitmap.compress(Bitmap.CompressFormat.JPEG, 70, outstream);
outstream.close();
Toast.makeText(getApplicationContext(),"Success",Toast.LENGTH_LONG).show();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(),e.getMessage(),Toast.LENGTH_LONG).show();
}
Still thank you #blackapps for explaining me some basics things about the IOexception, mkdir and toasts. It'll be useful anyway.
I am trying to save a bitmap that is created by the user to the default 'Pictures' folder on the device. I will start with the option that seems to be the more common method:
public void saveImageToExternalStorage(String filename, Bitmap image) {
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
try {
File directory = new File(path);
if (!directory.exists()) {
directory.mkdirs();
}
File file = new File(directory, filename + ".jpeg");
file.createNewFile();
OutputStream outputStream = new FileOutputStream(file);
image.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.flush();
outputStream.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
This is supposed to save to the 'Pictures' folder, but Environment.getExternalStorageDirectory().getAbsolutePath() seems to create a new file structure like this file://storage/emulated/11/Pictures/ and save the picture to that. I have also tried Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) with the same result. But this is not the default 'Pictures' folder on any of my test devices or emulators. Which led me to look for other methods, and I found this:
public void saveImageToExternalStorage(String filename, Bitmap image) {
try {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, filename);
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
Uri filepath = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
OutputStream outputStream = getContentResolver().openOutputStream(filepath);
image.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
This actually saves to the default file structure, which apparently looks like this: content://media/external/images/media/4282. But there seems to be no way to specify the filename of the image (Media.TITLE just sets the title attribute, not the actual filename), it saves as a (seemingly) random string of numbers. I looked at the API Guide for MediaStore.Images.Media and there does not seem to be any other variable that would set the filename. This also does not seem to be the correct way of saving, according to the Android Developer Guides. I would like to know if there is any way of saving to this folder, while also setting my own filename. And if this method could produce unforeseen problems on other devices.
EDIT:
For anyone interested this is my current code, based on the answer by #CommonsWare:
public void saveImageToExternalStorage(Context context, String filename, Bitmap image) throws IOException {
File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File file = new File(directory, filename + ".jpeg");
FileOutputStream outputStream = new FileOutputStream(file);
mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.flush();
outputStream.getFD().sync();
outputStream.close();
MediaScannerConnection.scanFile(context, new String[] {file.getAbsolutePath()}, null, null);
}
This is supposed to save to the 'Pictures' folder
getExternalStorageDirectory() returns the root of external storage, not some location inside of it (e.g., Pictures/, DCIM/, Movies/).
I have also tried Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) with the same result
That will give you the proper directory, or whatever the device thinks the proper directory is (usually named Pictures/ in external storage for whoever the current user is).
but Environment.getExternalStorageDirectory().getAbsolutePath() seems to create a new file structure like this file://storage/emulated/11/Pictures/
The 11 is a bit unusual, and getAbsolutePath() does not return something with a file:// scheme, but otherwise that seems about right.
But this is not the default 'Pictures' folder on any of my test devices or emulators.
I do not know how you have determined this.
I would like to know if there is any way of saving to this folder, while also setting my own filename.
Start with your first sample, switching to DIRECTORY_PICTURES. Then:
have outputStream be a FileOutputStream
call outputStream.getFD().sync() after flush() and before close()
use MediaScannerConnection and its scanFile() method to arrange for the MediaStore to index the image, so it is available to on-device galleries, desktop OS file managers, etc.
With Android 6.0 Marshmallow (API >= 23), Google introduced a new permission model.
You have to request permission at run-time:
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(permissions, WRITE_REQUEST_CODE);
and handle the result in onRequestPermissionsResult(),
File path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File file = new File(path, "db.JPG");
try {
file.createNewFile();
}
catch (IOException e) {
Toast.makeText(this.getApplicationContext(),
"createNewFile:"+e.toString(),
Toast.LENGTH_LONG).show();
}
then
if (Build.VERSION.SDK_INT < 19) {
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + filename)));
}
else {
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
Uri.parse("file://" + filename)));
}
to show in the gallery.
I was looking for a way to save a bitmap to a folder (with my app's name), in such a way the default gallery will recognize it.
So I managed to save the image, but I can't see it either from the gallery or my PC (using explorer)
this is my code:
// Save bitmap to internal memory
private void savePhoto(Bitmap bmp){
String appName = "myApp";
String file_path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)+ File.separator + appName;
File dir = new File(file_path);
if(!dir.exists())
dir.mkdirs();
// Image file
File file = new File(dir, "IMG" + "_" + System.currentTimeMillis() + ".jpg");
FileOutputStream out = null;
try
{
out = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
out = null;
} catch (Exception e)
{
e.printStackTrace();
}
}
I can see it using some File Manager installed on my galaxy, but it doesn't help a lot.
I know I should inform the gallery using a media scanner (which is another bridge I need to cross) but may someone help me understand way I can't even find the file..should I change its visibility somehow?
and one more thing: I read at another question regarding the issue, that I should add metadata to the image/folder so that the gallery would show it. Is it necessary?
Many thanks!!!
Apparently it did have something to do with the media scanner, I added this line
MediaScannerConnection.scanFile(this, new String[] { file.getAbsolutePath() }, null, null);
and now it works.
from the documentation:
MediaScannerConnection provides a way for applications to pass a newly created or downloaded media file to the media scanner service. The media scanner service will read metadata from the file and add the file to the media content provider.
try to replace this getExternalStoragePublicDirectory(....)
with this one getExternalStorageDirectory();
I've got an Image Uri, retrieved using the following:
public Uri getImageUri(Context inContext, Bitmap inImage) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
String path = Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
return Uri.parse(path);
}
This works just amazing for Intents that require an Image URI, etc (so I know for sure the URI is valid).
But now I want to save this Image URI to a file on the SDCARD. This is more difficult because the URI does not really point at a file on the SDCARD or the app.
Will I have to create a bitmap from the URI first, and then save the Bitmap on the SDCARD or is there a quicker way (preferable one that does not require the conversion to a bitmap first).
(I've had a look at this answer, but it returns file not found - https://stackoverflow.com/a/13133974/1683141)
The problem is that the Uri you've been given by Images.Media.insertImage() isn't to an image file, per se. It is to a database entry in the Gallery. So what you need to do is read the data from that Uri and write it out to a new file in the external storage using this answer https://stackoverflow.com/a/8664605/772095
This doesn't require creating a Bitmap, just duplicating the data linked to the Uri into a new file.
You can get the data using an InputStream using code like:
InputStream in = getContentResolver().openInputStream(imgUri);
Update
This is completely untested code, but you should be able to do something like this:
Uri imgUri = getImageUri(this, bitmap); // I'll assume this is a Context and bitmap is a Bitmap
final int chunkSize = 1024; // We'll read in one kB at a time
byte[] imageData = new byte[chunkSize];
try {
InputStream in = getContentResolver().openInputStream(imgUri);
OutputStream out = new FileOutputStream(file); // I'm assuming you already have the File object for where you're writing to
int bytesRead;
while ((bytesRead = in.read(imageData)) > 0) {
out.write(Arrays.copyOfRange(imageData, 0, Math.max(0, bytesRead)));
}
} catch (Exception ex) {
Log.e("Something went wrong.", ex);
} finally {
in.close();
out.close();
}