How to save and check if the file exists in scoped storage?
Asked Answered
C

1

6

Till now, I check whether a file exists or not and if it does not then I save it to the device in Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) but it is not supported from Android 10.

So I tried to use mediastore API for saving the image.

READ_STORAGE_PERMISSION -> NOT ALLOWED

First I query the contentresolver to check the existence of the file with the following code:

Uri collection = null;
collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
String[] PROJECTION = new String[]{MediaStore.Images.Media.DISPLAY_NAME,
        MediaStore.MediaColumns.RELATIVE_PATH};
String QUERY = MediaStore.Files.FileColumns.RELATIVE_PATH + " like ? and " + 
MediaStore.Files.FileColumns.DISPLAY_NAME + " like ?";

ContentResolver mContentResolver = this.getContentResolver();
Cursor cursor = mContentResolver.query(collection, PROJECTION, QUERY , new String[]{"%" + dirName + "%", "%" + fname + "%"}, null);

if (cursor != null) {
    Log.d("upisdk", "cursor != null");
    if (cursor.getCount() > 0) {

    } else {
   
    }
}

If the cursor is empty, then it means that the file does not exists and I will save the file using code

ContentResolver contentResolver = getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fname);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, dirName);
Uri imageUri = 
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);

fos = contentResolver.openOutputStream(Objects.requireNonNull(imageUri));
finalBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
Objects.requireNonNull(fos).close();

Now If everything is working fine until I delete the App data.

After deleting the data, the folder is still there and the previously saved file also. Now If the query the contentresolver for that file, it is giving cursor empty. If the READ_STORAGE_PERMISSION is not allowed then it is happening. If I allow the READ_STORAGE_PERMISSION then it is giving me the cursor non-empty. On the other hand, If i try to save a new different image and query the resolver then I am getting non-empty cursor even without the READ_STORAGE_PERMISSION.

Please tell me what I am doing wrong.

Communitarian answered 31/5, 2021 at 9:46 Comment(26)
I save it to the device in Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) but it is not supported from Android 10. Wrong. It's not supported for an Android 10 device. But it works for an Android 11 device.Dekker
check if the file exists in scoped storage?'Sorry. But has nothing to do with scoped storage. Instead you try to check if a file exists in the media store.Dekker
How to check if it exists in mediastore. I can see it in the files app. but when query content resolver it is giving cursor emptyCommunitarian
Since the file is created by my application, i should get access the file without read_external_storageCommunitarian
Actually, I have been trying and testing that's why. I have updated my questionCommunitarian
but when query content resolver it is giving cursor empty You are noty querying the content resolver. Instead you use the content resolver to query the media store. And all has nothing to do with scoped storage and i still see it as subject.Dekker
After deleting the data, the folder ... You did not tell how you deleted App data. And we wonder if data==file?Dekker
By going to the settings and Clear Data.Communitarian
That will only clear app specific caches and storages and data as far as i know. Not files you put in public directories using the media store or direct file access. Well... never used that option.. So you are telling that entries in media store are cleared to. Hmmm. interesting.Dekker
Yes. I know and I don't want it ti be deleted. The thing is after clearing the data if i query in contentresolver then I am getting empty cursor which means that such files does not exists.Communitarian
No. It means that the entry in the media store is removed. Just an entry from a database. The file still exists as you told us.Dekker
Yes. But I fI give the READ storage application then I am getting the cursor.Communitarian
Interesting... I did not know that. Now... what is the problem?Dekker
And If I remove the persmission then again I am getting empty cursor. Why does the entry depends upon the storage permission. If it was some other folder then it is okay but it is created by the application so it should get access.Communitarian
I just want to know if the image, suppose 1234.jpg is saved and user again want to save that same image then i show the user that it is already saved.Communitarian
@AmanVerma So did you solved how to check file already exist in api 30 ?Louisiana
@bdevloper Unfortunately, no. I switched back to 29 because I still can update apps with API 29. However, I need to change it before novemeber. So I will begin searching for it again. Mediastore API is flawed, I guess. I will raise a bug issue.Communitarian
I found how to check but now i want to move my older folder from getExternalStorageDirectory to Android\media folder so do you know how to move files from older folder to new folder ? because getExternalStorageDirectory is now deprecated in api 30 so how to get path from that and move?Louisiana
How are you checking if the file with a certain filename exists or not?Communitarian
Are you querying the mediastore using contentresolver and if cursor is null then it does not exists? Right? Something like that? If it is, then in some phones it is not giving the value correctly.Communitarian
@bdevloper did you check this link? developer.android.com/training/data-storage/…Communitarian
@Dekker I saved an image using mediastore with the name abc.jpg and then I query for the filename if it exists then I will not save it again and show the user the image. Now If I uninstall and reinstall the app then the same image is not recognized by mediastore and the new image is saved like abc.jpg (1) which is unable to preview.Communitarian
Yes that is normal as after reinstall the picture still exists but does not belong to the reinstalled app as the reinstall is considered a different app. Now adays apps can only see their own files and some media files.Dekker
Okay. I get it. But once I give read storage permission then I am able to get the file name and if it exists or not. I just want to be sure that can I declare read storage permission for SDK > 29 so that google won't reject my application?Communitarian
Which permissions exactly?Dekker
READ_EXTERNAL_STORAGECommunitarian
L
4

Solution-1


private fun getUriFromPath(displayName: String): Uri? {
        val photoId: Long
        val photoUri = MediaStore.Images.Media.getContentUri("external")
        val projection = arrayOf(MediaStore.Images.ImageColumns._ID)
        val cursor = contentResolver.query(
            photoUri,
            projection,
            MediaStore.Images.ImageColumns.DISPLAY_NAME + " LIKE ?",
            arrayOf(displayName),
            null
        )!!
        cursor.moveToFirst()
        val columnIndex = cursor.getColumnIndex(projection[0])
        photoId = cursor.getLong(columnIndex)
        cursor.close()
        return Uri.parse("$photoUri/$photoId")
    }

if you want you can add relative path too to find at specific location

check using file name

private fun isExist(){
        val pathOfImage=getUriFromPath("myimage.png")
        val isExist = try {
            val i= pathOfImage?.let { contentResolver.openInputStream(it) }
            i != null
        }
        catch (e: IOException) {
            e.printStackTrace()
            false
        }
        if (!isExist) {
            // do whatever u want
        }
    }

this solution give exception when file not found and try to openstream.



Solution-2

In this solution I did save my files into Android\media\com.yourpackge\ directory

private fun isExist(){
        val path = this.externalMediaDirs.first()
        val folder = File(path, getString(R.string.app_name))
        if (!folder.exists()) folder.mkdirs()
        val  file = File(folder, "myimage.png")

        if (!file.exists()) {
            // save your files or do whatever you want

            // use this for show files into gallery
            MediaScannerConnection.scanFile(this, arrayOf(file.absolutePath), null,
                object : MediaScannerConnection.OnScanCompletedListener {
                    override fun onScanCompleted(path: String, uri: Uri?) {
                        Log.e("scann--","Finished scanning $path")
                    }
                })
        }
    }

Now I want to move my files from older getExternalStorageDirectory folder to Android\media\com.yourpackge\ directory in android 11 like whatsapp did without asking any permission.

EDIT - 1

I'm able to move my folder to new on using kotlin Utils class

sourcefolder.copyRecursively(newfolder,true)

private fun copyFiles(){
        val path = this.externalMediaDirs.first()
        val folder = File(path, getString(R.string.app_name))
        if (!folder.exists()) folder.mkdirs()
        val sourcefolder = File(Environment.getExternalStorageDirectory().toString() + "/"
                +"Supreme")
        sourcefolder.copyRecursively(folder,true)

    }
Louisiana answered 6/7, 2021 at 5:39 Comment(1)
would you please explain why using LIKE rather than = to match the display name?Pronucleus

© 2022 - 2024 — McMap. All rights reserved.