Android Gallery auf Android 4.4 (KitKat) gibt einen anderen URI für Intent.ACTION_GET_CONTENT zurück

Lesezeit: 17 Minuten

Android Gallery auf Android 44 KitKat gibt einen anderen URI
Michael Greifeneder

Vor KitKat (oder vor der neuen Galerie) die Intent.ACTION_GET_CONTENT gab einen URI wie diesen zurück

content://media/external/images/media/3951.

Verwendung der ContentResolver und frage nach
MediaStore.Images.Media.DATA hat die Datei-URL zurückgegeben.

In KitKat gibt die Galerie jedoch einen URI (über “Last”) wie folgt zurück:

content://com.android.providers.media.documents/document/image:3951

Wie gehe ich damit um?

  • Aus dem Stegreif würde ich Wege finden, die Inhalte zu nutzen, die keinen direkten Zugriff auf die Datei erfordern. Zum Beispiel das Uri sollte als Stream über geöffnet werden können ContentResolver. Ich bin schon lange nervös bei Apps, die davon ausgehen, dass a content:// Uri die eine Datei darstellt, kann immer in eine konvertiert werden File.

    – CommonsWare

    7. November 2013 um 12:31 Uhr

  • @CommonsWare,Wenn ich einen Bildpfad in sqlite db speichern möchte, damit ich ihn später öffnen kann, soll ich den URI oder den absoluten Dateipfad speichern?

    – Schlange

    19. Dezember 2013 um 20:45 Uhr

  • @CommonsWare Ich stimme Ihrer Nervosität zu. 🙂 Allerdings muss ich in der Lage sein, einen Dateinamen (für ein Bild) an nativen Code zu übergeben. Eine Lösung besteht darin, die erhaltenen Daten mit einem zu kopieren InputStream auf der ContentResolver an einen vorher festgelegten Ort, damit es einen bekannten Dateinamen hat. Allerdings klingt das für mich verschwenderisch. Irgendwelche anderen Vorschläge?

    – darrenp

    15. Januar 2014 um 17:22 Uhr


  • @darrenp: Ummm …, schreiben Sie den nativen Code neu, um mit einer zu arbeiten InputStream über JNI? Leider gibt es nicht allzu viele Möglichkeiten für Sie.

    – CommonsWare

    15. Januar 2014 um 17:34 Uhr

  • Das ist nützlich zu wissen. Vielen Dank für Ihre Antwort. Ich habe seitdem herausgefunden, dass wir das Bild jetzt im Speicher und nicht über eine Datei an C++ übergeben, sodass wir es jetzt verwenden können InputStream anstelle einer Datei (was großartig ist). Nur das Lesen von EXIF-Tags ist etwas schwierig und erfordert Die Bibliothek von Drew Noakes. Vielen Dank für Ihre Kommentare.

    – darrenp

    15. Januar 2014 um 20:49 Uhr


1646631796 592 Android Gallery auf Android 44 KitKat gibt einen anderen URI
Paul Burke

Dies erfordert keine besonderen Berechtigungen und funktioniert mit dem Storage Access Framework sowie dem inoffiziellen ContentProvider Muster (Dateipfad in _data Bereich).

/**
 * 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() + "https://stackoverflow.com/" + 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());
}

Sehen Sie sich eine aktuelle Version dieser Methode an Hier.

  • Dies funktionierte fantastisch auf einer 4.4 Nexus 5 Documents-Benutzeroberfläche und einigen anderen bevorzugten KitKat-Geräten mit den Standard-Galerie-Apps, danke Paul!

    – Josch

    14. Dezember 2013 um 12:44 Uhr

  • Danke dafür, es hat ewig gedauert, bis ich mit SDK 19 so weit gekommen bin!! Mein Problem ist, dass mein Gerät Google Drive als Dateibrowser verwendet. Wenn sich die Datei auf dem Geräte-Image-Pfad befindet, ist dies in Ordnung, aber wenn sich die Datei auf dem Laufwerk befindet, wird sie nicht geöffnet. Vielleicht muss ich mich nur mit dem Öffnen von Bildern von Google Drive befassen. Die Sache ist, dass meine App so geschrieben ist, dass sie den Dateipfad verwendet und Bilder mit Insampling erhält …

    – RuAware

    17. Dezember 2013 um 21:18 Uhr


  • @RuAware Wenn Sie eine Drive-Datei auswählen, gibt sie zurück Authority: com.google.android.apps.docs.storage und Segments: [document, acc=1;doc=667]. Ich bin mir nicht sicher, gehe aber davon aus, dass die doc Wert ist der Uri ID, die Sie abfragen können. Sie benötigen wahrscheinlich Berechtigungen, um wie unter “Autorisieren Sie Ihre App auf Android” hier beschrieben eingerichtet zu werden: developer.google.com/drive/integrate-android-ui. Bitte aktualisieren Sie hier, wenn Sie es herausfinden.

    – Paul Burke

    18. Dezember 2013 um 15:21 Uhr

  • das ist absolut schrecklich! Sie sollten nicht weiterhin Code verbreiten, der auf diese Weise “betrügt”. Es unterstützt nur die Quell-Apps, für die Sie das Muster kennen, und der Sinn des Dokumentanbietermodells besteht darin, beliebige Quellen zu unterstützen

    – j__m

    29. September 2014 um 20:01 Uhr

  • Die _data würde nicht funktionieren, wenn ContentProvider es nicht unterstützt. Es wird empfohlen, zu folgen @CommonsWare-Anweisungen und verwenden Sie nicht mehr den vollständigen Dateipfad, da es sich um eine Datei in der Dropbox-Cloud anstelle einer echten Datei handeln könnte.

    – soshial

    27. Dezember 2016 um 15:10 Uhr

Versuche dies:

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);

}

Wahrscheinlich brauchen

@SuppressLint(“NeueApi”)

zum

takePersistableUriPermission

  • Würde es Ihnen etwas ausmachen, näher darauf einzugehen, was der KitKat-Code macht? Braucht es dafür eine neue Genehmigung? Der Pre-KitKat-Code funktioniert bei mir auch auf KitKat. Warum sollte ich also einen anderen Code für KitKat verwenden? Danke.

    – Michael Greifeneder

    9. November 2013 um 18:19 Uhr


  • Es scheint, dass wir den Pfad von neuen SDKS-URI nicht erhalten können. Es ist auch eine Schande, dass Google diese Art von Änderung ohne ordnungsgemäße Dokumentation und Ankündigung vorgenommen hat.

    – Benutzer65721

    17. November 2013 um 3:11 Uhr


  • Können Sie erklären, wie Sie die Datei-URL erhalten? Ich möchte den tatsächlichen Pfad in der SD-Karte erhalten. Wenn es sich beispielsweise um ein Bild handelt, möchte ich diesen Pfad /storage/sdcard0/DCIM/Camera/IMG_20131118_153817_119.jpg anstelle des Dokuments Uri erhalten.

    – Alvaro

    20. November 2013 um 8:34 Uhr

  • Basierend auf KitKat-Dokumentation (developer.android.com/about/versions/…) ist dies möglicherweise nicht das, was das OP benötigt, es sei denn, er beabsichtigt, Dokumente zu verwenden/zu bearbeiten, die Eigentum der anderen Anwendung(en) sind. Wenn das OP eine Kopie haben möchte oder Dinge in Übereinstimmung mit älteren Versionen tun möchte, wäre die Antwort von @voytez angemessener.

    – Colin M.

    27. November 2013 um 22:14 Uhr

  • Das funktioniert bei mir nicht. Ich erhalte folgende Ausnahme (auf Lager 4.4.2): E/AndroidRuntime(29204): Caused by: java.lang.SecurityException: Requested flags 0x1, but only 0x0 are allow

    – Russel Stewart

    11. Februar 2014 um 18:34 Uhr

Android Gallery auf Android 44 KitKat gibt einen anderen URI
Voytez

Hatte das gleiche Problem, habe die obige Lösung ausprobiert, aber obwohl es im Allgemeinen funktionierte, erhielt ich aus irgendeinem Grund die Erlaubnisverweigerung für den Uri-Inhaltsanbieter für einige Bilder, obwohl ich die hatte android.permission.MANAGE_DOCUMENTS Berechtigung richtig hinzugefügt.

Wie auch immer, ich habe eine andere Lösung gefunden, die das Öffnen der Bildergalerie anstelle der Ansicht der KITKAT-Dokumente mit erzwingen soll:

// KITKAT

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

und dann das Bild laden:

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

BEARBEITEN

ACTION_OPEN_DOCUMENT Möglicherweise müssen Sie Berechtigungsflags usw. beibehalten und führen im Allgemeinen häufig zu Sicherheitsausnahmen …

Eine andere Lösung ist die Verwendung von ACTION_GET_CONTENT kombiniert mit c.getContentResolver().openInputStream(selectedImageURI) die sowohl auf Pre-KK als auch auf KK funktionieren. Kitkat verwendet dann die neue Dokumentenansicht und diese Lösung funktioniert mit allen Apps wie Fotos, Galerie, Datei-Explorer, Dropbox, Google Drive usw.), aber denken Sie daran, dass Sie bei Verwendung dieser Lösung ein Bild in Ihrem erstellen müssen onActivityResult() und speichern Sie es beispielsweise auf einer SD-Karte. Das Neuerstellen dieses Bildes aus gespeicherten URI beim nächsten App-Start würde eine Sicherheitsausnahme für den Content-Resolver auslösen, selbst wenn Sie Berechtigungs-Flags hinzufügen, wie in der Google API-Dokumentation beschrieben (das ist passiert, als ich einige Tests durchgeführt habe).

Zusätzlich empfehlen die Android Developer API Guidelines:

ACTION_OPEN_DOCUMENT ist nicht als Ersatz für ACTION_GET_CONTENT gedacht. Welche Sie verwenden sollten, hängt von den Anforderungen Ihrer App ab:

Verwenden Sie ACTION_GET_CONTENT, wenn Ihre App Daten einfach lesen/importieren soll. Bei diesem Ansatz importiert die App eine Kopie der Daten, beispielsweise eine Bilddatei.

Verwenden Sie ACTION_OPEN_DOCUMENT, wenn Sie möchten, dass Ihre App langfristigen, dauerhaften Zugriff auf Dokumente hat, die einem Dokumentanbieter gehören. Ein Beispiel wäre eine Fotobearbeitungs-App, mit der Benutzer Bilder bearbeiten können, die in einem Dokumentanbieter gespeichert sind.

  • Diese Antwort enthielt die richtigen Informationen für meine Zwecke. Die bedingte Verwendung von ACTION_PICK und EXTERNAL_CONTENT_URI auf KitKat bot die gleiche Möglichkeit, Metadaten zu Bildern in der Galerie über ContentResolver abzurufen, wie es bei älteren Versionen möglich ist, indem einfach ACTION_GET_CONTENT verwendet wird.

    – Colin M.

    27. November 2013 um 22:16 Uhr

  • @voytez, kann dieser durch Ihre Nachricht zurückgegebene URI in den vollständigen realen Pfad des Bildes konvertiert werden?

    – Schlange

    19. Dezember 2013 um 21:45 Uhr

  • Ich glaube schon, es sollte genauso funktionieren wie vor KitKat, da dieser Code das Öffnen einer Bildergalerie anstelle der Ansicht von KK-Dokumenten erzwingt. Aber wenn Sie beabsichtigen, es zum Erstellen von Bildern zu verwenden, ist diese Lösung besser, da das Konvertieren in einen echten Pfad ein unnötiger zusätzlicher Schritt ist.

    – Voytez

    13. Januar 2014 um 13:23 Uhr


  • Hat bei mir auch funktioniert, statt Intent.ACTION_GET_CONTENT. Jedenfalls habe ich die behalten Intent.createChooser() Wrapper auf die neue Intent, damit der Benutzer die App zum Durchsuchen auswählen kann, und funktionierte wie erwartet. Kann jemand Nachteile für diese Lösung sehen?

    – lorenzo-s

    15. April 2014 um 7:30 Uhr

  • Für alle, die sich fragen, woher das Zitat stammt developer.android.com/guide/topics/providers/…

    – Schnarchen

    4. November 2017 um 15:16 Uhr

1646631797 890 Android Gallery auf Android 44 KitKat gibt einen anderen URI
Michal Klimczak

Wie von Commonsware erwähnt, sollten Sie nicht davon ausgehen, dass der Stream, den Sie erhalten, über ContentResolver ist in eine Datei umwandelbar.

Was Sie wirklich tun sollten, ist, die zu öffnen InputStream von dem ContentProvider, dann erstellen Sie daraus eine Bitmap. Und es funktioniert auch auf 4.4 und früheren Versionen, keine Notwendigkeit zum Nachdenken.

    //cxt -> current context

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

    }

Wenn Sie mit großen Bildern arbeiten, sollten Sie diese natürlich mit angemessen laden inSampleSize: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html. Aber das ist ein anderes Thema.

1646631798 528 Android Gallery auf Android 44 KitKat gibt einen anderen URI
LÖWE

Ich glaube, die bereits geposteten Antworten sollten die Leute in die richtige Richtung lenken. Hier ist jedoch, was ich getan habe, was für den Legacy-Code, den ich aktualisiert habe, sinnvoll war. Der alte Code verwendete den URI aus der Galerie, um die Bilder zu ändern und dann zu speichern.

Vor 4.4 (und Google Drive) sahen die URIs so aus:
content://media/extern/images/media/41

Wie in der Frage angegeben, sehen sie häufiger so aus:
content://com.android.providers.media.documents/document/image:3951

Da ich die Möglichkeit brauchte, Bilder zu speichern und den bereits vorhandenen Code nicht zu stören, habe ich einfach die URI aus der Galerie in den Datenordner der App kopiert. Dann entstand eine neue URI aus der gespeicherten Bilddatei im Datenordner.

Hier ist die Idee:

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 */
 }

Hinweis – copyAndClose() führt nur Datei-I/O aus, um InputStream in einen FileOutputStream zu kopieren. Der Code wird nicht gepostet.

  • ziemlich clever. Ich brauchte auch die eigentliche Datei uri

    – Großer König

    15. März 2014 um 20:25 Uhr

  • Du bist mein Held, das ist genau das, was ich brauchte! funktioniert auch hervorragend für Google Drive-Dateien

    – Mischkin

    26. Mai 2015 um 3:04 Uhr

  • Denken Sie über den Tellerrand hinaus, oder? 😀 Dieser Code funktioniert genau so, wie ich es erwartet hatte.

    – Neerkoli

    24. Oktober 2015 um 17:58 Uhr

  • Poste den Code für copyAndClose, die Antwort ist nicht vollständig

    – Bartek Pacia

    25. November 2019 um 20:45 Uhr

Ich wollte nur sagen, dass diese Antwort brillant ist und ich sie seit langem ohne Probleme verwende. Aber vor einiger Zeit bin ich auf ein Problem gestoßen, dass DownloadsProvider URIs im Format zurückgibt content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdf und daher ist die App mit abgestürzt NumberFormatException da es unmöglich ist, seine URI-Segmente so lange zu analysieren. Aber raw: segment enthält direkte URI, die verwendet werden können, um eine referenzierte Datei abzurufen. Also habe ich es durch Austausch behoben isDownloadsDocument(uri) if Inhalt mit folgendem:

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;
}
}

  • ziemlich clever. Ich brauchte auch die eigentliche Datei uri

    – Großer König

    15. März 2014 um 20:25 Uhr

  • Du bist mein Held, das ist genau das, was ich brauchte! funktioniert auch hervorragend für Google Drive-Dateien

    – Mischkin

    26. Mai 2015 um 3:04 Uhr

  • Denken Sie über den Tellerrand hinaus, oder? 😀 Dieser Code funktioniert genau so, wie ich es erwartet hatte.

    – Neerkoli

    24. Oktober 2015 um 17:58 Uhr

  • Poste den Code für copyAndClose, die Antwort ist nicht vollständig

    – Bartek Pacia

    25. November 2019 um 20:45 Uhr

1646631798 730 Android Gallery auf Android 44 KitKat gibt einen anderen URI
Grzegorz Pawelczuk

Ich habe mehrere Antworten zu einer funktionierenden Lösung kombiniert, die mit dem Dateipfad resultiert

Der Mime-Typ ist für den Beispielzweck irrelevant.

            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);
            }

Handhabungsergebnis

    @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() + "https://stackoverflow.com/" + 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());
    }

}

  • Ich hatte ein Problem …. uri.getPath() gab uri mit /external zurück und es gab keinen Pfad zurück. Ich habe diesen Else-if-Block (“content”.equalsIgnoreCase(uri.getScheme())) hinzugefügt und das funktioniert jetzt gut. Kannst du erklären, was es tut?

    – FaisalAhmed

    26. April 2017 um 10:21 Uhr

  • filePath wird null. In uri: content://com.android.externalstorage.documents/document/799B-1419%3AScreenshot%2FScreenshot_20181117_162826.png

    – Bhavesh Moradiya

    7. Dezember 2018 um 9:11 Uhr

963260cookie-checkAndroid Gallery auf Android 4.4 (KitKat) gibt einen anderen URI für Intent.ACTION_GET_CONTENT zurück

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy