In my App the user is able to save images from a pciture gallery to the local device by clicking on a DL Button.
The Code does work on old devices but on API 29 it has the following behaviour:
While saving I tried to open the Gallery to have a look what happens: the gallery gets updated and for 1 second an empty images appears and disappears immediately after. I get noticed, that the image got saved but it doesn't appear, not even in the device explorer.
//DEXTER HERE
Picasso.get().load(dummyimage.getLarge()).placeholder(R.drawable.ic_img_error).error(R.drawable.ic_red).into(saveImageToDirectory);
final Target saveImageToDirectory = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
ProgressDialog mydialog = new ProgressDialog(getActivity());
mydialog.setMessage("saving Image to phone");
mydialog.show();
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
try {
String fileName = "myApp_" + timeStamp + ".JPG";
String dirName= "/myApp";
File file = new File(requireActivity().getApplicationContext().getExternalFilesDir(null).getAbsolutePath() + dirName, fileName);
//new File(path for the file to be saved, saving file name)
if (!file.exists()) {
//check if the file already exist or if not create a new file
//if exist the file will be overwritten with the new image
File filedirectory = new File(requireActivity().getApplicationContext().getExternalFilesDir(null).getAbsolutePath() + dirName);
filedirectory.mkdirs();
}
if (file.exists()) {
file.delete();
}
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, ostream);
Toast.makeText(getActivity(), "Picture saved to Gallery" + file.getAbsolutePath(), Toast.LENGTH_SHORT).show();
ostream.close();
mydialog.dismiss();
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, "My Images");
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
values.put(MediaStore.MediaColumns.RELATIVE_PATH,"/myApp");
// API LEVEL Q: values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put("_data", file.getAbsolutePath());
ContentResolver cr = getActivity().getContentResolver();
cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} catch (Exception e) {
mydialog.dismiss();
Log.e("file creation error", e.toString());
}
}
#Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
As you may see, instead of
File filedirectory = new File(Environment.getExternalStorageDirectory() + dirName);
I'm already using
File filedirectory = new File(requireActivity().getApplicationContext().getExternalFilesDir(null).getAbsolutePath() + dirName);
I hope this shouldn't be the problem, but I'm a little stuck on this strange behaviour.
This is the error I'm getting out of my Logcat:
E/file creation error: java.lang.IllegalArgumentException: Primary
directory (invalid) not allowed for
content://media/external/images/media; allowed directories are [DCIM,
Pictures]
PS: I'm using Dexter to avoid problems with the permissions
Try this
private Uri saveImage(Context context, Bitmap bitmap, #NonNull String folderName, #NonNull String fileName) throws IOException
{
OutputStream fos;
File imageFile = null;
Uri imageUri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentResolver resolver = context.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM" + File.separator + folderName);
imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
fos = resolver.openOutputStream(imageUri);
} else {
String imagesDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM).toString() + File.separator + folderName;
imageFile = new File(imagesDir);
if (!imageFile.exists()) {
imageFile.mkdir();
}
imageFile = new File(imagesDir, fileName + ".png");
fos = new FileOutputStream(imageFile);
}
boolean saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
if (imageFile != null) // pre Q
{
MediaScannerConnection.scanFile(context, new String[]{imageFile.toString()}, null, null);
imageUri = Uri.fromFile(imageFile);
}
return imageUri;
}
And add requestLegacyExternalStorage in your Manifest file
<application
android:allowBackup="true"
android:icon="#drawable/icon"
android:requestLegacyExternalStorage="true"
android:label="#string/app_name"
android:roundIcon="#drawable/icon"
android:supportsRtl="true"
android:theme="#style/AppTheme">
...
...
...
</application>
Related
I am building an android application using java.
https://github.com/skydragon1115/Android_Camera
But the image does not save to the storage on the below devices.
Galaxy Feel2 SC-02L OS:Android 10
dtab Compact d-01J OS:Android 7.0
This function is working on the others.
try {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String fileName = timeStamp + ".jpg";
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
values.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/Camera/");
values.put(MediaStore.MediaColumns.IS_PENDING, 1);
} else {
File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
File file = new File(directory, "Camera/" + fileName);
values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath());
}
Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
try (OutputStream output = getContentResolver().openOutputStream(uri)) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output);
}
} catch (Exception e) {
}
I found the reason.
try {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String fileName = timeStamp + ".jpg";
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.TITLE, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg");
values.put(MediaStore.Images.Media.DESCRIPTION, "captured image");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
final String relativeLocation = Environment.DIRECTORY_DCIM + File.separator + "Camera" + File.separator;
values.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);
values.put(MediaStore.MediaColumns.IS_PENDING, 0);
} else {
File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
File file = new File(directory, "Camera/" + fileName);
values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath());
}
Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
try (OutputStream output = getContentResolver().openOutputStream(uri)) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output);
}
} catch (Exception e) {
}
I use this code to save the image in the divice and it is working good
but I have problem with image name, I don't know what to tape here (I download the image from url with picasso).
Here is the code:
void saveMyImage (String appName, String imageUrl, String imageName) {
Bitmap bmImg = loadBitmap(imageUrl);
File filename;
try {
String path1 = android.os.Environment.getExternalStorageDirectory()
.toString();
File file = new File(path1 + "/" + appName);
if (!file.exists())
file.mkdirs();
filename = new File(file.getAbsolutePath() + "/" + imageName
+ ".jpg");
FileOutputStream out = new FileOutputStream(filename);
bmImg.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
ContentValues image = new ContentValues();
image.put(Images.Media.TITLE, appName);
image.put(Images.Media.DISPLAY_NAME, imageName);
image.put(Images.Media.DESCRIPTION, "App Image");
image.put(Images.Media.DATE_ADDED, System.currentTimeMillis());
image.put(Images.Media.MIME_TYPE, "image/jpg");
image.put(Images.Media.ORIENTATION, 0);
File parent = filename.getParentFile();
image.put(Images.ImageColumns.BUCKET_ID, parent.toString()
.toLowerCase().hashCode());
image.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, parent.getName()
.toLowerCase());
image.put(Images.Media.SIZE, filename.length());
image.put(Images.Media.DATA, filename.getAbsolutePath());
Uri result = getContentResolver().insert(
Images.Media.EXTERNAL_CONTENT_URI, image);
Toast.makeText(getApplicationContext(),
"download in " + filename, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
}
This is the call method in onCreate:
/* button to download the image */
download_image = findViewById(R.id.button_download);
checkPermission();
download_image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (checkPermission()) {
String URL = intent.getStringExtra("imageUrl");
saveMyImage ("my app",URL,"i want to get the image name from the url");
}
}
});
I want to get the image name from the url, so how can I do that?
Try this:
URI uri = new URI(URL);
URL videoUrl = uri.toURL();
File tempFile = new File(videoUrl.getFile());
String fileName = tempFile.getName();
I can not open a pdf file on andorid Q I do this :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
OutputStream fosTemp;
ContentResolver resolver = context.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, "mamy123");
contentValues.put(MediaStore.Files.FileColumns.MIME_TYPE, "application/pdf");
contentValues.put(MediaStore.Files.FileColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
Uri imageUri2 = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues);
fosTemp = resolver.openOutputStream(imageUri2);
byte[] pdfAsBytes = Base64.decode(base64, 0);
fosTemp.write(pdfAsBytes);
fosTemp.flush();
fosTemp.close();
openPDF(context, imageUri2);
}
I see a black screen and in logs I have :
java.lang.SecurityException: com.google.android.apps.docs has no access to content://media/external/downloads/49
public static void openPDF(Context context, Uri localUri) {
Intent i = new Intent( Intent.ACTION_VIEW );
i.setDataAndType( localUri, PDF_MIME_TYPE );
context.startActivity( i );
}
This is how I open pdf file on android Q I see a black file , a file is on folder Download
This is working for me. Tested on Android R (11). Create and save a PDF in the Downloads folder on Android Q (10) & R. I use this library to generate PDFs:
implementation 'com.uttampanchasara.pdfgenerator:pdfgenerator:1.3'
Create the PDF and call the writeFileToDownloads method in the OnSuccess callback. As a bonus, a download notification is created:
String root = mContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();
File file = new File(root, documentTitle + ".pdf");
if (root != null) {
new CreatePdf(mContext)
.setPdfName(documentTitle)
.openPrintDialog(false)
.setContentBaseUrl(null)
.setPageSize(PrintAttributes.MediaSize.ISO_A4)
.setContent(documentContent)
.setFilePath(root)
.setCallbackListener(new CreatePdf.PdfCallbackListener() {
#Override
public void onFailure(String s) {
Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_an_error_occurred), Toast.LENGTH_LONG).show();
}
#Override
public void onSuccess(String s) {
DownloadManager downloadManager = (DownloadManager) mContext.getSystemService(DOWNLOAD_SERVICE);
downloadManager.addCompletedDownload(file.getName(), file.getName(), true, "application/pdf", file.getAbsolutePath(), file.length(), true);
writeFileToDownloads(file);
}
})
.create();
To save the PDF in the downloads folder, call the writeFileToDownloads method. This will check the that the Android version is greater than or equal to Q:
private void writeFileToDownloads(File file) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentResolver resolver = mContext.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, file.getName());
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
Uri uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues);
try {
int size = (int) file.length();
byte[] bytes = new byte[size];
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
buf.read(bytes, 0, bytes.length);
buf.close();
OutputStream fos = resolver.openOutputStream(uri);
fos.write(bytes);
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Im building an android app that needs to fetch an image from an url and, after is done displaying it into the image view, I want to store it in the hard drive of the phone so it can be use later without creating a new petition or depending on the cache.
Im using glide 4.9.0
Some of the solutions online include using some deprecated clases such as SimpleTarget and Target that wont be applicable in this project.
This is what I have so far.
File file = new File(holder.context.getExternalFilesDir(null), fileName);
if (file.exists()) {
GlideApp.with(holder.context).load(file).into(holder.ivProductImage);
} else {
GlideApp.with(holder.context).load(urlImage).into(holder.ivProductImage);
// save the image to the hard drive
}
//Step 1
Glide.with(mContext)
.load(images.get(position).getThumbnail())
.asBitmap()
.into(new Target<Bitmap>(100,100) {
#Override
public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) {
saveImage(resource,position);
}
});
//Step 2
private String saveImage(Bitmap image, int position) {
String savedImagePath = null;
String imageFileName = "JPEG_" + images.get(position).getName() + ".jpg";
File storageDir = new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
+ "/Comicoid");
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);
}
return savedImagePath;
}
//Step 3
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);
mContext.sendBroadcast(mediaScanIntent);
}
So I have a bitmap and after I edit it I want that the User can save it and see it in the gallery. I was searching in google and a found many Ideas but all I tried didn't work. This is my code:
((ImageButton) findViewById(R.id. imageButton)).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
File myDir = new File(root + "/saved_images");
myDir.mkdirs();
Random generator = new Random();
int n = 10000;
n = generator.nextInt(n);
String fname = "Image-" + n + ".jpg";
File file = new File(myDir, fname);
if (file.exists())
file.delete();
try {
FileOutputStream out = new FileOutputStream(file);
mutableBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
}
catch (Exception e) {
e.printStackTrace();
}
// Tell the media scanner about the new file so that it is
// immediately available to the user.
MediaScannerConnection.scanFile(MainActivity7.this, new String[] { file.toString() }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
}
});
So whats wrong or do you have any other solutions how I can save my bitmap in the gallery?
Go to Settings > App and find Storage permission is allowed for your app or not. If not, allow it and try again.