Save .txt file on Android Q - android

I was trying to save text file using below code
try {
FileOutputStream fos = new FileOutputStream(TXT_FILE_NAME, true);
FileWriter fWriter;
try {
fWriter = new FileWriter(fos.getFD());
fWriter.write(binding.tvExtractedResult.getText().toString());
fWriter.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
fos.getFD().sync();
fos.close();
Toast.makeText(this, "File Saved Successfully", Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Toast.makeText(this, "Error while saving file", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
But the problem is this code doesn't work with Android Q.
After this I tried to search the solution and I did this
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues myContentValues = new ContentValues();
myContentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, TXT_FILE_NAME);
String myFolder = "Download/MY_PROJECT";
myContentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, myFolder);
myContentValues.put(MediaStore.MediaColumns.MIME_TYPE, "text/plain");
myContentValues.put(MediaStore.MediaColumns.IS_PENDING, 1);
Uri extVolumeUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
ContentResolver contentResolver = getContentResolver();
Uri uri = contentResolver.insert(extVolumeUri, myContentValues);
if (uri == null) {
Log.e(TAG, "uri is null");
return;
}
Log.e(TAG, "uri=" + uri);
try {
FileOutputStream fos = new FileOutputStream(new File(uri.toString()));
fos.write(binding.tvExtractedResult.getText().toString().getBytes());
fos.close();
} catch (Exception e) {
Log.e(TAG, "error occurred" + e.getMessage());
e.printStackTrace();
} finally {
myContentValues.clear();
myContentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
contentResolver.update(uri, myContentValues, null, null);
}
}
In above code I'm getting uri == null
Appreciate your help.
Thanks.

Please have a look at this code, it will allow you to save text file below and higher versions of Android Q.
public static void saveFile(Context context, String fileName, String text, String extension) throws IOException{
OutputStream outputStream;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName + extension); // file name
values.put(MediaStore.MediaColumns.MIME_TYPE, "text/plain");
values.put(MediaStore.MediaColumns.RELATIVE_PATH, DIRECTORY);
Uri extVolumeUri = MediaStore.Files.getContentUri("external");
Uri fileUri = context.getContentResolver().insert(extVolumeUri, values);
outputStream = context.getContentResolver().openOutputStream(fileUri);
}
else {
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).toString() + FOLDER_NAME;
File file = new File(path, fileName + extension);
Log.d(TAG, "saveFile: file path - " + file.getAbsolutePath());
outputStream = new FileOutputStream(file);
}
byte[] bytes = text.getBytes();
outputStream.write(bytes);
outputStream.close();
}

The answer provided by #SheikhHasib is correct.
Here, however, is the Kotlin version of his code with small adjustments. This will save "backup.txt" file in the "Download" directory, and the file will contain "Example content". I've tested in on Android 11 and it works perfectly.
#Throws(IOException::class)
private fun saveFile(context: Context, fileName: String, text: String, extension: String) {
val outputStream: OutputStream? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val values = ContentValues()
values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
values.put(MediaStore.MediaColumns.MIME_TYPE, "text/plain")
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
val extVolumeUri: Uri = MediaStore.Files.getContentUri("external")
val fileUri: Uri? = context.contentResolver.insert(extVolumeUri, values)
context.contentResolver.openOutputStream(fileUri!!)
} else {
val path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString()
val file = File(path, "$fileName.$extension")
FileOutputStream(file)
}
val bytes = text.toByteArray()
outputStream?.write(bytes)
outputStream?.close()
}
And to run it:
CoroutineScope(Dispatchers.IO).launch {
runCatching{
val content: String = "Example content"
saveFile(requireContext(), "backup", content, "txt")
}
}

Related

Trying to copy image one folder to another folder using contentResolver in android 11 but it create blank image

i m working on an application where i have list of images inside recyclerview and from recyclerview i have to copy my images from one folder to another folder my code is working fine in many android 11 and 12 device but in few device it creates blank images the size of blank image is in byte i don't understand what is wrong with my code
here is the code i used for copy image
public static String copyFile(#NonNull final String filePath, String newName, File targetFolder, Context context) {
ContentResolver contentResolver = context.getContentResolver();
FileOutputStream fos = null;
String folderName = targetFolder.getName();
String relativePath;
String fileName = new File(filePath).getName();
String mimeType = getFileType(fileName);
ArraySet<Uri> uries = new ArraySet<>();
if (isImage(mimeType)) {
uries.add(getImageUriFromFile(filePath, context));
} else {
uries.add(getVideoUriFromFile(filePath, context));
}
if (targetFolder.getPath().contains(Environment.DIRECTORY_PICTURES)) {
relativePath = Environment.DIRECTORY_PICTURES + File.separator + folderName;
} else if (targetFolder.getPath().contains(Environment.DIRECTORY_DCIM)) {
relativePath = Environment.DIRECTORY_DCIM + File.separator + folderName;
} else {
relativePath = Environment.DIRECTORY_PICTURES + File.separator + folderName;
}
try {
if (isImage(new File(filePath).getName().split("\\.")[1])) {
Bitmap bitmap = getBitmap(filePath);
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, newName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath);
Uri imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
fos = (FileOutputStream) contentResolver.openOutputStream(imageUri);
if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)) {
fos.flush();
}
return getPath(imageUri, context);
} else {
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, newName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/" + mimeType);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath);
Uri imageUri = contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues);
fos = (FileOutputStream) contentResolver.openOutputStream(imageUri);
FileInputStream inStream = new FileInputStream(filePath);
FileChannel inChannel = inStream.getChannel();
FileChannel outChannel = fos.getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
inStream.close();
fos.close();
return getPath(imageUri, context);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
and i called this method inside thread like below.
i called this method from thread because i m doing another operation on copied file
new Thread(() -> StorageUtils.copyFileOnAboveQ(
file1.getPath(),
file2.getName().split("\\.")[0],
file2.getParentFile(), context)).start();
code is working fine in many devices but in some device it create blank image

In Android Nougat and Later versions, can't we store image/video in Public directories? If we can, how to we write code compatible with old versions? [duplicate]

I need to save an image to camera folder, but as Android Q getExternalStoragePublicDirectory is deprecated, I do it in another way.
What I have (this method receive bitmap and its name):
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentResolver resolver = mContext.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/" + IMAGES_FOLDER_NAME);
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
OutputStream fos = resolver.openOutputStream(imageUri);
saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
} else {
String imagesDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM).toString() + File.separator + IMAGES_FOLDER_NAME;
File file = new File(imagesDir);
if (!file.exists()) {
file.mkdir();
}
File image = new File(
imagesDir,
name + ".png"
);
final long fileHashCode = image.hashCode();
Logger.d(TAG, "saveImage, saving image file, hashCode = " + fileHashCode);
FileOutputStream fos = new FileOutputStream(image);
saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
}
That perfectly works for all needed OS versions, but it looks inaccurate and I'd like to find a more common way.
I tried to play around with content values or try some similar way as for Q, but it doesn't work. I've seen many questions here, but neither of them can help me.
The question is how can I optimize saving for OS lower than Q?
The most generalized version I was able to write is:
private Uri saveImage(Context context, Bitmap bitmap, #NonNull String folderName, #NonNull String fileName) throws IOException {
OutputStream fos = null;
File imageFile = null;
Uri imageUri = null;
try {
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, Environment.DIRECTORY_PICTURES + File.separator + folderName);
imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
if (imageUri == null)
throw new IOException("Failed to create new MediaStore record.");
fos = resolver.openOutputStream(imageUri);
} else {
File imagesDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).toString() + File.separator + folderName);
if (!imagesDir.exists())
imagesDir.mkdir();
imageFile = new File(imagesDir, fileName + ".png");
fos = new FileOutputStream(imageFile);
}
if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos))
throw new IOException("Failed to save bitmap.");
fos.flush();
} finally {
if (fos != null)
fos.close();
}
if (imageFile != null) {//pre Q
MediaScannerConnection.scanFile(context, new String[]{imageFile.toString()}, null, null);
imageUri = Uri.fromFile(imageFile);
}
return imageUri;
}
If you've found a better way, post here, I'll mark it as answer.
Using this document : https://developer.android.com/training/data-storage
Create temp file first
val mTempFileRandom = Random()
fun createTempFile(ext:String, context:Context):String {
val path = File(context.getExternalCacheDir(), "AppFolderName")
if (!path.exists() && !path.mkdirs())
{
path = context.getExternalCacheDir()
}
val result:File
do
{
val value = Math.abs(mTempFileRandom.nextInt())
result = File(path, "AppFolderName-" + value + "-" + ext)
}
while (result.exists())
return result.getAbsolutePath()
}
Send file from path
copyFileToDownloads(this#CameraNewActivity, File(savedUri.path))
Copy data to storage
MAIN_DIR: Main folder name in which you want to store image (Like App Name)
IMAGE_DIR: Sub folder if you want to create.
fun copyFileToDownloads(context: Context, downloadedFile: File): Uri? {
// Create an image file name
val timeStamp = SimpleDateFormat(DATE_FORMAT_SAVE_IMAGE).format(Date())
val imageFileName = "JPEG_$timeStamp.jpg"
val resolver = context.contentResolver
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val values = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, imageFileName)
put(MediaStore.Images.Media.MIME_TYPE, IMAGE_MIME_TYPE)
put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM + File.separator + MAIN_DIR + File.separator + IMAGE_DIR + File.separator)
}
resolver.run {
val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
uri
}
} else {
val authority = "${context.packageName}.provider"
val imagePath =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)?.absolutePath
val destinyFile = File(imagePath, imageFileName)
val uri = FileProvider.getUriForFile(context, authority, destinyFile)
FileUtils.scanFile(context, destinyFile.absolutePath)
uri
}?.also { uri ->
var writtenValue = 0L
// Opening an outputstream with the Uri that we got
resolver.openOutputStream(uri)?.use { outputStream ->
downloadedFile.inputStream().use { inputStream ->
writtenValue = inputStream.copyTo(outputStream)
Log.d("Copy Written flag", " = $writtenValue")
}
}
}
}
ScanFile : ( More detail : https://developer.android.com/reference/android/media/MediaScannerConnection )
fun scanFile(context:Context, path:String) {
MediaScannerConnection.scanFile(context,
arrayOf<String>(path), null,
{ newPath, uri-> if (BuildConfig.DEBUG)
Log.e("TAG", "Finished scanning " + newPath) })
}
Can't we use media store for above and below Android Q?
I have tried following way and it is working fine.
private fun writeImage() {
val uri =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
val imageDetail = ContentValues().apply {
put(MediaStore.Images.ImageColumns.DISPLAY_NAME, "${System.currentTimeMillis()}.jpeg")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Images.Media.IS_PENDING, 1)
}
}
val contentUri = contentResolver.insert(uri, imageDetail)
contentUri?.let {
contentResolver.openFileDescriptor(contentUri, "w", null).use { pd ->
pd?.let {
/* val fos = FileOutputStream(it.fileDescriptor)
val array = getBitmapToBase64()
fos.write(array, 0, array.size)
fos.close()
*/
// or
// Your logic to write an Image file.
}
}
imageDetail.clear()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
imageDetail.put(MediaStore.Images.Media.IS_PENDING, 0)
contentUri.let { contentResolver.update(it, imageDetail, null, null) }
}
// open the saved image file with gallery app
Snackbar.make(
findViewById(android.R.id.content), "saved", Snackbar.LENGTH_LONG
).setAction("Show") {
val intent = Intent(Intent.ACTION_VIEW, contentUri)
startActivity(intent)
}.show()
} ?: Toast.makeText(this, "not saved", Toast.LENGTH_LONG).show()
}

How do I save a picture to the default images folder on Android? [duplicate]

unfortunately the solutions I've found didn't work on android 5.1.1.
I have a bitmap called source. I need to save it directly to my phone's gallery. My manifest contains <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Can you give me a working method to do this?
There were several different ways to do it before API 29 (Android Q) but all of them involved one or a few APIs that are deprecated with Q. In 2019, here's a way to do it that is both backward and forward compatible:
(And since it is 2019 so I will write in Kotlin)
/// #param folderName can be your app's name
private fun saveImage(bitmap: Bitmap, context: Context, folderName: String) {
if (android.os.Build.VERSION.SDK_INT >= 29) {
val values = contentValues()
values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/" + folderName)
values.put(MediaStore.Images.Media.IS_PENDING, true)
// RELATIVE_PATH and IS_PENDING are introduced in API 29.
val uri: Uri? = context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
if (uri != null) {
saveImageToStream(bitmap, context.contentResolver.openOutputStream(uri))
values.put(MediaStore.Images.Media.IS_PENDING, false)
context.contentResolver.update(uri, values, null, null)
}
} else {
val directory = File(Environment.getExternalStorageDirectory().toString() + separator + folderName)
// getExternalStorageDirectory is deprecated in API 29
if (!directory.exists()) {
directory.mkdirs()
}
val fileName = System.currentTimeMillis().toString() + ".png"
val file = File(directory, fileName)
saveImageToStream(bitmap, FileOutputStream(file))
if (file.absolutePath != null) {
val values = contentValues()
values.put(MediaStore.Images.Media.DATA, file.absolutePath)
// .DATA is deprecated in API 29
context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
}
}
}
private fun contentValues() : ContentValues {
val values = ContentValues()
values.put(MediaStore.Images.Media.MIME_TYPE, "image/png")
values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
return values
}
private fun saveImageToStream(bitmap: Bitmap, outputStream: OutputStream?) {
if (outputStream != null) {
try {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
outputStream.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
Also, before calling this, you need to have WRITE_EXTERNAL_STORAGE first.
Use this one:
private void saveImage(Bitmap finalBitmap, String image_name) {
String root = Environment.getExternalStorageDirectory().toString();
File myDir = new File(root);
myDir.mkdirs();
String fname = "Image-" + image_name+ ".jpg";
File file = new File(myDir, fname);
if (file.exists()) file.delete();
Log.i("LOAD", root + fname);
try {
FileOutputStream out = new FileOutputStream(file);
finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Use this code this we help you to store images into a particular folder which is saved_images and that folder images show in gallery immediately.
private void SaveImage(Bitmap finalBitmap) {
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);
finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
// sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
// Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
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(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);
}
});
}
From Android Q there are changes in saving image to gallery.Thanks to #BaoLei, here is my answer in java if anybody needs it.
private void saveImage(Bitmap bitmap) {
if (android.os.Build.VERSION.SDK_INT >= 29) {
ContentValues values = contentValues();
values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/" + getString(R.string.app_name));
values.put(MediaStore.Images.Media.IS_PENDING, true);
Uri uri = this.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
if (uri != null) {
try {
saveImageToStream(bitmap, this.getContentResolver().openOutputStream(uri));
values.put(MediaStore.Images.Media.IS_PENDING, false);
this.getContentResolver().update(uri, values, null, null);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
} else {
File directory = new File(Environment.getExternalStorageDirectory().toString() + '/' + getString(R.string.app_name));
if (!directory.exists()) {
directory.mkdirs();
}
String fileName = System.currentTimeMillis() + ".png";
File file = new File(directory, fileName);
try {
saveImageToStream(bitmap, new FileOutputStream(file));
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
this.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
private ContentValues contentValues() {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
}
return values;
}
private void saveImageToStream(Bitmap bitmap, OutputStream outputStream) {
if (outputStream != null) {
try {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Here is a fully working solution in Kotlin:
fun saveToGallery(context: Context, bitmap: Bitmap, albumName: String) {
val filename = "${System.currentTimeMillis()}.png"
val write: (OutputStream) -> Boolean = {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
put(MediaStore.MediaColumns.RELATIVE_PATH, "${Environment.DIRECTORY_DCIM}/$albumName")
}
context.contentResolver.let {
it.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)?.let { uri ->
it.openOutputStream(uri)?.let(write)
}
}
} else {
val imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString() + File.separator + albumName
val file = File(imagesDir)
if (!file.exists()) {
file.mkdir()
}
val image = File(imagesDir, filename)
write(FileOutputStream(image))
}
}
Do it in One Line
MediaStore.Images.Media.insertImage(applicationContext.getContentResolver(), IMAGE ,"nameofimage" , "description");
Now we have android10 and android11, so here is an updated version, that will work in all android devices.
Make sure you have the WRITE_EXTERNAL_STORAGE permission before calling this function.
private fun saveMediaToStorage(bitmap: Bitmap) {
val filename = "${System.currentTimeMillis()}.jpg"
var fos: OutputStream? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentResolver?.also { resolver ->
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
}
val imageUri: Uri? =
resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
fos = imageUri?.let { resolver.openOutputStream(it) }
}
} else {
val imagesDir =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
val image = File(imagesDir, filename)
fos = FileOutputStream(image)
}
fos?.use {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
toast("Saved to Photos")
}
}
Its kotlin, and I think very straight forward and self explaining code. But still if you have a problem, comment below and I will explain.
Reference: Android Save Bitmap to Gallery Tutorial.
I'd like to add Java code based on #Bao Lei 's answer that I used in my app.
private void saveImage(Bitmap bitmap, Context context, String folderName) throws FileNotFoundException {
if (android.os.Build.VERSION.SDK_INT >= 29) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/" + folderName);
values.put(MediaStore.Images.Media.IS_PENDING, true);
// RELATIVE_PATH and IS_PENDING are introduced in API 29.
Uri uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
if (uri != null) {
saveImageToStream(bitmap, context.getContentResolver().openOutputStream(uri));
values.put(MediaStore.Images.Media.IS_PENDING, false);
context.getContentResolver().update(uri, values, null, null);
}
} else {
dir = new File(getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES),"");
// getExternalStorageDirectory is deprecated in API 29
if (!dir.exists()) {
dir.mkdirs();
}
java.util.Date date = new java.util.Date();
imageFile = new File(dir.getAbsolutePath()
+ File.separator
+ new Timestamp(date.getTime()).toString()
+ "Image.jpg");
imageFile = new File(dir.getAbsolutePath()
+ File.separator
+ new Timestamp(date.getTime()).toString()
+ "Image.jpg");
saveImageToStream(bitmap, new FileOutputStream(imageFile));
if (imageFile.getAbsolutePath() != null) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, imageFile.getAbsolutePath());
// .DATA is deprecated in API 29
context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
}
}
private ContentValues contentValues() {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
return values;
}
private void saveImageToStream(Bitmap bitmap, OutputStream outputStream) {
if (outputStream != null) {
try {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
This one worked fine in my app.
For Media Scanning, you can simply do
val intent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
intent.data = Uri.fromFile(path) // path must be of File type
context.sendBroadcast(intent)

How to save image to camera folder in Android Q?

I need to save an image to camera folder, but as Android Q getExternalStoragePublicDirectory is deprecated, I do it in another way.
What I have (this method receive bitmap and its name):
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentResolver resolver = mContext.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/" + IMAGES_FOLDER_NAME);
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
OutputStream fos = resolver.openOutputStream(imageUri);
saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
} else {
String imagesDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM).toString() + File.separator + IMAGES_FOLDER_NAME;
File file = new File(imagesDir);
if (!file.exists()) {
file.mkdir();
}
File image = new File(
imagesDir,
name + ".png"
);
final long fileHashCode = image.hashCode();
Logger.d(TAG, "saveImage, saving image file, hashCode = " + fileHashCode);
FileOutputStream fos = new FileOutputStream(image);
saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
}
That perfectly works for all needed OS versions, but it looks inaccurate and I'd like to find a more common way.
I tried to play around with content values or try some similar way as for Q, but it doesn't work. I've seen many questions here, but neither of them can help me.
The question is how can I optimize saving for OS lower than Q?
The most generalized version I was able to write is:
private Uri saveImage(Context context, Bitmap bitmap, #NonNull String folderName, #NonNull String fileName) throws IOException {
OutputStream fos = null;
File imageFile = null;
Uri imageUri = null;
try {
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, Environment.DIRECTORY_PICTURES + File.separator + folderName);
imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
if (imageUri == null)
throw new IOException("Failed to create new MediaStore record.");
fos = resolver.openOutputStream(imageUri);
} else {
File imagesDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).toString() + File.separator + folderName);
if (!imagesDir.exists())
imagesDir.mkdir();
imageFile = new File(imagesDir, fileName + ".png");
fos = new FileOutputStream(imageFile);
}
if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos))
throw new IOException("Failed to save bitmap.");
fos.flush();
} finally {
if (fos != null)
fos.close();
}
if (imageFile != null) {//pre Q
MediaScannerConnection.scanFile(context, new String[]{imageFile.toString()}, null, null);
imageUri = Uri.fromFile(imageFile);
}
return imageUri;
}
If you've found a better way, post here, I'll mark it as answer.
Using this document : https://developer.android.com/training/data-storage
Create temp file first
val mTempFileRandom = Random()
fun createTempFile(ext:String, context:Context):String {
val path = File(context.getExternalCacheDir(), "AppFolderName")
if (!path.exists() && !path.mkdirs())
{
path = context.getExternalCacheDir()
}
val result:File
do
{
val value = Math.abs(mTempFileRandom.nextInt())
result = File(path, "AppFolderName-" + value + "-" + ext)
}
while (result.exists())
return result.getAbsolutePath()
}
Send file from path
copyFileToDownloads(this#CameraNewActivity, File(savedUri.path))
Copy data to storage
MAIN_DIR: Main folder name in which you want to store image (Like App Name)
IMAGE_DIR: Sub folder if you want to create.
fun copyFileToDownloads(context: Context, downloadedFile: File): Uri? {
// Create an image file name
val timeStamp = SimpleDateFormat(DATE_FORMAT_SAVE_IMAGE).format(Date())
val imageFileName = "JPEG_$timeStamp.jpg"
val resolver = context.contentResolver
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val values = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, imageFileName)
put(MediaStore.Images.Media.MIME_TYPE, IMAGE_MIME_TYPE)
put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM + File.separator + MAIN_DIR + File.separator + IMAGE_DIR + File.separator)
}
resolver.run {
val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
uri
}
} else {
val authority = "${context.packageName}.provider"
val imagePath =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)?.absolutePath
val destinyFile = File(imagePath, imageFileName)
val uri = FileProvider.getUriForFile(context, authority, destinyFile)
FileUtils.scanFile(context, destinyFile.absolutePath)
uri
}?.also { uri ->
var writtenValue = 0L
// Opening an outputstream with the Uri that we got
resolver.openOutputStream(uri)?.use { outputStream ->
downloadedFile.inputStream().use { inputStream ->
writtenValue = inputStream.copyTo(outputStream)
Log.d("Copy Written flag", " = $writtenValue")
}
}
}
}
ScanFile : ( More detail : https://developer.android.com/reference/android/media/MediaScannerConnection )
fun scanFile(context:Context, path:String) {
MediaScannerConnection.scanFile(context,
arrayOf<String>(path), null,
{ newPath, uri-> if (BuildConfig.DEBUG)
Log.e("TAG", "Finished scanning " + newPath) })
}
Can't we use media store for above and below Android Q?
I have tried following way and it is working fine.
private fun writeImage() {
val uri =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
val imageDetail = ContentValues().apply {
put(MediaStore.Images.ImageColumns.DISPLAY_NAME, "${System.currentTimeMillis()}.jpeg")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Images.Media.IS_PENDING, 1)
}
}
val contentUri = contentResolver.insert(uri, imageDetail)
contentUri?.let {
contentResolver.openFileDescriptor(contentUri, "w", null).use { pd ->
pd?.let {
/* val fos = FileOutputStream(it.fileDescriptor)
val array = getBitmapToBase64()
fos.write(array, 0, array.size)
fos.close()
*/
// or
// Your logic to write an Image file.
}
}
imageDetail.clear()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
imageDetail.put(MediaStore.Images.Media.IS_PENDING, 0)
contentUri.let { contentResolver.update(it, imageDetail, null, null) }
}
// open the saved image file with gallery app
Snackbar.make(
findViewById(android.R.id.content), "saved", Snackbar.LENGTH_LONG
).setAction("Show") {
val intent = Intent(Intent.ACTION_VIEW, contentUri)
startActivity(intent)
}.show()
} ?: Toast.makeText(this, "not saved", Toast.LENGTH_LONG).show()
}

How to save image in android gallery

I try to save the image into WathsappIMG but when I go to image gallery android I don't see the image and the image there into the directory can be seen from ES File Explorer
OutputStream output;
// Find the SD Card path
File filepath = Environment.getExternalStorageDirectory();
// Create a new folder in SD Card
File dir = new File(filepath.getAbsolutePath()
+ "/WhatSappIMG/");
dir.mkdirs();
// Retrieve the image from the res folder
BitmapDrawable drawable = (BitmapDrawable) principal.getDrawable();
Bitmap bitmap1 = drawable.getBitmap();
// Create a name for the saved image
File file = new File(dir, "Wallpaper.jpg" );
try {
output = new FileOutputStream(file);
// Compress into png format image from 0% - 100%
bitmap1.compress(Bitmap.CompressFormat.JPEG, 100, output);
output.flush();
output.close();
}
catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
the gallery don't displaying (necessarily) files from external storage.
this is a common mistake.
the gallery displays images stored on the media store provider
you can use this method to store image file on media store provider:
public static void addImageToGallery(final String filePath, final Context context) {
ContentValues values = new ContentValues();
values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.MediaColumns.DATA, filePath);
context.getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values);
}
here is what you should enter, when you're about to save the picture in the Gallery
MediaStore.Images.Media.insertImage(getContentResolver(), yourBitmap, yourTitle , yourDescription);
That code will add the image at the end of the Gallery. so please, check your Gallery picture, to be sure
Try adding this:
MediaStore.Images.Media.insertImage(getContentResolver(), yourBitmap, yourTitle , yourDescription);
Fill in your details for yourBitmap, yourTitle, and yourDescription, or just leave it as "".
You need to add a MediaScannerConnection class to your function of saving the image to the gallery. This class scans for new files and folders in gallery connected with your app. Add the following class to scan the newly saved image files or new added image directory to the gallery or download Source Code
MediaScannerConnection.scanFile(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);
}
});
Read more
For Xamarin fellows:
public static void SaveToTheGalley(this string filePath, Context context)
{
var values = new ContentValues();
values.Put(MediaStore.Images.Media.InterfaceConsts.DateTaken, Java.Lang.JavaSystem.CurrentTimeMillis());
values.Put(MediaStore.Images.Media.InterfaceConsts.MimeType, "image/jpeg");
values.Put(MediaStore.MediaColumns.Data, filePath);
context.ContentResolver.Insert(MediaStore.Images.Media.ExternalContentUri, values);
}
And don't forget about android.permission.WRITE_EXTERNAL_STORAGE permission.
As
MediaStore.MediaColumns.Data
and
MediaStore.Images.Media.insertImage
is deprecated now,
here is how I did it using bitmap
fun addImageToGallery(
fileName: String,
context: Context,
bitmap: Bitmap
) {
val values = ContentValues()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
}
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, fileName)
values.put(MediaStore.Images.ImageColumns.TITLE, fileName)
val uri: Uri? = context.contentResolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
values
)
uri?.let {
context.contentResolver.openOutputStream(uri)?.let { stream ->
val oStream =
BufferedOutputStream(stream)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, oStream)
oStream.close()
}
}
}
You should change this piece of code-
try {
output = new FileOutputStream(file);
// Compress into png format image from 0% - 100%
bitmap1.compress(Bitmap.CompressFormat.JPEG, 100, output);
output.flush();
output.close();
String url = Images.Media.insertImage(getContentResolver(), bitmap1,
"Wallpaper.jpg", null);
}
catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Kindly refer this code worked for me:
public static boolean saveImageToGallery(Context context, Bitmap bmp) {
// First save the picture
String storePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "dearxy";
File appDir = new File(storePath);
if (!appDir.exists()) {
appDir.mkdir();
}
String fileName = System.currentTimeMillis() + ".jpg";
File file = new File(appDir, fileName);
try {
FileOutputStream fos = new FileOutputStream(file);
//Compress and save pictures by io stream
boolean isSuccess = bmp.compress(Bitmap.CompressFormat.JPEG, 60, fos);
fos.flush();
fos.close();
//Insert files into the system Gallery
//MediaStore.Images.Media.insertImage(context.getContentResolver(), file.getAbsolutePath(), fileName, null);
//Update the database by sending broadcast notifications after saving pictures
Uri uri = Uri.fromFile(file);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
if (isSuccess) {
return true;
} else {
return false;
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
Use the following code to make your image visible in the gallery.
public void saveImageToGallery(Context context, Uri path) {
// Create image from the Uri for storing it in the preferred location
Bitmap bmp = null;
ContentResolver contentResolver = getContentResolver();
try {
if(Build.VERSION.SDK_INT < 28) {
bmp = MediaStore.Images.Media.getBitmap(contentResolver, path);
} else {
ImageDecoder.Source source = ImageDecoder.createSource(contentResolver, path);
bmp = ImageDecoder.decodeBitmap(source);
}
} catch (Exception e) {
e.printStackTrace();
}
// Store image to internal storage/ImagePicker directory
String storePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "ImagePicker";
File appDir = new File(storePath);
if (!appDir.exists()) {
appDir.mkdir();
}
String fileName = System.currentTimeMillis() + ".jpg";
File file = new File(appDir, fileName);
try {
FileOutputStream fos = new FileOutputStream(file);
boolean isSuccess = bmp.compress(Bitmap.CompressFormat.JPEG, 60, fos);
fos.flush();
fos.close();
// Broadcast the image & make it visible in the gallery
Uri uri = Uri.fromFile(file);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
if (isSuccess) {
Toast.makeText(context, "File saved to gallery", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "Failed to save", Toast.LENGTH_SHORT).show();
}
} catch (IOException e) {
e.printStackTrace();
}
}

Categories

Resources