Get file path from URI

A simpler version of the accepted answer that will detect the API level and use the correct method :

@TargetApi(Build.VERSION_CODES.KITKAT)
public static String getFilePath(Context context, Uri uri)
{
    int currentApiVersion;
    try
    {
         currentApiVersion = android.os.Build.VERSION.SDK_INT;
    }
    catch(NumberFormatException e)
    {
        //API 3 will crash if SDK_INT is called
        currentApiVersion = 3;
    }
    if (currentApiVersion >= Build.VERSION_CODES.KITKAT)
    {
        String filePath = "";
        String wholeID = DocumentsContract.getDocumentId(uri);

        // Split at colon, use second item in the array
        String id = wholeID.split(":")[1];

        String[] column = {MediaStore.Images.Media.DATA};

        // where id is equal to
        String sel = MediaStore.Images.Media._ID + "=?";

        Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                column, sel, new String[]{id}, null);

        int columnIndex = cursor.getColumnIndex(column[0]);

        if (cursor.moveToFirst())
        {
            filePath = cursor.getString(columnIndex);
        }
        cursor.close();
        return filePath;
    }
    else if (currentApiVersion <= Build.VERSION_CODES.HONEYCOMB_MR2 && currentApiVersion >= Build.VERSION_CODES.HONEYCOMB)

    {
        String[] proj = {MediaStore.Images.Media.DATA};
        String result = null;

        CursorLoader cursorLoader = new CursorLoader(
                context,
                uri, proj, null, null, null);
        Cursor cursor = cursorLoader.loadInBackground();

        if (cursor != null)
        {
            int column_index =
                    cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            result = cursor.getString(column_index);
        }
        return result;
    }
    else
    {

        String[] proj = {MediaStore.Images.Media.DATA};
        Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null);
        int column_index
                = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    }
}

Here's my solution, using all tricks I've found so far, which I use on my own app too (here) :

FileUtilEx.kt

fun Closeable?.closeSilently() {
    if (this != null) try {
        this.close()
    } catch (e: Exception) {
    }
}

object FileUtilEx {
    fun getFilePathFromUri(context: Context, uri: Uri, includeUriMappingTechnique: Boolean = true, tryToGetWritePermission: Boolean = false): ClosableFileHolder? {
        var file: File
        uri.path?.let {
            file = File(it)
            if (file.exists() && file.canRead()) {
//                Log.d("AppLog", "got real file")
                return ClosableFileHolder(file)
            }
        }
        if (uri.scheme == "file") {
            try {
                val jUri = java.net.URI(uri.scheme, uri.schemeSpecificPart, uri.fragment)
                file = File(jUri)
                if (file.exists() && file.canRead()) {
//                    Log.d("AppLog", "got real file")
                    return ClosableFileHolder(file)
                }
            } catch (e: Exception) {
            }
            try {
                val uriStr = uri.toString()
                val decodePath = URLDecoder.decode(uriStr, "UTF-8")
                file = File(decodePath)
                if (file.exists() && file.canRead()) {
//                    Log.d("AppLog", "got real file")
                    return ClosableFileHolder(file)
                }
            } catch (e: UnsupportedEncodingException) {
            }
        }
        val authority = uri.authority
        if (uri.scheme == "content") {
            //handles "Files" app, and many others
            getRealPathFromUri(context, uri)?.let {
                file = File(it)
                if (file.exists() && file.canRead()) {
//                    Log.d("AppLog", "got real file")
                    return ClosableFileHolder(file)
                }
            }
        }
        if (includeUriMappingTechnique) {
            val fileUsingUriMappingTechnique = getFileUsingUriMappingTechnique(context, uri, tryToGetWritePermission)
            if (fileUsingUriMappingTechnique != null)
                return fileUsingUriMappingTechnique
        }
        getFilePathFromDocumentUri(context, uri)?.let { filePath ->
            file = File(filePath)
            if (file.exists() && file.canRead())
                return ClosableFileHolder(file)
        }
        return null
    }


    private fun getRealPathFromUri(context: Context, contentUri: Uri): String? {
        try {
            context.contentResolver.query(contentUri, arrayOf(MediaStore.Images.Media.DATA), null, null, null)?.use { cursor ->
                val columnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA)
                if (columnIndex < 0 || cursor.count <= 0)
                    return null
                cursor.moveToFirst()
                return cursor.getString(columnIndex)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    fun getFileUsingUriMappingTechnique(context: Context, androidUri: Uri, tryToGetWritePermission: Boolean = false): ClosableFileHolder? {
        var parcelFileDescriptor: ParcelFileDescriptor? = null
        for (i in 0..1)
            try {
                parcelFileDescriptor = context.contentResolver.openFileDescriptor(androidUri, if (tryToGetWritePermission && i == 0) "w" else "r")
                if (parcelFileDescriptor != null) {
                    val fd: Int = parcelFileDescriptor.fd
                    val linkFileName = "/proc/self/fd/$fd"
                    if (VERSION.SDK_INT >= VERSION_CODES.O) {
                        val realFilePath = Files.readSymbolicLink(Paths.get(linkFileName))
                        val file = realFilePath.toFile()
                        if (file.exists() && file.canRead()) {
                            parcelFileDescriptor.closeSilently()
                            return ClosableFileHolder(file)
                        }
                    }
                    val file = File(linkFileName)
                    if (file.exists() && file.canRead())
                        return ClosableFileHolder(file, parcelFileDescriptor)
                    parcelFileDescriptor.closeSilently()
                }
            } catch (e: Exception) {
                parcelFileDescriptor.closeSilently()
                parcelFileDescriptor = null
            }
        return null
    }

    private fun getFilePathFromDocumentUri(context: Context, uri: Uri): String? {
        //            https://stackoverflow.com/questions/5657411/android-getting-a-file-uri-from-a-content-uri
        // DocumentProvider
        if (VERSION.SDK_INT >= VERSION_CODES.KITKAT && DocumentFile.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if ("com.android.externalstorage.documents" == uri.authority) {
                val docId = DocumentsContract.getDocumentId(uri)
                val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                val type = split[0]
                // This is for checking Main Memory
                return if ("primary".equals(type, ignoreCase = true)) {
                    if (split.size > 1) {
                        Environment.getExternalStorageDirectory().absolutePath + "/" + split[1] + "/"
                    } else {
                        Environment.getExternalStorageDirectory().absolutePath + "/"
                    }
                    // This is for checking SD Card
                } else {
                    "storage" + "/" + docId.replace(":", "/")
                }
            }
        }
        return null
    }
}

ClosableFileHolder.kt

class ClosableFileHolder(val file: File, private val parcelFileDescriptor: ParcelFileDescriptor? = null) : Closeable {

    fun isUsingUriMappingTechnique(): Boolean = parcelFileDescriptor != null

    override fun close() {
        parcelFileDescriptor.closeSilently()
    }

    protected fun finalize() {
        parcelFileDescriptor.closeSilently()
    }
}

Usage:

FileUtilEx.getFilePathFromUri(context, uri, false)?.use {
    val file = it.file
    ...
}

The tryToGetWritePermission parameter is in case you want to access the file, but it's in fact not available normally (like in SAF), so it's offered you in a different place. This is useful in case you want to access the file but don't care where it really exists.


Solution:

    public class RealPathUtil {

    @SuppressLint("NewApi")
    public static String getRealPathFromURI_API19(Context context, Uri uri){
        String filePath = "";
        String wholeID = DocumentsContract.getDocumentId(uri);

         // Split at colon, use second item in the array
         String id = wholeID.split(":")[1];

         String[] column = { MediaStore.Images.Media.DATA };     

         // where id is equal to             
         String sel = MediaStore.Images.Media._ID + "=?";

         Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
                                   column, sel, new String[]{ id }, null);

         int columnIndex = cursor.getColumnIndex(column[0]);

         if (cursor.moveToFirst()) {
             filePath = cursor.getString(columnIndex);
         }   
         cursor.close();
         return filePath;
    }


    @SuppressLint("NewApi")
    public static String getRealPathFromURI_API11to18(Context context, Uri contentUri) {
          String[] proj = { MediaStore.Images.Media.DATA };
          String result = null;

          CursorLoader cursorLoader = new CursorLoader(
                  context, 
            contentUri, proj, null, null, null);        
          Cursor cursor = cursorLoader.loadInBackground();

          if(cursor != null){
           int column_index = 
             cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
           cursor.moveToFirst();
           result = cursor.getString(column_index);
          }
          return result;  
    }

    public static String getRealPathFromURI_BelowAPI11(Context context, Uri contentUri){
               String[] proj = { MediaStore.Images.Media.DATA };
               Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
               int column_index
          = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
               cursor.moveToFirst();
               return cursor.getString(column_index);
    }
}

http://hmkcode.com/android-display-selected-image-and-its-real-path/