Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get dir file list in Android 11

Tags:

android

I want to write own lite file browser.

File API does not work for external storage now.

The release also offers improvements to scoped storage, which makes it easier for developers to migrate to using this storage model.

I don't understand how use scoped storage for access to /sdcard.

like image 535
Алексей Мальченко Avatar asked Sep 09 '20 15:09

Алексей Мальченко


2 Answers

If you're looking for a file picker experience, Storage Access Framework is your only option now. Below code let's you pick up multiple files. If you want directory level selection, you can use ACTION_OPEN_DOCUMENT_TREE intent.

private fun openStorageAccess() {
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
        type = "*/*"  
        addCategory(Intent.CATEGORY_OPENABLE)
        putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
    }
    startActivityForResult(intent, openDirectoryAccessCode)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == openDirectoryAccessCode && resultCode == Activity.RESULT_OK) {
        val fileUris = data?.clipData ?: data?.data
    }
}

Additionally, you can request a special all files access to MediaStore.Files from Google Play Console. More on Android 11 storage changes.

Edit:

If you want to implement a file browser like experience in Android 29+, here is a proposed idea that I personally haven't tried out but should work. It won't be an optimal experience but would closely resemble a file browser.

  1. If API =< 29, use regular File API (with requestLegacyExternalStorage="true" if API == 29).
  2. If API > 29, ask for all files access from Google Play Console.
    • Put MANAGE_EXTERNAL_STORAGE flag in Manifest
    • Prompt user to allow all files access by opening the settings with ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION intent.
    • Query all files from MediaStore.Files along with RELATIVE_PATH
    • Relative path + display name will give you something like DCIM/Vacation/IMG_1234.jpg.
    • Create a tree (more like a forest) out of all the relative path and show it in UI.
like image 90
Siddharth Kamaria Avatar answered Oct 20 '22 04:10

Siddharth Kamaria


I recently wrote an Android file/folder chooser library in Java that targets API 29+ (e.g., the Android OS 10/11 changes). It is called the Android File Picker Light. The idea is to have a barebones file/directory selector that displays minimalistic information about the local file system without pulling in too much multimedia processing to do things like visually display media files and image thumbnails. Some code that I used to get at the initial poster's question of how to enumerate files on the local disk (or SD card) via traditional Unix-V style paths is seen here (for example, and elsewhere there). It still works well on Android OS 10 (API 29) active as of December, 2020.

The links provided above should get you started. The source to my library project is available freely under the GPL. There is also some Google-sanctioned official source code to document the implementation of a custom file storage provider on their GitHub repos. One thing to keep in mind is that the Android API is effectively trying to generalize the interface to a "file content" so that we have a common mechanism for online indexing (like a DropBox link) and the local file system. This is well intentioned enough until one sees how much more complicated it becomes to perform simple, routine filesystem operations from the local disk or integrated SD card. Eventually us avid developers may have to be led en mass to petition the Android official documentation writers for better pointers to handle these formerly common cases of file system access :).

With this point in mind, I want to iterate on a couple of distinguishing issues to the new scoped storage changes that others may run into writing their own custom code. I hope this saves developers some future stumbling around with what seem to be common issues (to me) not adequately addressed in some of the under documented parts of the Android developer docs about scoped storage. It is possible that as Android 11 starts to occupy more live devices in the wild these problems I had will get integrated into the office docs. For now, I suggest the following few reference points:

  • It is now tricky to get filesystem data like POSIX style permissions out of the newly restricted Android API changes using more standard (portable even) Java I/O, or NIO, calls. Based on what I was able to get out of my Android 10 OS development device, it seems that the system gives special permissions when you try to access MatrixCursor type column data by creating a subclass of DocumentsProvider. This is all to say that the new changes will return legit System-V-Unix style traditional paths (like /storage/0/self/primary/Pictures/Screenshots), but doesn't seem to like to honor them as a way to refer to these files explicitly outside of the special file provider inheritance scheme (sigh.). The only way I found to extract POSIX style permissions from a valid Java File instance enumerated from a hardcoded path is referenced in my library code seen inline here. Your milage may vary.
  • The move to scoped storage imposed whenever Android 11 becomes common place on devices is going to introduce some additional constraints on selecting files by path. In particular, the DocumentsProvider subclass I had to create in my library is capable of returning handles to the Unix-type paths for Java files (for local file system cases), or otherwise to Uri objects for abstract file type references that do not conform to this specification. In general, and so moving forward with the new API requirements, the subclassed DocumentsProvider is responsible for servicing the actual file data (as a String or Byte[] array typically), and currently at the same time as when users can enumerate these files to select by their familiar path names. Needless to say, this complicates the usage pattern for developers that want the user to select the file by its name a long while before the file contents are ever read to process by the program.
  • There are some legacy style AndroidManifest.xml options that can be used with current Android 10 devices to bypass the scoped storage requirements. Read about them in my compiled links listed here (library Markdown reference). These legacy behavior enablers include the android:requestLegacyExternalStorage and android:preserveLegacyExternalStorage application tag options. Notice that this type of compatibility for pre-11 OS devices has the strange counterintuitive downside of requiring a new MinSdkVersion setting in the local application build.gradle file (with Android Studio).
  • There is some funny, again under documented, behavior going on with how to (which to use) refer to hardcoded file system path references. In my library (for example), this syntax is used. In general, my intuition is that the new Android 11 scoped storage changes make historical access patterns convoluted and clearly dated to the point of deprecation. By this I mean that appCtx.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) may return a NULL Java File handler, but a hardcoded path like new File("/storage/self/primary/Documents") can still be made to work if we configure the XML file path names correctly for use with the AndroidManifest file, e.g.:
 <provider
            android:name="com.maxieds.androidfilepickerlightlibrary.BasicFileProvider"
            android:authorities="com.maxieds.androidfilepickerlightlibrary.FileChooserActivity"
            android:exported="true"
            android:enabled="true"
            android:grantUriPermissions="true"
            android:permission="android.permission.MANAGE_DOCUMENTS"
            android:initOrder="100"
            >

            <intent-filter>
                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
            </intent-filter>

            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_provider_paths_base" />

        </provider>

I apologize for the long winded commentary I just gave. I hope this saves people some time in the future. -- M

like image 32
mds Avatar answered Oct 20 '22 04:10

mds