Obtenir le vrai chemin de l'URI, Android KitKat nouveau cadre d'accès au stockage


Answers

Cela obtiendra le chemin du fichier à partir de MediaProvider, DownloadsProvider et ExternalStorageProvider, tout en retombant à la méthode non officielle ContentProvider que vous mentionnez.

/**
 * 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 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());
}

Ceux-ci sont tirés de ma bibliothèque open source, aFileChooser .

Question

Avant le nouvel accès à la galerie sous Android 4.4 (KitKat), j'ai obtenu mon vrai chemin sur la carte SD avec cette méthode:

public String getPath(Uri uri) {
   String[] projection = { MediaStore.Images.Media.DATA };
   Cursor cursor = managedQuery(uri, projection, null, null, null);
   startManagingCursor(cursor);
   int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
   cursor.moveToFirst();
 return cursor.getString(column_index);
}

Maintenant, l'Intent.ACTION_GET_CONTENT renvoie différentes données:

Avant:

content://media/external/images/media/62

À présent:

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

Comment ai-je réussi à obtenir le vrai chemin sur la carte SD?




Voici une version mise à jour de la réponse de Paul Burke . Dans les versions ci-dessous Android 4.4 (KitKat), nous n'avons pas la classe DocumentsContract .

Afin de travailler sur les versions ci-dessous KitKat créer cette classe:

public class DocumentsContract {
    private static final String DOCUMENT_URIS =
        "com.android.providers.media.documents " +
        "com.android.externalstorage.documents " +
        "com.android.providers.downloads.documents " +
        "com.android.providers.media.documents";

    private static final String PATH_DOCUMENT = "document";
    private static final String TAG = DocumentsContract.class.getSimpleName();

    public static String getDocumentId(Uri documentUri) {
        final List<String> paths = documentUri.getPathSegments();
        if (paths.size() < 2) {
            throw new IllegalArgumentException("Not a document: " + documentUri);
        }

        if (!PATH_DOCUMENT.equals(paths.get(0))) {
            throw new IllegalArgumentException("Not a document: " + documentUri);
        }
        return paths.get(1);
    }

    public static boolean isDocumentUri(Uri uri) {
        final List<String> paths = uri.getPathSegments();
        Logger.v(TAG, "paths[" + paths + "]");
        if (paths.size() < 2) {
            return false;
        }
        if (!PATH_DOCUMENT.equals(paths.get(0))) {
            return false;
        }
        return DOCUMENT_URIS.contains(uri.getAuthority());
    }
}



Avant de pouvoir accéder à une nouvelle galerie dans KitKat, j'ai obtenu mon vrai chemin en sdcard avec cette méthode

Ce n'était jamais fiable. Il n'y a aucune exigence que le Uri que vous êtes retourné à partir d'une ACTION_GET_CONTENT ou ACTION_PICK doit être indexé par le MediaStore , ou même doit représenter un fichier sur le système de fichiers. L' Uri pourrait, par exemple, représenter un flux, où un fichier crypté est décrypté pour vous à la volée.

Comment ai-je réussi à obtenir le vrai chemin en sdcard?

Il n'y a aucune exigence qu'il existe un fichier correspondant à l' Uri .

Oui, j'ai vraiment besoin d'un chemin

Puis copiez le fichier du flux dans votre propre fichier temporaire et utilisez-le. Mieux encore, il suffit d'utiliser directement le flux et d'éviter le fichier temporaire.

J'ai changé mon Intent.ACTION_GET_CONTENT pour Intent.ACTION_PICK

Cela n'aidera pas votre situation. Il n'y a aucune exigence qu'une réponse ACTION_PICK soit pour un Uri qui a un fichier sur le système de fichiers que vous pouvez en quelque sorte magiquement dériver.




J'ai eu exactement le même problème. J'ai besoin du nom de fichier pour pouvoir le télécharger sur un site Web.

Cela a fonctionné pour moi, si j'ai changé l'intention de PICK. Cela a été testé dans AVD pour Android 4.4 et dans AVD pour Android 2.1.

Ajouter une autorisation READ_EXTERNAL_STORAGE:

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

Changer l'intention:

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

/* OLD CODE
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
  Intent.createChooser( intent, "Select Image" ),
  66453666
  );
*/

Je n'ai pas eu à changer mon code pour obtenir le chemin réel:

// Convert the image URI to the direct file system path of the image file
 public String mf_szGetRealPathFromURI(final Context context, final Uri ac_Uri )
 {
     String result = "";
     boolean isok = false;

     Cursor cursor = null;
      try { 
        String[] proj = { MediaStore.Images.Media.DATA };
        cursor = context.getContentResolver().query(ac_Uri,  proj, null, null, null);
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        result = cursor.getString(column_index);
        isok = true;
      } finally {
        if (cursor != null) {
          cursor.close();
        }
      }

      return isok ? result : "";
 }



Links