Can't create file with deleted file name at android 10 - android

I want to keep a log text file for each day in the download folder. I want to store day based text files in my own Log folder(MyApp Log folder) in the Download folder.
When I delete the My App Log folder, I can't create this folder in the same location with the same name. Likewise, when the text file I created is deleted, I can't create a file with the same text file name. resolver.insert(downloadUri, contentValues); always returns null.
Even though I get a null result when I query whether there is a file belonging to that path, I can't create the same file.
The function where I created the file:
public static void createFile(){
String contentType = "text/log";
Date cDate = new Date(System.currentTimeMillis());
String today = new SimpleDateFormat("yyyy_MM_dd").format(cDate);
long seconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, today + ".txt");//2021_10_13.txt
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, contentType);//text/log
contentValues.put(MediaStore.MediaColumns.DATE_ADDED, seconds);//System.currentTimeMillis
contentValues.put(MediaStore.MediaColumns.DATE_MODIFIED, seconds);//System.currentTimeMillis
contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + File.separator + "MyApp Log");//Download/MyApp Log
ContentResolver resolver = getContext().getContentResolver();
outputUri = resolver.insert(getDownloadUri(), contentValues);
if (outputUri == null)
throw new IOException("Failed to create new MediaStore record.");
try (final OutputStream stream = resolver.openOutputStream(outputUri)) {
if (stream == null)
return;
} finally {
ContentValues updateValues = new ContentValues();
updateValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
resolver.update(outputUri, updateValues, null, null);
}
}
public static #NonNull
Uri getDownloadUri() {
if (Build.VERSION.SDK_INT < 29) {
return getLegacyUri(Environment.DIRECTORY_DOWNLOADS);
} else {
return MediaStore.Downloads.EXTERNAL_CONTENT_URI;
}
}
The function I am querying if the file exists:
public static Uri getExternalContentUriFromFile(Uri externalUri, String filePath) {
if (externalUri == null)
return null;
try (Cursor cursor = getContentResolver().query(externalUri, new String[]{MediaStore.MediaColumns._ID},
MediaStore.MediaColumns.DATA + "=? ", new String[]{filePath}, null)) {
if (cursor != null && cursor.moveToFirst()) {
int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
return Uri.withAppendedPath(externalUri, "" + id);
}
return null;
}
}

I found a solution myself:
int fileNo = 0;
Uri uri = saveToUri(fileName, contentType, seconds, relativePath);
if (uri == null) {
while (fileNo < 4 && uri == null) {
fileNo++;
fileName = AttachmentUtil.removeExtensionForName(fileName) + "(" + fileNo + ")" + extension;
uri = saveToUri(fileName, contentType, seconds, storageName + File.separator + myDirName, storageUri);
}
}
#Will V :
public static String removeExtensionForName(String fileName) {
int i = fileName.lastIndexOf('.');
if (i > 0) {
return fileName.substring(0, i);
}
return fileName;
}
And I got my code in above question into saveToUri function.

Related

How do I copy files from the DOCUMENTS folder to the app's cache folder in Android 10?

I am trying to copy files from DOCUMENTS folder to cache folder in Android 10
Since Android 10, MEDIASTORE has to be used, so I wrote the code referring to the data in the link below.
Create/Copy File in Android Q using MediaStore
The file exists but returns 'No file found' Log
How do I copy or read files that my app did not create?
File deviceDB = new File(getDatabasePath(Define.DBNAME).toString() + ".db");
Uri contentUri = MediaStore.Files.getContentUri("external");
String selection = MediaStore.MediaColumns.RELATIVE_PATH + "=?";
String[] selectionArgs = new String[]{Environment.DIRECTORY_DOCUMENTS + "/" + Define.BACKUP_DIR + "/"};
Cursor cursor = getContentResolver().query(contentUri, null, selection, selectionArgs, null);
Uri uri = null;
if (cursor.getCount() == 0) {
Log.i("debug","No file found");
} else {
while (cursor.moveToNext()) {
String fileName = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME));
if (fileName.equals(dbName + ".db")) {
long id = cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
uri = ContentUris.withAppendedId(contentUri, id);
break;
}
}
if (uri == null) {
Log.i("debug","uri == null");
} else {
InputStream inputStream = getContentResolver().openInputStream(uri);
int size = inputStream.available();
byte[] bytes = new byte[size];
inputStream.read(bytes);
OutputStream output = new FileOutputStream(deviceDB);
output.write(bytes);
output.flush();
inputStream.close();
}
}

How to get URI from filename which is saved in the external Storage using Scoped Storage in android 10 and above?

Before SDK 29 this is the correct way to find the Uri but now it won't work anymore for sdk greater than 28. Let's assume I save the BITMAP using scoped-storage like:
#RequiresApi(api = Build.VERSION_CODES.Q)
#NonNull
private Uri saveBitmap(#NonNull final Context context, #NonNull final Bitmap bitmap,
#NonNull final Bitmap.CompressFormat format, #NonNull final String mimeType,
#NonNull final String displayName, #Nullable final String subFolder) throws IOException {
String relativeLocation = Environment.DIRECTORY_PICTURES;
if (!TextUtils.isEmpty(subFolder)) {
relativeLocation += File.separator + subFolder;
}
final ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);
final ContentResolver resolver = context.getContentResolver();
OutputStream stream = null;
Uri uri = null;
try {
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
uri = resolver.insert(contentUri, contentValues);
if (uri == null) {
throw new IOException("Failed to create new MediaStore record.");
}
stream = resolver.openOutputStream(uri);
if (stream == null) {
throw new IOException("Failed to get output stream.");
}
if (!bitmap.compress(format, 95, stream)) {
throw new IOException("Failed to save bitmap.");
}
return uri;
} catch (IOException e) {
if (uri != null) {
// Don't leave an orphan entry in the MediaStore
resolver.delete(uri, null, null);
}
throw e;
} finally {
if (stream != null) {
stream.close();
}
}
}
This code works for SDK <= 28. But, How to get URI using fileName which is saved in the external storage for SDK version 29 and above?
private String getFilePathUri(String enteredFileName) {
String file_uri_string = Environment.getExternalStorageDirectory() + "/"
+ AppConstants.APP_FOLDER + "/" + enteredFileName + ".jpg";
AppUtils.showLog(TAG, file_uri_string + "");
return file_uri_string;
}
Android 10 and above: Here the uri gives the file_id based you the given displayName.
/**
* Returns the Uri which can be used to delete/work with images in the photo gallery.
* #param displayName Path to IMAGE on SD card
* #return Uri in the format of... content://media/external/images/media/[NUMBER]
*/
private Uri getUriFromPath(String displayName) {
long photoId;
Uri photoUri = MediaStore.Images.Media.getContentUri("external");
String[] projection = {MediaStore.Images.ImageColumns._ID};
// TODO This will break if we have no matching item in the MediaStore.
Cursor cursor = getContentResolver().query(photoUri, projection, MediaStore.Images.ImageColumns.DISPLAY_NAME + " LIKE ?", new String[] { displayName }, null);
assert cursor != null;
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(projection[0]);
photoId = cursor.getLong(columnIndex);
cursor.close();
return Uri.parse(photoUri.toString() + "/" + photoId);
}

get count of certain extension files from Android device

I am trying to count certain extension files (pdf, txt, docx, pptx, xlsx) which are stored in Android device using getcontentresolver query and reading file by file.
However, I'm getting different results from both ways. count results are the following:
By Content Resolver Query:
pdf 289
txt 133
docs 29
pptx 21
xlsx 9
By reading file by file
pdf 289
txt 134
docs 151
pptx 77
xlsx 27
Here is Code
By Content Resolver Query:
public static long getFilesCount() {
long myCounter = 0;
String selectionMimeType = MediaStore.Files.FileColumns.MIME_TYPE + "=?" + " OR " + MediaStore.Files.FileColumns.MIME_TYPE + "=?" + " OR " + MediaStore.Files.FileColumns.MIME_TYPE + "=?" + " OR " + MediaStore.Files.FileColumns.MIME_TYPE + "=?" + " OR " + MediaStore.Files.FileColumns.MIME_TYPE + "=?";
String pdfMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("pdf");
String txtMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("txt");
String docMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("docx");
String pptxMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("pptx");
String xlsxMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("xlsx");
String[] selectionArgs = new String[]{pdfMimeType, txtMimeType, docMimeType, pptxMimeType, xlsxMimeType};
Cursor mCursor = App
.getInstance()
.getApplicationContext()
.getContentResolver()
.query(MediaStore.Files.getContentUri("external")
, null, selectionMimeType, selectionArgs, null);
if (mCursor != null && mCursor.moveToFirst()) {
int aCount = 0;
do {
int id = mCursor.getInt(mCursor.getColumnIndex(MediaStore.Files.FileColumns._ID));
String path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Files.FileColumns.DATA));
String type = mCursor.getString(mCursor.getColumnIndex(MediaStore.Files.FileColumns.MIME_TYPE));
if (path == null || type == null)
continue;
File file = new File(path);
if (file.exists()) {
if (!file.isDirectory()) {
myCounter++;
}
}
} while (mCursor.moveToNext());
}
if (mCursor != null && !mCursor.isClosed()) {
mCursor.close();
}
return myCounter;
}
`
By reading file by file
`
public static long filesCount() {
long counter = _loadFiles(predefineExtension(), new File(getFilesPath())));
return counter;
}
public static String getFilesPath() {
return Environment.getExternalStorageDirectory().getPath();
}
public static ArrayList<String> predefineExtension() {
ArrayList<String> list = new ArrayList<>();
list.add("pdf");
list.add("txt");
list.add("docx");
list.add("pptx");
list.add("xlsx");
return list;
}
private static long _loadFiles(ArrayList<String> selectedExtension, File directory) {
long counter = 0;
File[] files = directory.listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
if (files[i] != null) {
if (files[i].isDirectory()) {
counter += _loadFiles(selectedExtension, files[i]);
} else {
File file = files[i];
if (file.exists()) {
for (int j = 0; j < selectedExtension.size(); j++) {
String ext = selectedExtension.get(j);
if (file.getPath().endsWith("." + ext)) {
counter++;
break;
} // file match
} // selected file extension loop
} // file exist
} // file is not directory
} // file is not null
} // loop
} // files array is not null
return counter;
}
So, my question is why these both approaches have different results? Any help is appreciated! Thank you!
my question is why these both approaches have different results?
They are unlikely to be identical.
Not every file on external storage necessarily gets indexed by the MediaStore. This is particularly true of files that were recently put on external storage and files in dot-prefixed directories (e.g., .stuff/).
MediaStore has access to content on removable storage that you may not have access to.
Note that your filesystem-based approach will not work on Android Q (by default) or Android R+ (for all apps). I am not certain about your MediaStore approach.

How to delete image file from Android Internal when path is stored in SQLite DB

I Know this is already answered question.But I'm unable to figure it out when using SQLite DB. My app captures some documents and will be stores in phone memory. I'm using SQLite DB in my app which stores the path of the above image. How can i delete the image from phone memory if i delete the image in SQLite DB.
String photoPath = cursor.getString(i_COL_PICTURE);
--My path is
`"content://com.google.android.apps.photos.contentprovider/-1/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F153/ORIGINAL/NONE/1743496576"
`
When you want delete some file in your storage, Just do this.
File file = new File(yourFilePathHere);
deleted = file.delete();
I am considering you have required permissions because you are able to write files in storage.
Edit
You are using MediaStore for getting images. So now when you want delete file you should delete file from MediaStore also. I have a method which will help you.
public static int deleteFileFromMediaStore(final ContentResolver contentResolver, final File file) {
String canonicalPath;
try {
canonicalPath = file.getCanonicalPath();
} catch (IOException e) {
canonicalPath = file.getAbsolutePath();
}
final Uri uri = MediaStore.Files.getContentUri("external");
final int result = contentResolver.delete(uri,
MediaStore.Files.FileColumns.DATA + "=?", new String[]{canonicalPath});
if (result == 0) {
final String absolutePath = file.getAbsolutePath();
if (!absolutePath.equals(canonicalPath)) {
int deletedRow = contentResolver.delete(uri,
MediaStore.Files.FileColumns.DATA + "=?", new String[]{absolutePath});
return deletedRow;
}
} else return result;
return result;
}
Call it in your Activity like
deleteFileFromMediaStore(getContentResolver(), fileToDelete)
Note Check if you are getting absolute path by MediaStore. Here is my method to get all gallery images if you have problem with your code.
public static ArrayList<ModelBucket> getImageBuckets(Context context) {
ArrayList<ModelBucket> list = new ArrayList<>();
String absolutePathOfImage;
String absoluteFolder;
boolean same_folder = false;
int pos = 0;
Uri uri;
Cursor cursor;
int column_index_data, column_index_folder_name;
uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.MediaColumns.DATA, MediaStore.Images.Media.BUCKET_DISPLAY_NAME};
final String orderBy = MediaStore.Images.Media.DATE_TAKEN;
cursor = context.getContentResolver().query(uri, projection, null, null, orderBy + " DESC");
if (cursor == null) return null;
column_index_data = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
column_index_folder_name = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
while (cursor.moveToNext()) {
absolutePathOfImage = cursor.getString(column_index_data);
absoluteFolder = cursor.getString(column_index_folder_name);
Log.d("Column", absolutePathOfImage);
Log.d("Folder", absoluteFolder);
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getFolderName().equals(absoluteFolder)) {
same_folder = true;
pos = i;
break;
} else {
same_folder = false;
}
}
if (same_folder) {
ArrayList<String> al_path = new ArrayList<>(list.get(pos).getAllFilesPath());
al_path.add(absolutePathOfImage);
list.get(pos).setAllFilesPath(al_path);
} else {
ArrayList<String> al_path = new ArrayList<>();
al_path.add(absolutePathOfImage);
ModelBucket modelBucket = new ModelBucket();
modelBucket.setFolderName(absoluteFolder);
modelBucket.setAllFilesPath(al_path);
list.add(modelBucket);
}
}
return list;
}
here ModelBucket.class is a model class.
public class ModelBucket {
String folderName;
ArrayList<String> allFilesPath;
ArrayList<ModelFile> files;
// make getter setter
}
before deleting the image get the path of the image and pass the path to below code
File fdelete = new File(path);
if (fdelete.exists()) {
if (fdelete.delete()) {
System.out.println("file Deleted :" + path);
} else {
System.out.println("file not Deleted :" + path);
}
}
after this remove the path from sqlite db
If you have your Uri pointing to the file you can do :
String pathToFile = myUri.getEncodedPath(); // this gives your the real path to the file, like /emulated/0/sdcard/myImageFile.jpg
File file = new File(pathToFile);
if(file.exists()){
file.delete();
}

Getting filename from uri

How can I get the name of a file from a uri returned in OnActivityResult,
I tried using this bit of code
Uri uri = data.getData();
String fileName = uri.getLastPathSegment();
but it just returns something like this images:3565. The file that is picked is not only of image type it can be a video, or document file, etc.... I realized that the uri returned from kitkat is different than previous versions as well, I would be interested in a method that works for pre kitkat as well.
This is the code I'm using to get informations from a Uri :
public static class FileMetaData
{
public String displayName;
public long size;
public String mimeType;
public String path;
#Override
public String toString()
{
return "name : " + displayName + " ; size : " + size + " ; path : " + path + " ; mime : " + mimeType;
}
}
public static FileMetaData getFileMetaData(Context context, Uri uri)
{
FileMetaData fileMetaData = new FileMetaData();
if ("file".equalsIgnoreCase(uri.getScheme()))
{
File file = new File(uri.getPath());
fileMetaData.displayName = file.getName();
fileMetaData.size = file.length();
fileMetaData.path = file.getPath();
return fileMetaData;
}
else
{
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(uri, null, null, null, null);
fileMetaData.mimeType = contentResolver.getType(uri);
try
{
if (cursor != null && cursor.moveToFirst())
{
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
fileMetaData.displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
if (!cursor.isNull(sizeIndex))
fileMetaData.size = cursor.getLong(sizeIndex);
else
fileMetaData.size = -1;
try
{
fileMetaData.path = cursor.getString(cursor.getColumnIndexOrThrow("_data"));
}
catch (Exception e)
{
// DO NOTHING, _data does not exist
}
return fileMetaData;
}
}
catch (Exception e)
{
Log.e(Log.TAG_CODE, e);
}
finally
{
if (cursor != null)
cursor.close();
}
return null;
}
}
Maybe this is too trivial, but in my case it worked:
DocumentFile.fromSingleUri(context, uri).getName();
(simplified, without null pointer checks). Similar with other metadata.
I think the most straightforward and easy way to retrieve information from an URI is using DocumentFile. Just create a new DocumentFile using context and your URI.
DocumentFile file = DocumentFile.fromSingleUri(context, uri);
Then you can retrieve various information from it.
String fileName = file.getName();
long fileSize = file.length();
String mimeType = file.getType(); //get the mime type
Note that file.getName() will return file name with extension (e.g. video.mp4)
For kotlin just use the name atttribute from the File class:
val fileName = File(uri.path).name
According to Android Documentation
/*
* Get the file's content URI from the incoming Intent,
* then query the server app to get the file's display name
* and size.
*/
returnIntent.data?.let { returnUri ->
contentResolver.query(returnUri, null, null, null, null)
}?.use { cursor ->
/*
* Get the column indexes of the data in the Cursor,
* move to the first row in the Cursor, get the data,
* and display it.
*/
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
cursor.moveToFirst()
findViewById<TextView>(R.id.filename_text).text = cursor.getString(nameIndex)
findViewById<TextView>(R.id.filesize_text).text = cursor.getLong(sizeIndex).toString()
...
}
https://developer.android.com/training/secure-file-sharing/retrieve-info
fun Uri.getFileNameWithExtension(context: Context): String? {
val name = this.path?.let { path -> File(path).name }.orEmpty()
val extension = MimeTypeMap.getSingleton()
.getExtensionFromMimeType(getMimeType(context)).orEmpty()
return if (name.isNotEmpty() && extension.isNotEmpty()) "$name.$extension" else null
}
fun Uri.getMimeType(context: Context): String? {
return when (scheme) {
ContentResolver.SCHEME_CONTENT -> context.contentResolver.getType(this)
ContentResolver.SCHEME_FILE -> MimeTypeMap.getSingleton().getMimeTypeFromExtension(
MimeTypeMap.getFileExtensionFromUrl(toString()).toLowerCase(Locale.US)
)
else -> null
}
}
This worked for me. Have a look at the official documentation here
String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME};
ContentResolver cr = mctx.getContentResolver();
Cursor metaCursor = cr.query(uri[0], projection, null, null, null);
if (metaCursor != null) {
try {
if (metaCursor.moveToFirst()) {
realFileName = metaCursor.getString(0);
}
} finally {
metaCursor.close();
}
}
If I use ContentResolver, it returns null if uri is from camera captured image in my case so simple function to get file name from uri
public static String getFileNameFromURI(#NonNull Context context, #NonNull Uri uri) {
String result = null;
if("file".equalsIgnoreCase(uri.getScheme())){
result= new File(uri.getPath()).getName();
}
else {
Cursor c = null;
try {
c = context.getContentResolver().query(uri, null, null, null, null);
c.moveToFirst();
result = c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME));
} catch (Exception e) {
// error occurs
} finally {
if (c != null) {
c.close();
}
}
}
return result;
}

Categories

Resources