Android 10 falhou: EACCES (Permissão negada)

Recentemente, um dos apps que eu estava a trabalhar, nós mudamos o destino SDK para API de nível 29 (Android 10). Desde que a mudança foi feita, um de nossos fluxos de trabalho quebrou. O fluxo de trabalho é simples, perguntamos ao usuário se ele deseja enviar um logotipo para seu perfil. Deixamos o usuário escolher um arquivo de sua galeria de mídia e fazer upload dessa imagem para o servidor.

executar este fluxo de trabalho em um dispositivo Android 10 falhou ao carregar as imagens. O erro que vimos foi aberto falhou: Eacces (Permissão negada)

então, o que mudou?

de acordo com a documentação do Android.

aplicativos voltados para o Android 10 (API Nível 29) e superior recebem acesso com escopo em um dispositivo de armazenamento externo ou armazenamento com escopo, por padrão. Tais aplicações só podem ver a sua app específico do diretório

Também na documentação

para acessar qualquer outro arquivo que outro aplicativo criado, incluindo arquivos em um diretório “downloads”, o aplicativo deve usar o Acesso a Armazenamento Framework, que permite que o usuário selecione um arquivo específico.

de volta ao nosso fluxo de trabalho, quando um usuário escolhe um arquivo da galeria, não há garantia de que o arquivo escolhido foi adicionado ou editado por algum outro aplicativo. Portanto, se o usuário escolher um arquivo que, digamos, pertença a outro aplicativo, encontraremos os problemas de permissão. Então, como resolvemos isso?

existem algumas maneiras de fazer isso, neste artigo vamos olhar para duas maneiras.

se o seu aplicativo não estiver pronto para as alterações que estão chegando para o Android 10, você pode “desativar” definindo o sinalizador requestLegacyExternalStorage como true em seu manifesto.

<manifest ... > <application android:requestLegacyExternalStorage="true" ... >
...
</application></manifest>

eu falei para um dos desenvolvedores da Privacidade equipe durante a recente Android dev Cimeira e a sua recomendação era usar esta solução como temporário

Usando openFileDescriptor

A ideia é fazer uma cópia do arquivo que o usuário seleciona na galeria de mídia e adicioná-lo para o seu diretório de cache do aplicativo.Portanto, o primeiro passo é abrir o arquivo usando openFileDescriptor

val parcelFileDescriptor = context.contentResolver.openFileDescriptor(fileUri, "r", null)

vamos criar um fluxo de entrada a partir dele.

val inputStream = FileInputStream(parcelFileDescriptor.fileDescriptor)

o próximo passo é obter o nome do arquivo do URI, podemos escrever uma função de extensão na classe ContentResolver para isso. Observe que a lógica abaixo é inspirada no código de exemplo do Android docs.

fun ContentResolver.getFileName(fileUri: Uri): String {
var name = ""
val returnCursor = this.query(fileUri, null, null, null, null)
if (returnCursor != null) {
val nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
returnCursor.moveToFirst()
name = returnCursor.getString(nameIndex)
returnCursor.close()
}
return name
}

Alternativamente, você sempre pode dar o seu próprio nome de arquivo gerado.Agora que sabemos como obter o nome do arquivo, vamos criar um arquivo no diretório de cache do nosso aplicativo.

val file = File(context.cacheDir, getFileName(context.contentResolver, fileUri))

agora vamos fazer OutputStream fora deste arquivo

val outputStream = FileOutputStream(file)

a etapa final é copiar o conteúdo do arquivo original para o nosso arquivo recém-criado em nosso diretório de cache.

Você pode fazer isso de várias maneiras, uma maneira fácil é utilizar a função de cópia de IOUtils classe(org.apache.o commons.io.IOUtils).

IOUtils.copy(inputStream, outputStream)

Montando todos juntos, o código final seria algo como isto.

val parcelFileDescriptor = context.contentResolver.openFileDescriptor(fileUri, "r", null)
parcelFileDescriptor?.let {
val inputStream = FileInputStream(parcelFileDescriptor.fileDescriptor)
val file = File(context.cacheDir, context.contentResolver.getFileName(fileUri))
val outputStream = FileOutputStream(file)
IOUtils.copy(inputStream, outputStream)
}

agora que seu aplicativo tem uma cópia do arquivo que precisa ser carregado, agora você pode fazer o upload com segurança para o seu servidor de back-end.

presenter.uploadLogo(file, ....)

isso deve corrigir os problemas de permissão que você pode enfrentar.

Deixe uma resposta

O seu endereço de email não será publicado.