Thursday, 24 December 2020

Building a File Explorer on Android 11

I'm looking forward to build a File Explorer app but I'm shocked with what I found on I do need full read/write permission and access to everything.

So as they state here:

Declare the MANAGE_EXTERNAL_STORAGE permission in the manifest.

Use the ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION intent action to direct users to a system settings page where they can enable the following option for your app

To determine whether your app has been granted the MANAGE_EXTERNAL_STORAGE permission, call Environment.isExternalStorageManager().

Now I can access to the root folder "/storage/emulated/0/" via Environment.getExternalStoragePublicDirectory(path) and then file.listFiles()

But when I want to access to Downloads folder I can't, I've looked at SAF but I'd have to use like ACTIONS and I just want to explore through folders, then I looked at MediaStore but there's not a clear example of how I could use it here since I'm trying to get the files but cursor.moveToNext() or cursor.moveToFirst() returns false

Here's the code I've used

val projectionDownloads = arrayOf(
    MediaStore.Downloads._ID,
    MediaStore.Downloads.DISPLAY_NAME,
  )
val selectionDownloads = ""
val selectionArgsDownloads = emptyArray<String>()
val sortOrderDownloads = "${MediaStore.Downloads.DISPLAY_NAME} ASC"

context.applicationContext.contentResolver.query(
    MediaStore.Downloads.EXTERNAL_CONTENT_URI,
    projectionDownloads,
    selectionDownloads,
    selectionArgsDownloads,
    sortOrderDownloads
  )?.use { cursor ->
        Log.i("SeeMedia", "we got cursor! $cursor")
        val idColumn = cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID)
        val nameColumn = cursor.getColumnIndex(MediaStore.Files.FileColumns.DISPLAY_NAME)

        while (cursor.moveToNext()) { //Here return false
            Log.i("SeeMedia", "Move to next!")
            val id = cursor.getLong(idColumn)
            val name = cursor.getString(nameColumn)
            Log.i("SeeMedia", "ID = $id AND NAME= $name")
        }
    }

My app contains the 2 permissions:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

There are 3 questions that I'm wondering after this:

Question 1: Do I need other permissions for the purpose of reading files?

Question 2: What am I missing on this MediaStore query? Why it doesn't provide me the files of download?

Question 3: What if I want to query on other unknown files/directories, for example a folder inside Downloads which there is another folder and so on? "Downloads/Folder1/Folder2" I guess I should use MediaStore.Files, right? So how is the URI for accessing those files?

EDIT 1: SDK 29 = Android 10

SDK 30 = Android 11

What I want is to query are just files, no image, no audio just files whatever it comes, so for example the path "/" which is root will return a list of Directories: -Ringtones -Music -DCIM -Download -Documents

That's good it works with the Environment.getExternalStorageDirectory and the permission MANAGE_EXTERNAL_STORAGE.

But then I want to access to one of those directories and it's empty, for example "DCIM" (which I know it contains some pictures) and <= 28 it retrieves me the pictures with:

  • Environment.getExternalStorageDirectory()/DCIM

But in >= 29 it returns an empty, how could I get access to those files with the classic methods or MediaStore?

If I'm wrong in something just tell me because right now my head is filled with a lot of mixed stuff since I've watched a lot of videos and documentation and questions and I'm just trying to understand some critical points.

Here's a simple code that I finally could query the root "/" with MediaStore in case you want to see it, but don't know how to query other paths and retrieve me their files:

val projection = arrayOf(
    MediaStore.Files.FileColumns.DISPLAY_NAME
  )
context.applicationContext.contentResolver.query(
    MediaStore.Files.getContentUri("external"),
    projection,
    null,
    null,
    null
  )?.use { cursor ->
        Log.i("SeeMedia", "we got cursor! $cursor")
        val nameColumn = 
        cursor.getColumnIndex(MediaStore.Files.FileColumns.DISPLAY_NAME)
        while(cursor.moveToNext()){
            Log.i("SeeMedia", "Move to next!")
            val name = cursor.getString(nameColumn)
            Log.i("SeeMedia", "Name = $name")
        }
    }


from Building a File Explorer on Android 11

No comments:

Post a Comment