Accessing Android Storage on Android 13 And Higher

Android 13 introduces granular storage permissions and the photo picker tool for accessing media files

Starting with Android 13, READ_EXTERNAL_STORAGE permission has been deprecated in favor of better alternative APIs for accessing media files from Android storage. If your app targets Android 13 (API level 33) or higher and needs to access media files that other apps have created, you need to update the code to make use of the new set of APIs which are introduced with the Android 13 release

Accessing Android Storage on Android 13 And Higher
Photo by Ross Findon on Unsplash

In this article, we will see two different approaches for accessing media files from Android storage. Which approach to choose depends on the use case and business requirements. Here are the 2 ways:

1. Photo picker (No permission Required):

This tool provides a way for users to select media files, without needing to grant access to their entire media library.

Use Case: 
If your app only needs to access images, photos, and videos, you can consider using Android Photo Picker. Android system will take care of displaying the Media chooser UI

Photo picker has been backported to all Android devices having Android OS 11 and 12 (excluding Android Go devices), so users will get a uniform and standard experience on all the Android devices

Limitation:
Photo picker comes with a few limitations, e.g. it works only with Visual media types and not the Audio type

In the next section of this article, we will see the photo picker implementation

2. Request Granular Media Permission

Use Case:
While it is recommended to use the new photo picker over requesting access to all media files, your app may have a use case requiring this broad access (e.g. gallery photo backup). For these specific usages, new permissions are introduced that provide access to specific types of media files, including images, video, or audio

Starting from Target 13, you must request one or more of the following granular media permissions instead of the READ_EXTERNAL_STORAGE permission:

  1. READ_MEDIA_IMAGES for accessing images
  2. READ_MEDIA_VIDEO for accessing videos
  3. READ_MEDIA_AUDIO for accessing audio files

Check out this decision tree to help you decide what approach to choose:

Request Granular Media Permission
Source: Yacine Rezgui’s article on using the Photo picker

Now that we are clear on what approach to choose in which case, we will now look into the implementation details of both approaches

Photo Picker Implementation

The photo picker provides a browsable, searchable interface that presents the user with their media library, sorted by date from newest to oldest. This tool provides a safe, built-in way for users to select images and videos, without needing to grant your app access to their entire media library.

In addition, the “Albums” section lets users browse by helpful categories like Screenshots or Downloads. The photo picker is customizable by specifying if users should see only photos or only videos, or by setting a maximum number of items they can select.

The tool updates automatically, offering expanded functionality to your app’s users over time without requiring any code changes.

Example of photo Picker. Source: https://developer.android.com
Example of photo Picker. Source: https://developer.android.com

Implementation

To use the photo picker support library, include version 1.6.0 or higher of the androidx.activity library. It’s a simple intent to be launched and it will use the photo picker when available and fallback to ACTION_OPEN_DOCUMENT on older devices:

The support library uses the following activity result contracts to launch the photo picker:

  1. PickVisualMedia: To select a single image or video.
  2. PickMultipleVisualMedia, to select multiple images or videos.

If the photo picker isn’t available on a device, the support library automatically invokes the ACTION_OPEN_DOCUMENT intent action instead.

Select a single media item

To select a single media item, use the PickVisualMedia activity result contract, as shown in the following code snippet:

// Registers a photo picker activity launcher in single-select mode.
val pickMedia = registerForActivityResult(PickVisualMedia()) { uri ->
    // Callback is invoked after the user selects a media item or closes the
    // photo picker.
    if (uri != null) {
        Log.d("PhotoPicker", "Selected URI: $uri")
    } else {
        Log.d("PhotoPicker", "No media selected")
    }
}

// Include only one of the following calls to launch(), depending on the types
// of media that you want to allow the user to choose from.

// Launch the photo picker and allow the user to choose images and videos.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageAndVideo))

// Launch the photo picker and allow the user to choose only images.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))

// Launch the photo picker and allow the user to choose only videos.
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.VideoOnly))

// Launch the photo picker and allow the user to choose only images/videos of a
// specific MIME type, such as GIFs.
val mimeType = "image/gif"
pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.SingleMimeType(mimeType)))

Select multiple media items

To select multiple media items, set a maximum number of selectable media files, as shown in the following code snippet.

// Registers a photo picker activity launcher in multi-select mode.
// In this example, the app allows the user to select up to 5 media files.
val pickMultipleMedia =
        registerForActivityResult(PickMultipleVisualMedia(5)) { uris ->
    // Callback is invoked after the user selects media items or closes the
    // photo picker.
    if (uris.isNotEmpty()) {
        Log.d("PhotoPicker", "Number of items selected: ${uris.size}")
    } else {
        Log.d("PhotoPicker", "No media selected")
    }
}

// For this example, launch the photo picker and allow the user to choose images
// and videos. If you want the user to select a specific type of media file,
// use the overloaded versions of launch(), as shown in the section about how
// to select a single media item.
pickMultipleMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageAndVideo))

The platform limits the maximum number of files that you can ask the user to select in the photo picker. To access this limit, call getPickImagesMaxLimit(). On devices where the photo picker isn’t supported, this limit is ignored.

You can verify whether the photo picker is available on a given device by calling isPhotoPickerAvailable.

Persist media file access

By default, the system grants your app access to media files until the device is restarted or until your app stops. If your app performs some long-running work and needs to retain access to files for a longer period of time then you can call the takePersistableUriPermission() method

val flag = Intent.FLAG_GRANT_READ_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(uri, flag)

Granular media permissions

Starting in API level 33, if your app accesses other apps’ media files, don’t request READ_EXTERNAL_STORAGE permission. Instead, request one or more of these permissions: READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO

Implementation

To access media files that other apps have created, you must declare the appropriate storage-related permissions. The following code snippet demonstrates how to declare the appropriate storage permissions:

<!-- Required only if your app needs to access images or photos
     that other apps created. -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

<!-- Required only if your app needs to access videos
     that other apps created. -->
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

<!-- Required only if your app needs to access audio files
     that other apps created. -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

<!-- If your app doesn't need to access media files that other apps created,
     set the "maxSdkVersion" attribute to "28" instead. -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
                 android:maxSdkVersion="32" />

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="29" />
Example of storage permissions on Android 13
Example of storage permissions on Android 13

If you request both the READ_MEDIA_IMAGES permission and the READ_MEDIA_VIDEO permission at the same time, only one system permission dialog appears.

If the user previously granted your app the READ_EXTERNAL_STORAGE permission, the system automatically grants granular media permissions to your app.

I hope this article has helped you. If it did, don’t forget to add a comment below and share this article

Leave a comment