O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

Acessar arquivos de mídia do armazenamento compartilhado

Para oferecer uma experiência melhor ao usuário, muitos apps permitem que os usuários contribuam e acessem a mídia disponível em um volume de armazenamento externo. O framework fornece um índice otimizado em coleções de mídia, chamado armazenamento de mídia, que permite recuperar e atualizar esses arquivos com mais facilidade. Mesmo após a desinstalação do app, esses arquivos permanecem no dispositivo do usuário.

Para interagir com a abstração do armazenamento de mídia, use um objeto ContentResolver recuperado do contexto do seu app:

Kotlin

val projection = arrayOf(media-database-columns-to-retrieve)
val selection = sql-where-clause-with-placeholder-variables
val selectionArgs = values-of-placeholder-variables
val sortOrder = sql-order-by-clause

applicationContext.contentResolver.query(
    MediaStore.media-type.Media.EXTERNAL_CONTENT_URI,
    projection,
    selection,
    selectionArgs,
    sortOrder
)?.use { cursor ->
    while (cursor.moveToNext()) {
        // Use an ID column from the projection to get
        // a URI representing the media item itself.
    }
}

Java

String[] projection = new String[] {
        media-database-columns-to-retrieve
};
String selection = sql-where-clause-with-placeholder-variables;
String[] selectionArgs = new String[] {
        values-of-placeholder-variables
};
String sortOrder = sql-order-by-clause;

Cursor cursor = getApplicationContext().getContentResolver().query(
    MediaStore.media-type.Media.EXTERNAL_CONTENT_URI,
    projection,
    selection,
    selectionArgs,
    sortOrder
);

while (cursor.moveToNext()) {
    // Use an ID column from the projection to get
    // a URI representing the media item itself.
}

O sistema verifica automaticamente um volume de armazenamento externo e adiciona arquivos de mídia às seguintes coleções bem definidas:

  • Imagens, incluindo fotos e capturas de tela, armazenadas nos diretórios DCIM/ e Pictures/. O sistema adiciona esses arquivos à tabela MediaStore.Images.
  • Vídeos, armazenados nos diretórios DCIM/, Movies/ e Pictures/. O sistema adiciona esses arquivos à tabela MediaStore.Video.
  • Arquivos de áudio, armazenados nos diretórios Alarms/, Audiobooks/, Music/, Notifications/, Podcasts/ e Ringtones/, assim como em playlists de áudio que estão nos diretórios Music/ ou Movies/. O sistema adiciona esses arquivos à tabela MediaStore.Audio.
  • Arquivos de transferência por download, armazenados no diretório Download/. Em dispositivos com o Android 10 (API de nível 29) e versões mais recentes, esses arquivos são armazenados na tabela MediaStore.Downloads. Esta tabela não está disponível no Android 9 (API de nível 28) e versões anteriores.

O armazenamento de mídia também inclui uma coleção denominada MediaStore.Files. Seu conteúdo depende do app usar ou não o armazenamento com escopo, disponível em apps destinados ao Android 10 ou versões mais recentes:

  • Se o armazenamento com escopo estiver ativado, a coleção mostrará apenas as fotos, vídeos e arquivos de áudio criados pelo seu app.
  • Se o armazenamento com escopo estiver indisponível ou não estiver sendo usado, a coleção mostrará todos os tipos de arquivo de mídia.

Solicitar as permissões necessárias

Antes de executar operações em arquivos de mídia, verifique se o app declarou as permissões necessárias para acessar esses arquivos. No entanto, lembre-se de que seu app não pode declarar permissões que ele não precisa ou usa.

Permissão de armazenamento

O modelo de permissões para acessar arquivos de mídia no app depende do app usar ou não o armazenamento com escopo, disponível em apps destinados ao Android 10 ou versões mais recentes.

Armazenamento com escopo ativado

Caso seu app use o armazenamento com escopo, ele precisa solicitar permissões relacionadas ao armazenamento apenas para dispositivos que executam o Android 9 (API de nível 28) ou versões anteriores. É possível aplicar essa condição adicionando o atributo android:maxSdkVersion à declaração de permissões no arquivo de manifesto do app:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="28" />

Não é necessário solicitar permissões relacionadas ao armazenamento para dispositivos que executam o Android 10 ou versões mais recentes. Seu app pode contribuir com coleções de mídia bem definidas, incluindo a coleção MediaStore.Downloads, sem solicitar permissões relacionadas ao armazenamento. Caso esteja desenvolvendo um app de câmera, por exemplo, não é necessário solicitar permissões relacionadas ao armazenamento, porque o app é proprietário das imagens que você grava no armazenamento de mídia.

Para acessar arquivos criados por outros apps, as seguintes condições precisam ser atendidas:

Especificamente, se o app quiser acessar um arquivo da coleção MediaStore.Downloads que ele não criou, use o framework de acesso a armazenamento. Para saber mais sobre como usar esse framework, consulte o guia sobre como acessar documentos e outros arquivos.

Armazenamento com escopo indisponível

Se o app for usado em um dispositivo com o Android 9 ou versões mais recentes, ou se ele estiver usando o recurso de compatibilidade de armazenamento, você precisará solicitar a permissão READ_EXTERNAL_STORAGE para acessar arquivos de mídia. Se quiser modificar arquivos de mídia, também precisará solicitar a permissão WRITE_EXTERNAL_STORAGE.

Permissão de localização de mídia

Caso seu app use armazenamento com escopo, para que ele recupere metadados Exif não editados de fotos, é necessário declarar a permissão ACCESS_MEDIA_LOCATION no manifesto do app e solicitá-la no momento da execução.

Consultar uma coleção de mídia

Para encontrar uma mídia que esteja de acordo com um determinado conjunto de condições, por exemplo, duração de cinco minutos ou mais, use uma declaração de seleção SQL semelhante à mostrada no snippet de código a seguir.

Kotlin

// Need the READ_EXTERNAL_STORAGE permission if accessing video files that your
// app didn't create.

// Container for information about each video.
data class Video(val uri: Uri,
    val name: String,
    val duration: Int,
    val size: Int
)
val videoList = mutableListOf<Video>()

val projection = arrayOf(
    MediaStore.Video.Media._ID,
    MediaStore.Video.Media.DISPLAY_NAME,
    MediaStore.Video.Media.DURATION,
    MediaStore.Video.Media.SIZE
)

// Show only videos that are at least 5 minutes in duration.
val selection = "${MediaStore.Video.Media.DURATION} >= ?"
val selectionArgs = arrayOf(
    TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES).toString()
)

// Display videos in alphabetical order based on their display name.
val sortOrder = "${MediaStore.Video.Media.DISPLAY_NAME} ASC"

val query = ContentResolver.query(
    MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
    projection,
    selection,
    selectionArgs,
    sortOrder
)
query?.use { cursor ->
    // Cache column indices.
    val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)
    val nameColumn =
            cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME)
    val durationColumn =
            cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION)
    val sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE)

    while (cursor.moveToNext()) {
        // Get values of columns for a given video.
        val id = cursor.getLong(idColumn)
        val name = cursor.getString(nameColumn)
        val duration = cursor.getInt(durationColumn)
        val size = cursor.getInt(sizeColumn)

        val contentUri: Uri = ContentUris.withAppendedId(
            MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
            id
        )

        // Stores column values and the contentUri in a local object
        // that represents the media file.
        videoList += Video(contentUri, name, duration, size)
    }
}

Java

// Need the READ_EXTERNAL_STORAGE permission if accessing video files that your
// app didn't create.

// Container for information about each video.
class Video {
    private final Uri uri;
    private final String name;
    private final int duration;
    private final int size;

    public Video(Uri uri, String name, int duration, int size) {
        this.uri = uri;
        this.name = name;
        this.duration = duration;
        this.size = size;
    }
}
List<Video> videoList = new ArrayList<Video>();

String[] projection = new String[] {
    MediaStore.Video.Media._ID,
    MediaStore.Video.Media.DISPLAY_NAME,
    MediaStore.Video.Media.DURATION,
    MediaStore.Video.Media.SIZE
};
String selection = MediaStore.Video.Media.DURATION +
        " >= ?";
String[] selectionArgs = new String[] {
    String.valueOf(TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES));
};
String sortOrder = MediaStore.Video.Media.DISPLAY_NAME + " ASC";

try (Cursor cursor = getApplicationContext().getContentResolver().query(
    MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
    projection,
    selection,
    selectionArgs,
    sortOrder
)) {
    // Cache column indices.
    int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
    int nameColumn =
            cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME);
    int durationColumn =
            cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION);
    int sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE);

    while (cursor.moveToNext()) {
        // Get values of columns for a given video.
        long id = cursor.getLong(idColumn);
        String name = cursor.getString(nameColumn);
        int duration = cursor.getInt(durationColumn);
        int size = cursor.getInt(sizeColumn);

        Uri contentUri = ContentUris.withAppendedId(
                MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id);

        // Stores column values and the contentUri in a local object
        // that represents the media file.
        videoList.add(new Video(contentUri, name, duration, size));
    }
}

Ao realizar essa consulta no seu app, lembre-se do seguinte:

  • Chame o método query() em uma linha de execução de worker.
  • Armazene em cache os índices de coluna para não precisar chamar getColumnIndexOrThrow() sempre que processar uma linha do resultado da consulta.
  • Anexe o código ao URI de conteúdo, conforme mostrado no snippet de código.
  • Os dispositivos que executam o Android 10 e versões mais recentes exigem nomes de coluna definidos na API MediaStore. Se uma biblioteca dependente do seu app espera um nome de coluna indefinido na API, como "MimeType", use CursorWrapper para traduzir o nome da coluna de forma dinâmica no processo do app.

Carregar miniaturas de arquivos

Caso seu app mostre vários arquivos de mídia e solicite que o usuário escolha um deles, é mais eficiente carregar versões de visualização (ou miniaturas) dos arquivos, em vez deles próprios.

Para carregar a miniatura de um determinado arquivo de mídia, use loadThumbnail() e transmita o tamanho da miniatura que você quer carregar, conforme mostrado no snippet de código a seguir:

Kotlin

// Load thumbnail of a specific media item.
val thumbnail: Bitmap =
        applicationContext.contentResolver.loadThumbnail(
        content-uri, Size(640, 480), null)

Java

// Load thumbnail of a specific media item.
Bitmap thumbnail =
        getApplicationContext().getContentResolver().loadThumbnail(
        content-uri, new Size(640, 480), null);

Abrir um arquivo de mídia

A lógica específica usada para abrir um arquivo de mídia depende se o conteúdo de mídia é melhor representado como descritor ou fluxo de arquivos:

Descritor do arquivo

Para abrir um arquivo de mídia com um descritor de arquivos, use uma lógica semelhante à mostrada no snippet de código a seguir.

Kotlin

// Open a specific media item using ParcelFileDescriptor.
val resolver = applicationContext.contentResolver

// "rw" for read-and-write;
// "rwt" for truncating or overwriting existing file contents.
val readOnlyMode = "r"
resolver.openFileDescriptor(content-uri, readOnlyMode).use { pfd ->
    // Perform operations on "pfd".
}

Java

// Open a specific media item using ParcelFileDescriptor.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();

// "rw" for read-and-write;
// "rwt" for truncating or overwriting existing file contents.
String readOnlyMode = "r";
try (ParcelFileDescriptor pfd =
        resolver.openFileDescriptor(content-uri, readOnlyMode)) {
    // Perform operations on "pfd".
} catch (IOException e) {
    e.printStackTrace();
}

Fluxo de arquivos

Para abrir um arquivo de mídia com um fluxo de arquivos, use uma lógica semelhante à mostrada no snippet de código a seguir.

Kotlin

// Open a specific media item using InputStream.
val resolver = applicationContext.contentResolver
resolver.openInputStream(content-uri).use { stream ->
    // Perform operations on "stream".
}

Java

// Open a specific media item using InputStream.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();
try (InputStream stream = resolver.openInputStream(content-uri)) {
    // Perform operations on "stream".
}

Considerações ao acessar conteúdo de mídia

Ao acessar o conteúdo de mídia, lembre-se das considerações discutidas nas seções a seguir.

Volumes de armazenamento

Apps voltados ao Android 10 ou versões mais recentes podem acessar o nome único que o sistema atribui para cada volume de armazenamento externo. Esse sistema de nomenclatura ajuda você a organizar e indexar o conteúdo de maneira eficiente, além de fornecer controle sobre o local em que novos conteúdos serão armazenados.

O principal volume de armazenamento compartilhado é sempre chamado de VOLUME_EXTERNAL_PRIMARY. Você pode descobrir outros volumes chamando MediaStore.getExternalVolumeNames():

Kotlin

val volumeNames: Set<String> = MediaStore.getExternalVolumeNames(context)
val firstVolumeName = volumeNames.iterator().next()

Java

Set<String> volumeNames = MediaStore.getExternalVolumeNames(context);
String firstVolumeName = volumeNames.iterator().next();

Informações de localização em fotos

Algumas fotos contêm informações de local nos metadados EXIF, que permitem que os usuários vejam o lugar em que uma foto foi tirada. No entanto, como essas informações de local são confidenciais, o Android 10 as oculta do seu app por padrão quando o armazenamento com escopo é usado.

Caso seu app precise de acesso às informações de local de uma foto, siga as etapas a seguir:

  1. Solicite a permissão ACCESS_MEDIA_LOCATION no manifesto do app.
  2. A partir do seu objeto MediaStore, consiga os bytes exatos da foto chamando setRequireOriginal() e transmita o URI da foto, conforme mostrado no snippet de código a seguir.

    Kotlin

    val photoUri: Uri = Uri.withAppendedPath(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            cursor.getString(idColumnIndex)
    )
    
    // Get location data using the Exifinterface library.
    // Exception occurs if ACCESS_MEDIA_LOCATION permission isn't granted.
    photoUri = MediaStore.setRequireOriginal(photoUri)
    contentResolver.openInputStream(photoUri)?.use { stream ->
        ExifInterface(stream).run {
            // If lat/long is null, fall back to the coordinates (0, 0).
            val latLong = latLong ?: doubleArrayOf(0.0, 0.0)
        }
    }
    

    Java

    Uri photoUri = Uri.withAppendedPath(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            cursor.getString(idColumnIndex));
    
    final double[] latLong;
    
    // Get location data using the Exifinterface library.
    // Exception occurs if ACCESS_MEDIA_LOCATION permission isn't granted.
    photoUri = MediaStore.setRequireOriginal(photoUri);
    InputStream stream = getContentResolver().openInputStream(photoUri);
    if (stream != null) {
        ExifInterface exifInterface = new ExifInterface(stream);
        double[] returnedLatLong = exifInterface.getLatLong();
    
        // If lat/long is null, fall back to the coordinates (0, 0).
        latLong = returnedLatLong != null ? returnedLatLong : new double[2];
    
        // Don't reuse the stream associated with
        // the instance of "ExifInterface".
        stream.close();
    } else {
        // Failed to load the stream, so return the coordinates (0, 0).
        latLong = new double[2];
    }
    

Compartilhamento de mídia

Alguns apps permitem que os usuários compartilhem arquivos de mídia entre si. Por exemplo, apps de mídia social permitem que os usuários compartilhem fotos e vídeos com amigos.

Para compartilhar arquivos de mídia, use um URI content://, conforme recomendado no guia de criação de um provedor de conteúdo.

Acessar conteúdo usando caminhos de arquivos brutos

Caso não tenha permissões relacionadas ao armazenamento, é possível acessar arquivos no diretório específico do app, bem como arquivos de mídia atribuídos ao app, usando a API File.

Se o app tentar acessar um arquivo usando a API File e não tiver as permissões necessárias, ocorrerá uma FileNotFoundException.

Para acessar outros arquivos no armazenamento compartilhado em um dispositivo com o Android 10, é recomendável desativar o armazenamento com escopo definindo requestLegacyExternalStorage como true no arquivo de manifesto do app.

Acesso ao conteúdo do código nativo

Você pode se deparar com situações em que seu app precise trabalhar com um arquivo de mídia específico em código nativo, por exemplo, um arquivo que outro app tenha compartilhado com seu app ou um arquivo da coleção de mídia do usuário.

Para que seu app possa ler arquivos de mídia usando métodos de arquivo nativos, como fopen(), faça o seguinte:

  1. Defina requestLegacyExternalStorage como true no arquivo de manifesto do app.
  2. Solicite a permissão READ_EXTERNAL_STORAGE.

Caso precise gravar nesses arquivos de mídia, transmita o descritor de arquivo associado do seu código baseado em Java ou Kotlin. O snippet de código a seguir mostra como transmitir o descritor de arquivo de um objeto de mídia ao código nativo do seu app:

Kotlin

val contentUri: Uri = ContentUris.withAppendedId(
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
        cursor.getLong(BaseColumns._ID))
val fileOpenMode = "r"
val parcelFd = resolver.openFileDescriptor(contentUri, fileOpenMode)
val fd = parcelFd?.detachFd()
// Pass the integer value "fd" into your native code. Remember to call
// close(2) on the file descriptor when you're done using it.

Java

Uri contentUri = ContentUris.withAppendedId(
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
        cursor.getLong(Integer.parseInt(BaseColumns._ID)));
String fileOpenMode = "r";
ParcelFileDescriptor parcelFd =
        resolver.openFileDescriptor(contentUri, fileOpenMode);
if (parcelFd != null) {
    int fd = parcelFd.detachFd();
    // Pass the integer value "fd" into your native code. Remember to call
    // close(2) on the file descriptor when you're done using it.
}

Para saber mais sobre como acessar arquivos em código nativo, veja a palestra Files for Miles (em inglês), da Conferência de Desenvolvedores Android 2018, a partir de 15:20.

Atribuição de apps de arquivos de mídia

Quando o armazenamento com escopo está ativado para um app destinado ao Android 10 ou versões mais recentes, o sistema atribui um app para cada arquivo de mídia, o que determina os arquivos que seu app poderá acessar quando não tiver solicitado as permissões de armazenamento. Cada arquivo pode ser atribuído a apenas um app. Portanto, se o app criar um arquivo de mídia que é armazenado em fotos, vídeos ou arquivos de áudio, o app terá acesso a ele.

No entanto, se o usuário desinstalar e reinstalar o app, será necessário solicitar que READ_EXTERNAL_STORAGE acesse os arquivos criados originalmente pelo app. Essa solicitação de permissão é necessária porque o sistema considera o arquivo como atribuído à versão do app instalada anteriormente, em vez da recém-instalada.

Adicionar um item

Para adicionar um item de mídia a uma coleção já existente, chame um código semelhante ao seguinte:

Kotlin

// Add a specific media item.
val resolver = applicationContext.contentResolver

// Find all audio files on the primary external storage device.
// On API <= 28, use VOLUME_EXTERNAL instead.
val audioCollection = MediaStore.Audio.Media.getContentUri(
        MediaStore.VOLUME_EXTERNAL_PRIMARY)

// Publish a new song.
val newSongDetails = ContentValues().apply {
    put(MediaStore.Audio.Media.DISPLAY_NAME, "My Song.mp3")
}

// Keeps a handle to the new song's URI in case we need to modify it
// later.
val myFavoriteSongUri = resolver
        .insert(audioCollection, newSongDetails)

Java

// Add a specific media item.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();

// Find all audio files on the primary external storage device.
// On API <= 28, use VOLUME_EXTERNAL instead.
Uri audioCollection = MediaStore.Audio.Media.getContentUri(
        MediaStore.VOLUME_EXTERNAL_PRIMARY);

// Publish a new song.
ContentValues newSongDetails = new ContentValues();
newSongDetails.put(MediaStore.Audio.Media.DISPLAY_NAME,
        "My Song.mp3");

// Keeps a handle to the new song's URI in case we need to modify it
// later.
Uri myFavoriteSongUri = resolver
        .insert(audioCollection, newSongDetails);

Alternar status pendente para arquivos de mídia

Se o app realizar operações que podem ser demoradas, por exemplo, programar arquivos de mídia, é útil ter acesso exclusivo ao arquivo durante o processamento. Em dispositivos que executam o Android 10 ou versões mais recentes, seu app pode conseguir esse acesso exclusivo definindo o valor da sinalização IS_PENDING como 1. Até que o valor seja alterado de IS_PENDING para zero, apenas seu app pode ver o arquivo.

O snippet de código a seguir se baseia no snippet de código anterior. Ele mostra como usar a sinalização IS_PENDING ao armazenar uma música longa no diretório correspondente à coleção MediaStore.Audio:

Kotlin

// Add a media item that other apps shouldn't see until the item is
// fully written to the media store.
val resolver = applicationContext.contentResolver

// Find all audio files on the primary external storage device.
// On API <= 28, use VOLUME_EXTERNAL instead.
val audioCollection = MediaStore.Audio.Media
        .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)

val songDetails = ContentValues().apply {
    put(MediaStore.Audio.Media.DISPLAY_NAME, "My Workout Playlist.mp3")
    put(MediaStore.Audio.Media.IS_PENDING, 1)
}

val songContentUri = resolver.insert(audioCollection, songDetails)

resolver.openFileDescriptor(songContentUri, "w", null).use { pfd ->
    // Write data into the pending audio file.
}

// Now that we're finished, release the "pending" status, and allow other apps
// to play the audio track.
songDetails.clear()
songDetails.put(MediaStore.Audio.Media.IS_PENDING, 0)
resolver.update(songContentUri, songDetails, null, null)

Java

// Add a media item that other apps shouldn't see until the item is
// fully written to the media store.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();

// Find all audio files on the primary external storage device.
// On API <= 28, use VOLUME_EXTERNAL instead.
Uri audioCollection = MediaStore.Audio.Media.getContentUri(
        MediaStore.VOLUME_EXTERNAL_PRIMARY);

ContentValues songDetails = new ContentValues();
newSongDetails.put(MediaStore.Audio.Media.DISPLAY_NAME,
        "My Workout Playlist.mp3");
newSongDetails.put(MediaStore.Audio.Media.IS_PENDING, 1);

Uri songContentUri = resolver
        .insert(audioCollection, songDetails);

try (ParcelableFileDescriptor pfd =
        resolver.openFileDescriptor(longSongContentUri, "w", null)) {
    // Write data into the pending audio file.
}

// Now that we're finished, release the "pending" status, and allow other apps
// to play the audio track.
songDetails.clear();
songDetails.put(MediaStore.Audio.Media.IS_PENDING, 0);
resolver.update(longSongContentUri, songDetails, null, null);

Dê uma dica sobre a localização do arquivo

Quando seu app armazena mídia em um dispositivo com Android 10, por padrão a mídia é organizada com base no tipo. Por exemplo, por padrão, novos arquivos de imagem são colocados no diretório Environment.DIRECTORY_PICTURES, que corresponde à coleção MediaStore.Images.

Se o app estiver ciente de um local específico em que os arquivos precisam ser armazenados, por exemplo, um álbum de fotos chamado Pictures/MyVacationPictures, você poderá definir MediaColumns.RELATIVE_PATH para oferecer uma dica ao sistema sobre onde armazenar os arquivos recém-gravados.

Atualizar um item

Para atualizar um arquivo de mídia do seu app, execute um código semelhante a este:

Kotlin

// Updates an existing media item.
val mediaId = // MediaStore.Audio.Media._ID of item to update.
val resolver = applicationContext.contentResolver

// When performing a single item update, prefer using the ID
val selection = "${MediaStore.Audio.Media._ID} = ?"

// By using selection + args we protect against improper escaping of // values.
val selectionArgs = arrayOf(mediaId.toString())

// Update an existing song.
val updatedSongDetails = ContentValues().apply {
    put(MediaStore.Audio.Media.DISPLAY_NAME, "My Favorite Song.mp3")
}

// Use the individual song's URI to represent the collection that's
// updated.
val numSongsUpdated = resolver.update(
        myFavoriteSongUri,
        updatedSongDetails,
        selection,
        selectionArgs)

Java

// Updates an existing media item.
long mediaId = // MediaStore.Audio.Media._ID of item to update.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();

// When performing a single item update, prefer using the ID
String selection = MediaStore.Audio.Media._ID + " = ?";

// By using selection + args we protect against improper escaping of
// values. Here, "song" is an in-memory object that caches the song's
// information.
String[] selectionArgs = new String[] { getId().toString() };

// Update an existing song.
ContentValues updatedSongDetails = new ContentValues();
updatedSongDetails.put(MediaStore.Audio.Media.DISPLAY_NAME,
        "My Favorite Song.mp3");

// Use the individual song's URI to represent the collection that's
// updated.
int numSongsUpdated = resolver.update(
        myFavoriteSongUri,
        updatedSongDetails,
        selection,
        selectionArgs);

Se o armazenamento com escopo estiver indisponível ou inativo, o processo mostrado no snippet de código anterior também funcionará para arquivos que não são dele.

Atualizar arquivos de mídia de outros apps

Em geral, caso seu app use armazenamento com escopo, ele não pode atualizar um arquivo de mídia que tenha sido disponibilizado no armazenamento por outro app.

Contudo, é possível conseguir o consentimento do usuário para modificar esse arquivo, capturando a RecoverableSecurityException que a plataforma gera. Você pode então solicitar que o usuário conceda ao app acesso de gravação para esse item específico, conforme mostrado no snippet de código a seguir.

Kotlin

// Apply a grayscale filter to the image at the given content URI.
try {
    contentResolver.openFileDescriptor(image-content-uri, "w")?.use {
        setGrayscaleFilter(it)
    }
} catch (securityException: SecurityException) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val recoverableSecurityException = securityException as?
            RecoverableSecurityException ?:
            throw RuntimeException(securityException.message, securityException)

        val intentSender =
            recoverableSecurityException.userAction.actionIntent.intentSender
        intentSender?.let {
            startIntentSenderForResult(intentSender, image-request-code,
                    null, 0, 0, 0, null)
        }
    } else {
        throw RuntimeException(securityException.message, securityException)
    }
}

Java

try {
    ParcelFileDescriptor imageFd = getContentResolver()
            .openFileDescriptor(image-content-uri, "w");
    setGrayscaleFilter(imageFd);
} catch (SecurityException securityException) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        RecoverableSecurityException recoverableSecurityException;
        if (securityException instanceof RecoverableSecurityException) {
            recoverableSecurityException =
                    (RecoverableSecurityException)securityException;
        } else {
            throw new RuntimeException(
                    securityException.getMessage(), securityException);
        }
        IntentSender intentSender =recoverableSecurityException.getUserAction()
                .getActionIntent().getIntentSender();
        startIntentSenderForResult(intentSender, image-request-code,
                null, 0, 0, 0, null);
    } else {
        throw new RuntimeException(
                securityException.getMessage(), securityException);
    }
}

Conclua esse processo sempre que seu app precisar modificar um arquivo de mídia que ele não tenha criado.

Caso seu app tenha outro caso de uso que não seja coberto pelo armazenamento com escopo, registre uma solicitação de recurso e use o recurso de compatibilidade de apps fornecido pela plataforma.

Remover um item

Para remover um item que não é mais necessário para seu app do armazenamento de mídia, use uma lógica semelhante à mostrada no snippet de código a seguir:

Kotlin

// Remove a specific media item.
val resolver = applicationContext.contentResolver

// URI of the image to remove.
val imageUri = "..."

// WHERE clause.
val selection = "..."
val selectionArgs = "..."

// Perform the actual removal.
val numImagesRemoved = resolver.delete(
        imageUri,
        selection,
        selectionArgs)

Java

// Remove a specific media item.
ContentResolver resolver = getApplicationContext()
        getContentResolver();

// URI of the image to remove.
Uri imageUri = "...";

// WHERE clause.
String selection = "...";
String[] selectionArgs = "...";

// Perform the actual removal.
int numImagesRemoved = resolver.delete(
        imageUri,
        selection,
        selectionArgs);

Caso o armazenamento com escopo esteja indisponível ou inativo, você pode usar o snippet de código anterior para remover arquivos de outros apps. No entanto, se o armazenamento com escopo está ativado, você precisa capturar um RecoverableSecurityException para cada arquivo que seu app for remover, conforme descrito na seção sobre como atualizar itens de mídia.

Caso seu app tenha outro caso de uso que não seja coberto pelo armazenamento com escopo, registre uma solicitação de recurso e use o recurso de compatibilidade de apps fornecido pela plataforma.

Casos de uso que exigem uma alternativa ao armazenamento de mídia

Se o app executa principalmente uma das funções a seguir, considere uma alternativa às APIs MediaStore.

Gerenciar grupos de arquivos de mídia

Em geral, os apps de criação de mídia gerenciam grupos de arquivos em uma hierarquia de diretórios. Para oferecer esse recurso no seu app, use a ação da intent ACTION_OPEN_DOCUMENT_TREE, conforme descrito no guia sobre como armazenar e acessar documentos e outros arquivos.

Trabalhar com outros tipos de arquivos

Se o app trabalha com documentos e arquivos que não contêm exclusivamente conteúdo de mídia, por exemplo, arquivos que usam a extensão EPUB ou PDF, use a ação da intent ACTION_OPEN_DOCUMENT, conforme descrito no guia sobre como armazenar e acessar documentos e outros arquivos.

Compartilhamento de arquivos em apps complementares

Nos casos em que você oferece um conjunto de apps complementares, por exemplo, um app de mensagens e um de perfil, configure o compartilhamento de arquivos usando URIs content://. Além disso, recomendamos esse fluxo de trabalho como uma prática recomendada de segurança.

Outros recursos

Para ver mais informações sobre como armazenar e acessar mídia, consulte os recursos a seguir.

Amostras

Vídeos