Skip to content

Permissions

modernstorage-permissions is a library simplifying checking storage permissions. It works on Android 21+.

Add dependency to project

modernstorage-permissions is available on mavenCentral().

// build.gradle
implementation("com.google.modernstorage:modernstorage-permissions:1.0.0-alpha06")

API reference

modernstorage-permissions API reference is available here.

Storage Permissions usage

Android provides two types of storage to save files:

  • App-specific (internal folder, external app folder)
  • Shared (visible using the system file manager)

With the introduction of Scoped Storage in Android 10 (API 29), the storage access has deeply changed:

  • You can add media & document files without any permission, reading and editing media files created by other apps require READ_EXTERNAL_STORAGE
  • Document files created by other apps are readable only using the Storage Access Framework

To help you navigate common use cases, check out the below table:

Actions API 29- API 29+
Read media files (created by self) READ_EXTERNAL_STORAGE No permission needed
Read media files (created by all apps) READ_EXTERNAL_STORAGE READ_EXTERNAL_STORAGE
Add file (media, document) WRITE_EXTERNAL_STORAGE No permission needed
Edit & Delete media files (created by self) WRITE_EXTERNAL_STORAGE No permission needed
Edit & Delete media files (created by all apps) WRITE_EXTERNAL_STORAGE READ_EXTERNAL_STORAGE 1️⃣
Edit & Delete document files (created by self) WRITE_EXTERNAL_STORAGE No permission needed
Edit & Delete document files (created by all apps) WRITE_EXTERNAL_STORAGE Storage Access Framework

1️⃣ When editing or deleting media files created by other apps on API 29+ (Android 10), you have to request explicitly user's consent. Read more here.

Check if app can access files

// Check if the app can read image & document files created by itself
val storagePermissions = StoragePermissions(context)

storagePermissions.hasAccess(
    action = Action.READ,
    types = listOf(FileType.Image, FileType.Document),
    createdBy = StoragePermissions.CreatedBy.Self
)

// Check if the app can read video & audio files created by all apps
storagePermissions.hasAccess(
    action = Action.READ,
    types = listOf(FileType.Video, FileType.Audio),
    createdBy = StoragePermissions.CreatedBy.AllApps
)

// Check if the app can read & write image & document files created by itself
val storagePermissions = StoragePermissions(context)

storagePermissions.hasAccess(
    action = Action.READ_AND_WRITE,
    types = listOf(FileType.Image, FileType.Video),
    createdBy = StoragePermissions.CreatedBy.Self
)

// Check if the app can read & write video & audio files created by all apps
storagePermissions.hasAccess(
    action = Action.READ_AND_WRITE,
    types = listOf(FileType.Audio, FileType.Document),
    createdBy = StoragePermissions.CreatedBy.AllApps
)

Get storage permissions

If the method returns an empty list, it means your app on the current device, given the defined usage, doesn't need any permissions.

// Get required permissions to read & write video & audio files created by all apps
StoragePermissions.getPermissions(
    action = Action.READ,
    types = listOf(FileType.Video, FileType.Audio),
    createdBy = StoragePermissions.CreatedBy.AllApps
)

// Get required permissions to read & write image & document files created by the app itself
StoragePermissions.getPermissions(
    action = Action.READ_AND_WRITE,
    types = listOf(FileType.Image, FileType.Document),
    createdBy = StoragePermissions.CreatedBy.Self
)

Request storage permissions

While you can use the ActivityResultContracts.RequestPermission provided by default with the Jetpack Activity or Fragment library to request storage permissions with input from StoragePermissions.getPermissions, modernstorage-permissions bundles a custom ActivityResultContract named RequestAccess to request the right storage permissions to simplify the logic for you.

@Composable
fun RequestAccessExample() {
    // Register a callback for the Activity Result
    val requestAccess = rememberLauncherForActivityResult(RequestAccess()) { hasAccess ->
        if (hasAccess) {
            // write logic here
        }
    }

    Column {
        Button(onClick = {
            // Request permission to read video & audio files created by all apps
            requestAccess.launch(
                 RequestAccess.Args(
                    action = Action.READ,
                    types = listOf(
                        StoragePermissions.FileType.Video,
                        StoragePermissions.FileType.Audio
                    ),
                    createdBy = StoragePermissions.CreatedBy.AllApps
                ),
            )
        }) {
            Text("I want to read all video & audio files")
        }

        Button(onClick = {
            // Request permission to read & write image & document files created by the app itself
            requestAccess.launch(
                 RequestAccess.Args(
                    action = Action.READ_AND_WRITE,
                    types = listOf(
                        StoragePermissions.FileType.Image,
                        StoragePermissions.FileType.Document
                    ),
                    createdBy = StoragePermissions.CreatedBy.Self
                )
            )
        }) {
            Text("I want to read & write the app's image & document files")
        }
    }
}
// Register a callback for the Activity Result
val requestAccess = registerForActivityResult(RequestAccess()) { hasAccess ->
    if (hasAccess) {
        // write logic here
    }
}


// Request permission to read video & audio files created by all apps
requestAccess.launch(
     RequestAccess.Args(
        action = Action.READ,
        types = listOf(StoragePermissions.FileType.Video, StoragePermissions.FileType.Audio),
        createdBy = StoragePermissions.CreatedBy.AllApps
    )
)

// Request permission to read & write image & document files created by the app itself
requestAccess.launch(
     RequestAccess.Args(
        action = Action.READ_AND_WRITE,
        types = listOf(StoragePermissions.FileType.Image, StoragePermissions.FileType.Document),
        createdBy = StoragePermissions.CreatedBy.Self
    )
)