Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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.

like image 744
Aman Verma Avatar asked May 31 '21 09:05

Aman Verma


People also ask

How do I check if URI exists?

DocumentFile sourceFile = DocumentFile. fromSingleUri(context, uri); boolean bool = sourceFile. exists();

What is requestLegacyExternalStorage?

Apps that run on Android 11 but target Android 10 (API level 29) can still request the requestLegacyExternalStorage attribute. This flag allows apps to temporarily opt out of the changes associated with scoped storage, such as granting access to different directories and different types of media files.

Why should scoped storage be enabled?

The goal of scoped storage is to protect the privacy of app and user data. This includes protecting user information (such as photo metadata), preventing apps from modifying or deleting user files without explicit permission, and protecting sensitive user documents downloaded to Download or other folders.

Is scoped storage backward compatibility?

it works in android 11 and android 10 but does not work in android 9 and below versions. Shows error (permission denial) in android 9 and below versions. For those versions you should continue to do it as you used to do.


1 Answers

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)

    }
like image 156
Bhavin Patel Avatar answered Sep 30 '22 18:09

Bhavin Patel