0

I am trying to open file picker and select files from installed applications. In this case I am trying to access scanned file from Adobe Scan. When I choose file I get following uri

content://com.adobe.scan.android.documents/document/root:1

But now I want to get file from this uri which I couldn't. I am using following class to get Real File Path. I dont know what logic I should add here which can access files from third party apps.

object RealFilePath {
fun getFile(context: Context, fileUri: Uri): File {
    return File(getRealPathFromURI(context, fileUri)!!)
}

private fun getRealPathFromURI(context: Context, uri: Uri): String? {
    val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        if (isExternalStorageDocument(uri)) {
            val docId = DocumentsContract.getDocumentId(uri)
            val split = docId.split(":").toTypedArray()
            val type = split[0]
            if ("primary".equals(type, ignoreCase = true)) {
                return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
            }
            // TODO handle non-primary volumes
        } else if (isDownloadsDocument(uri)) {
            val id = DocumentsContract.getDocumentId(uri)
            val contentUri: Uri = ContentUris.withAppendedId(
                Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)
            )
            return getDataColumn(context, contentUri, null, null)
        } else if (isMediaDocument(uri)) {
            val docId = DocumentsContract.getDocumentId(uri)
            val split = docId.split(":").toTypedArray()
            val type = split[0]
            var contentUri: Uri? = null
            if ("image" == type) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            } else if ("video" == type) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
            } else if ("audio" == type) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
            }
            val selection = "_id=?"
            val selectionArgs = arrayOf(
                split[1]
            )
            return getDataColumn(context, contentUri, selection, selectionArgs)
        }
    } else if ("content".equals(
            uri.scheme,
            ignoreCase = true
        )
    ) {
        return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(
            context,
            uri,
            null,
            null
        )
    } else if ("file".equals(uri.scheme, ignoreCase = true)) {
        return uri.path
    }
    return null
}

private fun getDataColumn(
    context: Context, uri: Uri?, selection: String?,
    selectionArgs: Array<String>?
): String? {
    var cursor: Cursor? = null
    val column = "_data"
    val projection = arrayOf(
        column
    )
    try {
        cursor = context.contentResolver.query(
            uri!!, projection, selection, selectionArgs,
            null
        )
        if (cursor != null && cursor.moveToFirst()) {
            val index: Int = cursor.getColumnIndexOrThrow(column)
            return cursor.getString(index)
        }
    } finally {
        cursor?.close()
    }
    return null
}

private fun isExternalStorageDocument(uri: Uri): Boolean {
    return "com.android.externalstorage.documents" == uri.authority
}

private fun isDownloadsDocument(uri: Uri): Boolean {
    return "com.android.providers.downloads.documents" == uri.authority
}

private fun isMediaDocument(uri: Uri): Boolean {
    return "com.android.providers.media.documents" == uri.authority
}

private fun isGooglePhotosUri(uri: Uri): Boolean {
    return "com.google.android.apps.photos.content" == uri.authority
}
}

Please help me with this logic or is there any library which can give me real file paths.

xsheru
  • 467
  • 4
  • 25
  • Why do you want to get a 'real file path'? For what? You can do all with that content scheme what you can do with a file path. – blackapps Jan 27 '20 at 09:51
  • After choosing multiple files, I want to upload them to server. I am getting real path and using that path I am getting file which is going in Multipart request. I might be doing this wrong way. Please let me know if this is wrong way. – xsheru Jan 27 '20 at 09:54
  • And why dont you use that content scheme for it? – blackapps Jan 27 '20 at 09:55
  • I got exception while using content uri directly while accessing third party apps. So thats why I am trying real path. – xsheru Jan 27 '20 at 09:59
  • When I do this File(contentUri) with files which are on external card it works but somehow gives error with third party apps – xsheru Jan 27 '20 at 10:00
  • `File(contentUri) ` ?? Dont use the FIle clas for a content scheme. Use the scheme directly. – blackapps Jan 27 '20 at 10:14
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/206700/discussion-between-xsheru-and-blackapps). – xsheru Jan 27 '20 at 10:14
  • Are you on Android Q? – blackapps Jan 27 '20 at 10:22
  • Yes on Android Q – xsheru Jan 27 '20 at 10:22
  • Adobe uses its own provider. There is no file. And if there was one you had no access to it on Q. What you can do instead is copy the 'file' to getExternalFilesDir() and then offer the file path to the Multipart request. – blackapps Jan 27 '20 at 10:25
  • What if I go below Q? – xsheru Jan 27 '20 at 10:27
  • 1
    I think there still is no file as Adobe uses its own provider. Where would it store files? You would have to find that out yourself first. Browse your device to find the 'adobe files' with ACTION_OPEN_DOCUMENT. Maybe they are in the .../Documents directory. Tell where the user browses to while using the file picker. – blackapps Jan 27 '20 at 10:28
  • Ok now I understood this thing. But how email app which attaches file has access to these files? If I open attachment chooser from email app (gmail, spark) I can select same adobe file which I am trying in my application. Works perfectly there. They are also using same file picker which android provides I dont think there is any library they are using. – xsheru Jan 27 '20 at 10:33
  • 1
    You have access too. Those email apps use the content scheme directly. It's so easy to open an InputStream for the content scheme and read the contents. InputStream is = getContentResolver().openInputStream(uri); That is what the Multipart should do to. If you can open a FIleInputStream to read the contents of a file then you can in this way open an InputStream to read the content behind the content scheme. The reading is the same. – blackapps Jan 27 '20 at 10:38

0 Answers0