A Galeria do Android no Android 4.4 (KitKat) retorna URI diferente para Intent.ACTION_GET_CONTENT

214

Antes do KitKat (ou antes da nova Galeria), Intent.ACTION_GET_CONTENTum URI retornado como este

conteúdo: // media / external / images / media / 3951.

O uso de ContentResolvere query for MediaStore.Images.Media.DATAretornou o URL do arquivo.

No KitKat, no entanto, a Galeria retorna um URI (via "Último") como este:

conteúdo: //com.android.providers.media.documents/document/image: 3951

Como eu manejo isso?

Michael Greifeneder
fonte
21
Fora do manguito, eu encontraria maneiras de usar o conteúdo que não requer acesso direto ao arquivo. Por exemplo, isso Urideve ser aberto como um fluxo via ContentResolver. Há muito tempo fico nervoso com aplicativos que assumem que um content:// Urique representa um arquivo sempre pode ser convertido em um File.
CommonsWare
1
@ CommonsWare, se eu quiser salvar um caminho de imagem no sqlite db para abri-lo mais tarde, devo salvar o URI ou o caminho absoluto do arquivo?
Cobra
2
@ CommmonsWare Concordo com o seu nervosismo. :-) No entanto, preciso passar um nome de arquivo (para uma imagem) para o código nativo. Uma solução é copiar os dados obtidos utilizando um InputStreamsobre o ContentResolverpara um local pré-designado por isso tem um nome de arquivo conhecida. No entanto, isso parece um desperdício para mim. Alguma outra sugestão?
darrenp
2
@ Darrenp: Ummm ..., reescreva o código nativo para trabalhar com um InputStreamover JNI? Infelizmente, não existem muitas opções para você.
CommonsWare
1
Isso é útil saber. Obrigado pela sua resposta. Desde então, descobri que agora estamos passando a imagem para C ++ na memória, e não através de um arquivo, para que agora possamos usar um em InputStreamvez de um arquivo (o que é ótimo). Somente a leitura de tags EXIF ​​é um pouco complicada e requer a biblioteca de Drew Noakes . Muito obrigado pelos seus comentários.
darrenp

Respostas:

108

Tente o seguinte:

if (Build.VERSION.SDK_INT <19){
    Intent intent = new Intent(); 
    intent.setType("image/jpeg");
    intent.setAction(Intent.ACTION_GET_CONTENT);
    startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.select_picture)),GALLERY_INTENT_CALLED);
} else {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("image/jpeg");
    startActivityForResult(intent, GALLERY_KITKAT_INTENT_CALLED);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode != Activity.RESULT_OK) return;
    if (null == data) return;
    Uri originalUri = null;
    if (requestCode == GALLERY_INTENT_CALLED) {
        originalUri = data.getData();
    } else if (requestCode == GALLERY_KITKAT_INTENT_CALLED) {
        originalUri = data.getData();
        final int takeFlags = data.getFlags()
                & (Intent.FLAG_GRANT_READ_URI_PERMISSION
                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        // Check for the freshest data.
        getContentResolver().takePersistableUriPermission(originalUri, takeFlags);
    }

    loadSomeStreamAsynkTask(originalUri);

}

Provavelmente precisa

@SuppressLint ("NewApi")

para

takePersistableUriPermission

localizador
fonte
1
Você gostaria de elaborar o que o código KitKat está fazendo? Isso requer alguma nova permissão? O código pré KitKat também funciona para mim no KitKat. Então, por que eu escolheria usar um código diferente para o KitKat? Obrigado.
9788 Michael Greifeneder
67
parece que não podemos encontrar o caminho do novo sdks uri. Também é uma pena que o Google tenha feito esse tipo de alteração sem a documentação e o anúncio adequados.
user65721
1
Você poderia explicar como obter o URL do arquivo? Eu gostaria de obter o caminho real no sdcard. Por exemplo, se for uma imagem, eu gostaria de obter esse caminho /storage/sdcard0/DCIM/Camera/IMG_20131118_153817_119.jpg em vez do documento Uri.
Álvaro
4
Com base nos documentos do KitKat ( developer.android.com/about/versions/… ), pode não ser o que o OP precisa, a menos que ele pretenda usar / editar documentos que pertencem aos outros aplicativos. Se o OP quiser uma cópia ou fazer as coisas de maneira consistente com as versões mais antigas, a resposta do @voytez seria mais apropriada.
Colin M.
8
Isso não está funcionando para mim. Eu recebo a seguinte exceção (no estoque 4.4.2): E / AndroidRuntime (29204): Causada por: java.lang.SecurityException: Solicitado bandeiras 0x1, mas apenas 0x0 são permitidos
Russell Stewart
177

Isso não requer permissões especiais e funciona com o Storage Access Framework, bem como o ContentProviderpadrão não oficial (caminho do arquivo no _datacampo).

/**
 * Get a file path from a Uri. This will get the the path for Storage Access
 * Framework Documents, as well as the _data field for the MediaStore and
 * other file-based ContentProviders.
 *
 * @param context The context.
 * @param uri The Uri to query.
 * @author paulburke
 */
public static String getPath(final Context context, final Uri uri) {

    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        // ExternalStorageProvider
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/" + split[1];
            }

            // TODO handle non-primary volumes
        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);
        }
        // MediaProvider
        else if (isMediaDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }

            final String selection = "_id=?";
            final String[] selectionArgs = new String[] {
                    split[1]
            };

            return getDataColumn(context, contentUri, selection, selectionArgs);
        }
    }
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) {

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;
}

/**
 * Get the value of the data column for this Uri. This is useful for
 * MediaStore Uris, and other file-based ContentProviders.
 *
 * @param context The context.
 * @param uri The Uri to query.
 * @param selection (Optional) Filter used in the query.
 * @param selectionArgs (Optional) Selection arguments used in the query.
 * @return The value of the _data column, which is typically a file path.
 */
public static String getDataColumn(Context context, Uri uri, String selection,
        String[] selectionArgs) {

    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = {
            column
    };

    try {
        cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                null);
        if (cursor != null && cursor.moveToFirst()) {
            final int index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}


/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is ExternalStorageProvider.
 */
public static boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is DownloadsProvider.
 */
public static boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is MediaProvider.
 */
public static boolean isMediaDocument(Uri uri) {
    return "com.android.providers.media.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is Google Photos.
 */
public static boolean isGooglePhotosUri(Uri uri) {
    return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}

Veja uma versão atualizada deste método aqui .

Paul Burke
fonte
2
Isso funcionou de maneira fantástica em uma interface do usuário do 4.4 Nexus 5 Documents e em alguns outros dispositivos anteriores do KitKat usando os aplicativos de galeria padrão, obrigado Paul!
21413 Josh
1
Obrigado por isso, demorei anos para chegar até aqui com o sdk 19 !! Meu problema é que meu dispositivo está usando o Google drive como navegador de arquivos. Se o arquivo estiver no caminho da imagem do dispositivo, tudo ficará bem, mas se o arquivo estiver na unidade, ele não será aberto. Talvez eu só precise lidar com imagens de abertura do google drive. A coisa é que meu aplicativo foi escrito para usar o caminho do arquivo e obter imagens usando amostragem ...
RuAware
2
@RuAware Quando você seleciona um arquivo do Drive, ele devolve Authority: com.google.android.apps.docs.storagee Segments: [document, acc=1;doc=667]. Não tenho certeza, mas suponha que o docvalor seja o UriID que você possa consultar. Provavelmente, você precisará configurar permissões conforme detalhado em "Autorizar seu aplicativo no Android" aqui: developers.google.com/drive/integrate-android-ui . Atualize aqui se você descobrir.
Paul Burke
30
isso é absolutamente horrível! você não deve continuar propagando código que "engana" dessa maneira. ele só suporta a fonte de aplicativos que você sabe o padrão para, e toda a ponto do modelo de provedor documento é apoiar fontes arbitrárias
j__m
2
O _datanão funcionaria quando o ContentProvider não o suporta. Recomenda-se seguir as instruções do @CommonsWare e não usar mais o caminho completo do arquivo, pois pode ser um arquivo na nuvem do Dropbox em vez de um arquivo real.
soshial
67

Teve o mesmo problema, tentei a solução acima, mas, embora funcionasse geralmente, por algum motivo eu estava obtendo negação de permissão no provedor de conteúdo Uri para algumas imagens, embora eu tivesse a android.permission.MANAGE_DOCUMENTSpermissão adicionada corretamente.

De qualquer forma, encontrou outra solução que é forçar a abertura da galeria de imagens em vez dos documentos do KITKAT visualizados com:

// KITKAT

i = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(i, CHOOSE_IMAGE_REQUEST);

e carregue a imagem:

Uri selectedImageURI = data.getData();
input = c.getContentResolver().openInputStream(selectedImageURI);
                BitmapFactory.decodeStream(input , null, opts);

EDITAR

ACTION_OPEN_DOCUMENT pode exigir que você persista sinalizadores de permissões etc. e geralmente resulta em Exceções de segurança ...

Outra solução é usar o ACTION_GET_CONTENTcombinado com o c.getContentResolver().openInputStream(selectedImageURI)qual funcionará tanto no pré-KK quanto no KK. Kitkat utilizará a nova exibição de documentos e essa solução funcionará com todos os aplicativos como Fotos, Galeria, Explorador de Arquivos, Dropbox, Google Drive, etc ...) mas lembre-se de que ao usar esta solução, é necessário criar uma imagem onActivityResult()e armazená-la no Cartão SD, por exemplo. A recriação dessa imagem do uri salvo no próximo lançamento do aplicativo geraria Exceção de Segurança no resolvedor de conteúdo, mesmo quando você adiciona sinalizadores de permissão, conforme descrito nos documentos da API do Google (foi o que aconteceu quando fiz alguns testes)

Além disso, as Diretrizes da API do desenvolvedor do Android sugerem:

ACTION_OPEN_DOCUMENT não pretende ser um substituto para ACTION_GET_CONTENT. O que você deve usar depende das necessidades do seu aplicativo:

Use ACTION_GET_CONTENT se você quiser que seu aplicativo simplesmente leia / importe dados. Com essa abordagem, o aplicativo importa uma cópia dos dados, como um arquivo de imagem.

Use ACTION_OPEN_DOCUMENT se quiser que seu aplicativo tenha acesso persistente e de longo prazo a documentos pertencentes a um provedor de documentos. Um exemplo seria um aplicativo de edição de fotos que permite aos usuários editar imagens armazenadas em um provedor de documentos.

voytez
fonte
1
Esta resposta continha as informações corretas para meus propósitos. O uso condicional de ACTION_PICK e EXTERNAL_CONTENT_URI no KitKat forneceu a mesma capacidade de obter metadados sobre imagens na galeria via ContentResolver, como é possível em versões mais antigas usando simplesmente ACTION_GET_CONTENT.
Colin M.
@voytez, esse URI retornado através da sua mensagem foi convertido no caminho real completo da imagem?
Cobra
Acredito que sim, ele deve funcionar como antes do KitKat, pois esse código força a abertura da galeria de imagens em vez da exibição de documentos KK. Mas se você pretende usá-lo para criar imagem, essa solução é melhor, pois a conversão para o caminho real é uma etapa desnecessária extra.
voytez
4
Trabalhou para mim também, em vez de Intent.ACTION_GET_CONTENT. De qualquer forma, mantive o Intent.createChooser()invólucro no novo Intent, para permitir que o usuário escolhesse o aplicativo para navegação e trabalhei como esperado. Alguém pode ver os inconvenientes desta solução?
Lorenzo-s
1
Para quem está se perguntando a citação vem de developer.android.com/guide/topics/providers/…
snark
38

Assim como o Commonsware mencionou, você não deve presumir que o fluxo obtido ContentResolveré convertível em arquivo.

O que você realmente deve fazer é abrir a InputStreampartir do ContentProvidere criar um bitmap a partir dele. E funciona também nas versões 4.4 e anteriores, sem necessidade de reflexão.

    //cxt -> current context

    InputStream input;
    Bitmap bmp;
    try {
        input = cxt.getContentResolver().openInputStream(fileUri);
        bmp = BitmapFactory.decodeStream(input);
    } catch (FileNotFoundException e1) {

    }

Obviamente, se você manipular imagens grandes, carregue-as com o seguinte inSampleSize: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html . Mas esse é outro tópico.

Michał K
fonte
Isso não está funcionando para mim com um Nexus 4 executando Kitkat, mas é com um Galaxy S3 executando 4.1.2 #
Dan2552
@ Dan2552 qual parte não está funcionando? Você recebe alguma exceção?
Michał K
Aconteceu que eu estava usando a chamada de intenção errada para a galeria. Eu estava usando um que era para qualquer tipo de documento, mas com um filtro de extensão de arquivo.
Dan2552
2
Que resposta maravilhosamente simples, obrigado! Para outras pessoas que seguem esta resposta, 'cxt' refere-se ao contexto atual e geralmente será 'this'.
thomasforth
1
Isso provavelmente significa que o arquivo simplesmente não está lá. O URI parece bom.
Michał K
33

Acredito que as respostas já postadas devem levar as pessoas na direção certa. No entanto, aqui está o que eu fiz que fazia sentido para o código legado que eu estava atualizando. O código legado estava usando o URI da galeria para alterar e salvar as imagens.

Antes do 4.4 (e do Google Drive), os URIs teriam a seguinte aparência: conteúdo: // media / external / images / media / 41

Conforme declarado na pergunta, eles costumam se parecer com isso: content: //com.android.providers.media.documents/document/image: 3951

Como eu precisava salvar imagens e não perturbar o código já existente, copiei o URI da galeria para a pasta de dados do aplicativo. Em seguida, originou um novo URI do arquivo de imagem salvo na pasta de dados.

Aqui está a ideia:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent), CHOOSE_IMAGE_REQUEST);

public void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);

    File tempFile = new File(this.getFilesDir().getAbsolutePath(), "temp_image");

    //Copy URI contents into temporary file.
    try {
        tempFile.createNewFile();
        copyAndClose(this.getContentResolver().openInputStream(data.getData()),new FileOutputStream(tempFile));
    }
    catch (IOException e) {
        //Log Error
    }

    //Now fetch the new URI
    Uri newUri = Uri.fromFile(tempFile);

    /* Use new URI object just like you used to */
 }

Nota - copyAndClose () apenas arquiva E / S para copiar InputStream em um FileOutputStream. O código não é publicado.

LEO
fonte
bem esperto. i também necessário o URI de arquivo reais
greaterKing
você é meu herói, é exatamente isso que eu precisava! funciona muito bem para arquivos do Google Drive, bem
Mishkin
Pense fora da caixa, certo? : D Este código funciona exatamente como eu esperava.
WeirdElfB0y
2
postar o código para copyAndClose, a resposta não está completa
Bartek Pacia
24

Só queria dizer que essa resposta é brilhante e eu a uso há muito tempo sem problemas. Mas, há algum tempo, deparei com um problema em que o DownloadsProvider retorna URIs no formato content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdfe, portanto, o aplicativo sofre uma falha NumberFormatExceptionporque é impossível analisar seus segmentos de uri por muito tempo. Mas o raw:segmento contém um URI direto que pode ser usado para recuperar um arquivo referenciado. Então, eu o corrigi substituindo o isDownloadsDocument(uri) ifconteúdo pelo seguinte:

final String id = DocumentsContract.getDocumentId(uri);
if (!TextUtils.isEmpty(id)) {
if (id.startsWith("raw:")) {
    return id.replaceFirst("raw:", "");
}
try {
    final Uri contentUri = ContentUris.withAppendedId(
            Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
    return getDataColumn(context, contentUri, null, null);
} catch (NumberFormatException e) {
    Log.e("FileUtils", "Downloads provider returned unexpected uri " + uri.toString(), e);
    return null;
}
}
Bringoff
fonte
2
Funciona perfeito! Obrigado
mikemike396
8

Combinei várias respostas em uma solução funcional que resulta no caminho do arquivo

O tipo MIME é irrelevante para o propósito de exemplo.

            Intent intent;
            if(Build.VERSION.SDK_INT >= 19){
                intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
                intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
            }else{
                intent = new Intent(Intent.ACTION_GET_CONTENT);
            }
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setType("application/octet-stream");
            if(isAdded()){
                startActivityForResult(intent, RESULT_CODE);
            }

Resultado de manipulação

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if( requestCode == RESULT_CODE && resultCode == Activity.RESULT_OK) {
        Uri uri = data.getData();
        if (uri != null && !uri.toString().isEmpty()) {
            if(Build.VERSION.SDK_INT >= 19){
                final int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION;
                //noinspection ResourceType
                getActivity().getContentResolver()
                        .takePersistableUriPermission(uri, takeFlags);
            }
            String filePath = FilePickUtils.getSmartFilePath(getActivity(), uri);
            // do what you need with it...
        }
    }
}

FilePickUtils

import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

public class FilePickUtils {
    private static String getPathDeprecated(Context ctx, Uri uri) {
        if( uri == null ) {
            return null;
        }
        String[] projection = { MediaStore.Images.Media.DATA };
        Cursor cursor = ctx.getContentResolver().query(uri, projection, null, null, null);
        if( cursor != null ){
            int column_index = cursor
                    .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            return cursor.getString(column_index);
        }
        return uri.getPath();
    }

    public static String getSmartFilePath(Context ctx, Uri uri){

        if (Build.VERSION.SDK_INT < 19) {
            return getPathDeprecated(ctx, uri);
        }
        return  FilePickUtils.getPath(ctx, uri);
    }

    @SuppressLint("NewApi")
    public static String getPath(final Context context, final Uri uri) {
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }

                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[] {
                        split[1]
                };

                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }


    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

}
Grzegorz Pawełczuk
fonte
eu estava enfrentando problema .... uri.getPath () estava retornando uri com / external e não estava retornando caminho. i adicionado este bloco else if ( "conteúdo" .equalsIgnoreCase (uri.getScheme ())) e esta boa trabalha agora .. u pode explicar o que ele faz
FaisalAhmed
filePath está ficando nula .. Em uri: conteúdo: //com.android.externalstorage.documents/document/799B-1419%3AScreenshot%2FScreenshot_20181117_162826.png
Bhavesh Moradiya
7

Questão

Como obter um caminho de arquivo real de um URI

Responda

Que eu saiba, não precisamos obter o caminho do arquivo de um URI porque, na maioria dos casos, podemos usar diretamente o URI para realizar nosso trabalho (como 1. obter bitmap 2. Enviar um arquivo para o servidor etc.) .)

1. Enviando para o servidor

Podemos enviar o arquivo diretamente para o servidor usando apenas o URI.

Usando o URI, podemos obter o InputStream, que podemos enviar diretamente para o servidor usando o MultiPartEntity.

Exemplo

/**
 * Used to form Multi Entity for a URI (URI pointing to some file, which we got from other application).
 *
 * @param uri     URI.
 * @param context Context.
 * @return Multi Part Entity.
 */
public MultipartEntity formMultiPartEntityForUri(final Uri uri, final Context context) {
    MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8"));
    try {
        InputStream inputStream = mContext.getContentResolver().openInputStream(uri);
        if (inputStream != null) {
            ContentBody contentBody = new InputStreamBody(inputStream, getFileNameFromUri(uri, context));
            multipartEntity.addPart("[YOUR_KEY]", contentBody);
        }
    }
    catch (Exception exp) {
        Log.e("TAG", exp.getMessage());
    }
    return multipartEntity;
}

/**
 * Used to get a file name from a URI.
 *
 * @param uri     URI.
 * @param context Context.
 * @return File name from URI.
 */
public String getFileNameFromUri(final Uri uri, final Context context) {

    String fileName = null;
    if (uri != null) {
        // Get file name.
        // File Scheme.
        if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
            File file = new File(uri.getPath());
            fileName = file.getName();
        }
        // Content Scheme.
        else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
            Cursor returnCursor =
                    context.getContentResolver().query(uri, null, null, null, null);
            if (returnCursor != null && returnCursor.moveToFirst()) {
                int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                fileName = returnCursor.getString(nameIndex);
                returnCursor.close();
            }
        }
    }
    return fileName;
}

2. Obtendo um BitMap de um URI

Se o URI estiver apontando para a imagem, obteremos bitmap, caso contrário nulo:

/**
 * Used to create bitmap for the given URI.
 * <p>
 * 1. Convert the given URI to bitmap.
 * 2. Calculate ratio (depending on bitmap size) on how much we need to subSample the original bitmap.
 * 3. Create bitmap bitmap depending on the ration from URI.
 * 4. Reference - http://stackoverflow.com/questions/3879992/how-to-get-bitmap-from-an-uri
 *
 * @param context       Context.
 * @param uri           URI to the file.
 * @param bitmapSize Bitmap size required in PX.
 * @return Bitmap bitmap created for the given URI.
 * @throws IOException
 */
public static Bitmap createBitmapFromUri(final Context context, Uri uri, final int bitmapSize) throws IOException {

    // 1. Convert the given URI to bitmap.
    InputStream input = context.getContentResolver().openInputStream(uri);
    BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
    onlyBoundsOptions.inJustDecodeBounds = true;
    onlyBoundsOptions.inDither = true;//optional
    onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
    BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
    input.close();
    if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) {
        return null;
    }

    // 2. Calculate ratio.
    int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;
    double ratio = (originalSize > bitmapSize) ? (originalSize / bitmapSize) : 1.0;

    // 3. Create bitmap.
    BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
    bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
    bitmapOptions.inDither = true;//optional
    bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
    input = context.getContentResolver().openInputStream(uri);
    Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
    input.close();

    return bitmap;
}

/**
 * For Bitmap option inSampleSize - We need to give value in power of two.
 *
 * @param ratio Ratio to be rounded of to power of two.
 * @return Ratio rounded of to nearest power of two.
 */
private static int getPowerOfTwoForSampleRatio(final double ratio) {
    int k = Integer.highestOneBit((int) Math.floor(ratio));
    if (k == 0) return 1;
    else return k;
}

Comentários

  1. O Android não fornece métodos para obter o caminho do arquivo a partir de um URI e, na maioria das respostas acima, codificamos algumas constantes, o que pode resultar na liberação do recurso (desculpe, posso estar errado).
  2. Antes de ir diretamente para uma solução do caminho de obtenção de arquivo de um URI, tente se você pode resolver seu caso de uso com um método padrão de URI e Android.

Referência

  1. https://developer.android.com/guide/topics/providers/content-provider-basics.html
  2. https://developer.android.com/reference/android/content/ContentResolver.html
  3. https://hc.apache.org/httpcomponents-client-ga/httpmime/apidocs/org/apache/http/entity/mime/content/InputStreamBody.html
Vasanth
fonte
Obrigado. Usar o Uri e o ContentResolver dessa maneira simplificou significativamente meu aplicativo ao lidar com arquivos.
Jackonakj # 20/16
3

Para aqueles que ainda estão usando o código de @Paul Burke com o Android SDK versão 23 e superior, se o seu projeto encontrou o erro dizendo que está faltando EXTERNAL_PERMISSION e você tem certeza de que já adicionou permissão de usuário no arquivo AndroidManifest.xml. Isso ocorre na API 23 do Android ou superior e o Google torna necessário garantir a permissão novamente enquanto você executa a ação para acessar o arquivo em tempo de execução.

Isso significa: Se a sua versão do SDK for 23 ou superior, você precisará da permissão READ & WRITE enquanto seleciona o arquivo de imagem e deseja saber o URI dele.

E a seguir está o meu código, além da solução de Paul Burke. Eu adiciono esse código e meu projeto começa a funcionar bem.

private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static final String[] PERMISSINOS_STORAGE = {
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.WRITE_EXTERNAL_STORAGE
};

public static void verifyStoragePermissions(Activity activity) {
    int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);

    if (permission != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(
                activity,
                PERMISSINOS_STORAGE,
                REQUEST_EXTERNAL_STORAGE
        );
    }
}

E em sua atividade e fragmento em que você está solicitando o URI:

private void pickPhotoFromGallery() {

    CompatUtils.verifyStoragePermissions(this);
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("image/*");
    // startActivityForResult(intent, REQUEST_PHOTO_LIBRARY);
    startActivityForResult(Intent.createChooser(intent, "选择照片"),
            REQUEST_PHOTO_LIBRARY);
}

No meu caso, CompatUtils.java é onde eu defino o método confirmStoragePermissions (como tipo estático, para que eu possa chamá-lo em outra atividade).

Além disso, deve fazer mais sentido se você fizer um estado if primeiro para ver se a versão atual do SDK está acima de 23 ou não antes de chamar o método confirmStoragePermissions.

Anthonyeef
fonte
2

Isto é o que eu faço:

Uri selectedImageURI = data.getData();    imageFile = new File(getRealPathFromURI(selectedImageURI)); 

private String getRealPathFromURI(Uri contentURI) {
  Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
  if (cursor == null) { // Source is Dropbox or other similar local file path
      return contentURI.getPath();
      } else { 
      cursor.moveToFirst(); 
      int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); 
      return cursor.getString(idx); 
  }
}

NOTA: o managedQuery()método está obsoleto, por isso não o estou usando.

Esta resposta é de m3n0R na pergunta android get real path by Uri.getPath () e não reivindico crédito. Eu apenas pensei que pessoas que ainda não haviam resolvido esse problema poderiam usá-lo.

Isaac Zais
fonte
2
Esta não é uma resposta para o novo aplicativo Gallery (estritamente "Media Documents Provider") no KitKat.
usar o seguinte comando
O aplicativo que o interlocutor chama de "Galeria" é provavelmente um novo seletor de arquivos no kitkat. FYI: addictivetips.com/android/...
nagoya0
Eu faço semelhante e tenho IndexOutOfBound no Nexus 5X, Android 6 nesta linha:cursor.getString(idx);
Osify
1

Tente evitar o uso do método takePersistableUriPermission, pois isso gerou uma exceção de tempo de execução para mim. / ** * Selecione da galeria. * /

public void selectFromGallery() {
    if (Build.VERSION.SDK_INT < AppConstants.KITKAT_API_VERSION) {

        Intent intent = new Intent(); 
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        ((Activity)mCalledContext).startActivityForResult(intent,AppConstants.GALLERY_INTENT_CALLED);

    } else {

        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("image/*");
        ((Activity)mCalledContext).startActivityForResult(intent, AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED);
    }
}

OnActivity para resultado para manipular os dados da imagem:

@Override void protegido onActivityResult (int requestCode, int resultCode, Intent data) {

    //gallery intent result handling before kit-kat version
    if(requestCode==AppConstants.GALLERY_INTENT_CALLED 
            && resultCode == RESULT_OK) {

        Uri selectedImage = data.getData();
        String[] filePathColumn = {MediaStore.Images.Media.DATA};
        Cursor cursor = getContentResolver().query(selectedImage,filePathColumn, null, null, null);
        cursor.moveToFirst();
        int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
        String filePath = cursor.getString(columnIndex);
        cursor.close();
        photoFile = new File(filePath);
        mImgCropping.startCropImage(photoFile,AppConstants.REQUEST_IMAGE_CROP);

    }
    //gallery intent result handling after kit-kat version
    else if (requestCode == AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED 
            && resultCode == RESULT_OK) {

        Uri selectedImage = data.getData();
        InputStream input = null;
        OutputStream output = null;

        try {
            //converting the input stream into file to crop the 
            //selected image from sd-card.
            input = getApplicationContext().getContentResolver().openInputStream(selectedImage);
            try {
                photoFile = mImgCropping.createImageFile();
            } catch (IOException e) {
                e.printStackTrace();
            }catch(Exception e) {
                e.printStackTrace();
            }
            output = new FileOutputStream(photoFile);

            int read = 0;
            byte[] bytes = new byte[1024];

            while ((read = input.read(bytes)) != -1) {
                try {
                    output.write(bytes, 0, read);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
saranya
fonte
Onde você está exibindo a imagem no segundo caso?
Quantum_VC
desculpe, eu falhei ao adicionar esta linha de código caso contrário mImgCropping.startCropImage (photoFile, AppConstants.REQUEST_IMAGE_CROP); novamente precisa chamar a função imagecropping como por minha necessidade de projeto
saranya
Que tipo de arquivo é photoFile e mImgCropping?
Philip BH
1

Se alguém estiver interessado, eu fiz uma versão funcional do Kotlin para ACTION_GET_CONTENT:

var path: String = uri.path // uri = any content Uri
val databaseUri: Uri
val selection: String?
val selectionArgs: Array<String>?
if (path.contains("/document/image:")) { // files selected from "Documents"
    databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    selection = "_id=?"
    selectionArgs = arrayOf(DocumentsContract.getDocumentId(uri).split(":")[1])
} else { // files selected from all other sources, especially on Samsung devices
    databaseUri = uri
    selection = null
    selectionArgs = null
}
try {
    val projection = arrayOf(MediaStore.Images.Media.DATA,
        MediaStore.Images.Media._ID,
        MediaStore.Images.Media.ORIENTATION,
        MediaStore.Images.Media.DATE_TAKEN) // some example data you can query
    val cursor = context.contentResolver.query(databaseUri,
        projection, selection, selectionArgs, null)
    if (cursor.moveToFirst()) {
        // do whatever you like with the data
    }
    cursor.close()
} catch (e: Exception) {
    Log.e(TAG, e.message, e)
}
0101100101
fonte
Eu só quero um código de trabalho do kotlin. É trabalho para mim. Obrigado
Harvi Sirja
1

Eu tentei várias respostas aqui e acho que tenho uma solução que funcionará sempre e gerencia permissões também.

Baseia-se na solução inteligente da LEO. Esta postagem deve conter todo o código necessário para que isso funcione e em qualquer telefone e versão do Android;)

Para poder escolher um arquivo de um cartão SD, você precisará disso em seu manifesto:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Constantes:

private static final int PICK_IMAGE = 456; // Whatever number you like
public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL = 28528; // Whatever number you like
public static final String FILE_TEMP_NAME = "temp_image"; // Whatever file name you like

Verifique a permissão e launchImagePick, se possível

if (ContextCompat.checkSelfPermission(getThis(),
        Manifest.permission.READ_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {

    ActivityCompat.requestPermissions(getThis(),
            new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
            MY_PERMISSIONS_REQUEST_READ_EXTERNAL);
}
else {
    launchImagePick();
}

Resposta de permissão

@Override
public void onRequestPermissionsResult(int requestCode,
                                       @NonNull
                                         String permissions[],
                                       @NonNull
                                         int[] grantResults) {

    if (manageReadExternalPermissionResponse(this, requestCode, grantResults)) {
        launchImagePick();
    }
}

Gerenciar resposta de permissão

public static boolean manageReadExternalPermissionResponse(final Activity activity, int requestCode, int[] grantResults) {

    if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL) {

        // If request is cancelled, the result arrays are empty.

        if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

            // Permission was granted, yay! Do the
            // contacts-related task you need to do.
            return true;

        } else if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) {

            boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity,
                    Manifest.permission.READ_EXTERNAL_STORAGE);

            if (!showRationale) {
                // The user also CHECKED "never ask again".
                // You can either enable some fall back,
                // disable features of your app
                // or open another dialog explaining
                // again the permission and directing to
                // the app setting.

            } else {
                // The user did NOT check "never ask again".
                // This is a good place to explain the user
                // why you need the permission and ask if he/she wants
                // to accept it (the rationale).
            }
        } else {
            // Permission denied, boo! Disable the
            // functionality that depends on this permission.
        }
    }
    return false;
}

Iniciar seleção de imagem

private void launchImagePick() {

    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("image/*");
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    startActivityForResult(intent, PICK_IMAGE);

    // see onActivityResult
}

Gerenciar resposta de seleção de imagem

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICK_IMAGE) {

        if (resultCode == Activity.RESULT_OK) {
            if (data != null && data.getData() != null) {

                try {
                     InputStream inputStream = getContentResolver().openInputStream(data.getData())
                     if (inputStream != null) {

                        // No special persmission needed to store the file like that
                        FileOutputStream fos = openFileOutput(FILE_TEMP_NAME, Context.MODE_PRIVATE);

                        final int BUFFER_SIZE = 1 << 10 << 3; // 8 KiB buffer
                        byte[] buffer = new byte[BUFFER_SIZE];
                        int bytesRead = -1;
                        while ((bytesRead = inputStream.read(buffer)) > -1) {
                            fos.write(buffer, 0, bytesRead);
                        }
                        inputStream.close();
                        fos.close();

                        File tempImageFile = new File(getFilesDir()+"/"+FILE_TEMP_NAME);

                        // Do whatever you want with the File

                        // Delete when not needed anymore
                        deleteFile(FILE_TEMP_NAME);
                    }
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                // Error display
            }
        } else {
            // The user did not select any image
        }
    }
}

Isso é tudo, pessoal; isso funciona para mim em todos os telefones que tenho.

Quentin G.
fonte
0

Este é um truque total, mas aqui está o que eu fiz ...

Portanto, durante a configuração do DocumentsProvider , observei que o código de exemplo (na getDocIdForFilelinha 450) gera um ID exclusivo para um documento selecionado com base no caminho (exclusivo) do arquivo em relação à raiz especificada (ou seja, o que você definiu mBaseDirna linha 96).

Portanto, o URI acaba parecendo algo como:

content://com.example.provider/document/root:path/to/the/file

Como dizem os documentos, ele assume apenas uma única raiz (no meu caso, Environment.getExternalStorageDirectory()mas você pode usar em outro lugar ... então ele pega o caminho do arquivo, começando na raiz, e o torna o ID exclusivo, precedendo " root:". Então eu pode determinar o caminho eliminando a "/document/root:"parte" de uri.getPath (), criando um caminho de arquivo real fazendo algo como isto:

public void onActivityResult(int requestCode, int resultCode, Intent data) {
// check resultcodes and such, then...
uri = data.getData();
if (uri.getAuthority().equals("com.example.provider"))  {
    String path = Environment.getExternalStorageDirectory(0.toString()
                 .concat("/")
                 .concat(uri.getPath().substring("/document/root:".length())));
    doSomethingWithThePath(path); }
else {
    // another provider (maybe a cloud-based service such as GDrive)
    // created this uri.  So handle it, or don't.  You can allow specific
    // local filesystem providers, filter non-filesystem path results, etc.
}

Eu sei. É vergonhoso, mas funcionou. Novamente, isso depende de você usar seu próprio provedor de documentos no seu aplicativo para gerar a ID do documento.

(Além disso, há uma maneira melhor de construir o caminho que não assume "/" é o separador de caminhos etc. Mas você entendeu.)

fattire
fonte
Para responder a mim mesmo com um pensamento ainda mais louco - se seu aplicativo já estiver lidando com file://intenções de um seletor de arquivos externo, você também poderá verificar a autoridade, como no exemplo acima, para garantir que seja do seu provedor personalizado e, se for o caso, poderá use também o caminho para "forjar" uma nova file://intenção usando o caminho que você extraiu StartActivity()e deixe seu aplicativo buscá-lo. Eu sei, terrível.
fattire
0

Isso funcionou bem pra mim:

else if(requestCode == GALLERY_ACTIVITY_NEW && resultCode == Activity.RESULT_OK)
{
    Uri uri = data.getData();
    Log.i(TAG, "old uri =  " + uri);
    dumpImageMetaData(uri);

    try {
        ParcelFileDescriptor parcelFileDescriptor =
                getContentResolver().openFileDescriptor(uri, "r");
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Log.i(TAG, "File descriptor " + fileDescriptor.toString());

        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);

        options.inSampleSize =
           BitmapHelper.calculateInSampleSize(options,
                                              User.PICTURE_MAX_WIDTH_IN_PIXELS,
                                              User.PICTURE_MAX_HEIGHT_IN_PIXELS);
        options.inJustDecodeBounds = false;

        Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
        imageViewPic.setImageBitmap(bitmap);

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
        // get byte array here
        byte[] picData = stream.toByteArray();
        ParseFile picFile = new ParseFile(picData);
        user.setProfilePicture(picFile);
    }
    catch(FileNotFoundException exc)
    {
        Log.i(TAG, "File not found: " + exc.toString());
    }
}
Rafa
fonte
esqueça o dumpImageMetaData (uri); é desnecessário
Rafa
0

Com base na resposta de Paul Burke, enfrentei muitos problemas ao resolver o caminho URI do cartão SD externo, pois a maioria das funções "internas" sugeridas retornam caminhos que não são resolvidos nos arquivos.

No entanto, esta é a minha abordagem de seu // TODO manipular volumes não primários .

String resolvedPath = "";
File[] possibleExtSdComposites = context.getExternalFilesDirs(null);
for (File f : possibleExtSdComposites) {
    // Reset final path
    resolvedPath = "";

    // Construct list of folders
    ArrayList<String> extSdSplit = new ArrayList<>(Arrays.asList(f.getPath().split("/")));

    // Look for folder "<your_application_id>"
    int idx = extSdSplit.indexOf(BuildConfig.APPLICATION_ID);

    // ASSUMPTION: Expected to be found at depth 2 (in this case ExtSdCard's root is /storage/0000-0000/) - e.g. /storage/0000-0000/Android/data/<your_application_id>/files
    ArrayList<String> hierarchyList = new ArrayList<>(extSdSplit.subList(0, idx - 2));

    // Construct list containing full possible path to the file
    hierarchyList.add(tail);
    String possibleFilePath = TextUtils.join("/", hierarchyList);

    // If file is found --> success
    if (idx != -1 && new File(possibleFilePath).exists()) {
        resolvedPath = possibleFilePath;
        break;
    }
}

if (!resolvedPath.equals("")) {
    return resolvedPath;
} else {
    return null;
}

Observe que depende da hierarquia que pode ser diferente em todos os fabricantes de telefones - ainda não os testei (funcionou até agora no Xperia Z3 API 23 e no Samsung Galaxy A3 API 23).

Confirme se não apresenta um bom desempenho em outro local.

Evusas
fonte
-1

A resposta de @paul burke funciona bem para as fotos da câmera e da galeria para o nível 19 da API e acima, mas não funciona se o SDK mínimo do seu projeto Android estiver definido como abaixo de 19 e algumas respostas acima não funcionarem para a galeria e Câmera. Bem, eu modifiquei o código do @paul burke, que funciona para o nível da API abaixo de 19. Abaixo está o código.

public static String getPath(final Context context, final Uri uri) {

    final boolean isKitKat = Build.VERSION.SDK_INT >=
                             Build.VERSION_CODES.KITKAT;
    Log.i("URI",uri+"");
    String result = uri+"";

    // DocumentProvider
    // if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
    if (isKitKat && (result.contains("media.documents"))) {

        String[] ary = result.split("/");
        int length = ary.length;
        String imgary = ary[length-1];
        final String[] dat = imgary.split("%3A");

        final String docId = dat[1];
        final String type = dat[0];

        Uri contentUri = null;
        if ("image".equals(type)) {
            contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        }
        else if ("video".equals(type)) {
        }
        else if ("audio".equals(type)) {
        }

        final String selection = "_id=?";
        final String[] selectionArgs = new String[] {
            dat[1]
        };

        return getDataColumn(context, contentUri, selection, selectionArgs);
    }
    else
    if ("content".equalsIgnoreCase(uri.getScheme())) {
        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;
}

public static String getDataColumn(Context context, Uri uri, String selection,
                                   String[] selectionArgs) {
    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = {
            column
    };

    try {
        cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                null);
        if (cursor != null && cursor.moveToFirst()) {
            final int column_index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(column_index);
        }
    }
    finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}
parvez rafi
fonte
I receber java.lang.IllegalArgumentException: Nenhuma das colunas solicitadas podem ser fornecidas ao selecionar uma imagem Google Doc
dirkoneill
@dirkoneill Estou recebendo a mesma exceção. Você encontrou uma solução?
Henry Henry
-4

A resposta para sua pergunta é que você precisa ter permissões. Digite o seguinte código no seu arquivo manifest.xml:

<uses-sdk  android:minSdkVersion="8"   android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_OWNER_DATA"></uses-permission>
<uses-permission android:name="android.permission.READ_OWNER_DATA"></uses-permission>`

Funcionou para mim ...

ashwin
fonte