Created
July 4, 2019 11:55
-
-
Save mirjalal/a9d0054be0aadab2102b828167b8ffa6 to your computer and use it in GitHub Desktop.
Get path of the files which picked from internal/external storage, Google Drive and Google Photos
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Get full file path from external storage | |
* | |
* @param pathData The storage type and the relative path | |
*/ | |
private fun getPathFromExtSD(pathData: Array<String>): String { | |
val type = pathData[0] | |
val relativePath = "/" + pathData[1] | |
var fullPath: String | |
// on my Sony devices (4.4.4 & 5.1.1), `type` is a dynamic string | |
// something like "71F8-2C0A", some kind of unique id per storage | |
// don't know any API that can get the root path of that storage based on its id. | |
// | |
// so no "primary" type, but let the check here for other devices | |
if ("primary".equals(type, ignoreCase = true)) { | |
fullPath = Environment.getExternalStorageDirectory().toString() + relativePath | |
if (fileExists(fullPath)) { | |
return fullPath | |
} | |
} | |
// Environment.isExternalStorageRemovable() is `true` for external and internal storage | |
// so we cannot relay on it. | |
// | |
// instead, for each possible path, check if file exists | |
// we'll start with secondary storage as this could be our (physically) removable sd card | |
fullPath = System.getenv("SECONDARY_STORAGE")!! + relativePath | |
if (fileExists(fullPath)) | |
return fullPath | |
fullPath = System.getenv("EXTERNAL_STORAGE")!! + relativePath | |
return if (fileExists(fullPath)) | |
fullPath | |
else fullPath | |
} | |
/** | |
* 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.<br></br> | |
* <br></br> | |
* Callers should check whether the path is local before assuming it | |
* represents a local file. | |
* | |
* @param uri The Uri to query. | |
*/ | |
private fun getPath(uri: Uri): String? { | |
// DocumentProvider | |
when { | |
DocumentsContract.isDocumentUri(activity, uri) -> // ExternalStorageProvider | |
when { | |
isExternalStorageDocument(uri) -> { | |
val docId = DocumentsContract.getDocumentId(uri) | |
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() | |
val type = split[0] | |
val fullPath = getPathFromExtSD(split) | |
return if (fullPath !== "") | |
fullPath | |
else | |
null | |
} | |
isDownloadsDocument(uri) -> { | |
var cursor: Cursor? = null | |
try { | |
cursor = activity.contentResolver | |
.query(uri, arrayOf(MediaStore.MediaColumns.DISPLAY_NAME), null, null, null) | |
if (cursor != null && cursor.moveToFirst()) { | |
val fileName = cursor.getString(0) | |
val path = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName | |
if (!TextUtils.isEmpty(path)) | |
return path | |
} | |
} finally { | |
cursor?.close() | |
} | |
val id = DocumentsContract.getDocumentId(uri) | |
return try { | |
val contentUri = ContentUris.withAppendedId( | |
Uri.parse("content://downloads/public_downloads"), id.toLong() | |
) | |
getDataColumn(contentUri, null, null) | |
} catch (e: NumberFormatException) { | |
//In Android 8 and Android P the id is not a number | |
uri.path!!.replaceFirst("^/document/raw:".toRegex(), "").replaceFirst("^raw:".toRegex(), "") | |
} | |
/*String id = DocumentsContract.getDocumentId(uri); | |
if (id.startsWith("raw:")) { | |
return id.replaceFirst("raw:", ""); | |
} | |
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads"), Long.valueOf(id)); | |
return getDataColumn(context, contentUri, null, null); | |
*/ | |
} | |
isMediaDocument(uri) -> { | |
val docId = DocumentsContract.getDocumentId(uri) | |
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() | |
val type = split[0] | |
var contentUri: Uri? = null | |
when (type) { | |
"image" -> contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI | |
"video" -> contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI | |
"audio" -> contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI | |
} | |
val selection = "_id=?" | |
val selectionArgs = arrayOf(split[1]) | |
return getDataColumn(contentUri!!, selection, selectionArgs) | |
} | |
isGoogleDriveUri(uri) -> return getDriveFilePath(uri) | |
}// MediaProvider | |
// DownloadsProvider | |
// MediaProvider | |
// DownloadsProvider | |
"content".equals(uri.scheme!!, ignoreCase = true) -> { | |
// Return the remote address | |
if (isGooglePhotosUri(uri)) { | |
val contentPath = getContentFromSegments(uri.pathSegments) | |
return if (contentPath !== "") | |
getPath(Uri.parse(contentPath)) | |
else | |
null | |
} | |
return if (isGoogleDriveUri(uri)) | |
getDriveFilePath(uri) | |
else | |
getDataColumn(uri, null, null) | |
} | |
"file".equals(uri.scheme!!, ignoreCase = true) -> return uri.path | |
}// File | |
// MediaStore (and general) | |
// File | |
// MediaStore (and general) | |
return null | |
} | |
private fun getDriveFilePath(uri: Uri): String { | |
var file: File? = null | |
val cursor = activity.contentResolver.query(uri, null, null, null, null) | |
/* | |
* Get the column indexes of the data in the Cursor, | |
* move to the first row in the Cursor, get the data, | |
* and display it. | |
*/ | |
if (cursor != null) { | |
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) | |
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) | |
cursor.moveToFirst() | |
val name = cursor.getString(nameIndex) | |
val size = cursor.getLong(sizeIndex).toString() | |
file = File(activity.cacheDir, name) | |
try { | |
val inputStream = activity.contentResolver.openInputStream(uri) | |
val outputStream = file.outputStream() | |
var read = 0 | |
val maxBufferSize = 1 * 1024 * 1024 | |
val bytesAvailable = inputStream?.available() ?: 0 | |
//int bufferSize = 1024; | |
val bufferSize = min(bytesAvailable, maxBufferSize) | |
val buffers = ByteArray(bufferSize) | |
while (read != -1) { | |
read = inputStream?.read(buffers) ?: 0 | |
outputStream.write(buffers, 0, read) | |
} | |
inputStream?.close() | |
outputStream.close() | |
} catch (e: Exception) { | |
Log.e("Exception", e.message) | |
} | |
} | |
cursor?.close() | |
return file?.path!! | |
} | |
/** | |
* @param uri The Uri to check. | |
* @return Whether the Uri authority is ExternalStorageProvider. | |
*/ | |
private fun isExternalStorageDocument(uri: Uri) = "com.android.externalstorage.documents" == uri.authority | |
/** | |
* @param uri The Uri to check. | |
* @return Whether the Uri authority is DownloadsProvider. | |
*/ | |
private fun isDownloadsDocument(uri: Uri) = "com.android.providers.downloads.documents" == uri.authority | |
/** | |
* @param uri The Uri to check. | |
* @return Whether the Uri authority is MediaProvider. | |
*/ | |
private fun isMediaDocument(uri: Uri) = "com.android.providers.media.documents" == uri.authority | |
/** | |
* @param uri The Uri to check. | |
* @return Whether the Uri authority is Google Photos. | |
*/ | |
private fun isGooglePhotosUri(uri: Uri) = | |
"com.google.android.apps.photos.content" == uri.authority || "com.google.android.apps.photos.contentprovider" == uri.authority | |
/** | |
* @param uri The Uri to check. | |
* @return Whether the Uri authority is Google Drive. | |
*/ | |
private fun isGoogleDriveUri(uri: Uri) = | |
"com.google.android.apps.docs.storage" == uri.authority || "com.google.android.apps.docs.storage.legacy" == uri.authority | |
/** | |
* Get the value of the data column for this Uri. This is useful for | |
* MediaStore Uris, and other file-based ContentProviders. | |
* | |
* @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. | |
*/ | |
private fun getDataColumn(uri: Uri, selection: String?, selectionArgs: Array<String>?): String? { | |
var cursor: Cursor? = null | |
val column = "_data" | |
val projection = arrayOf(column) | |
try { | |
cursor = activity.contentResolver.query(uri, projection, selection, selectionArgs, null) | |
if (cursor != null && cursor.moveToFirst()) { | |
val columnIndex = cursor.getColumnIndexOrThrow(column) | |
return cursor.getString(columnIndex) | |
} | |
} finally { | |
cursor?.close() | |
} | |
return null | |
} | |
/** | |
* Check if a file exists on device | |
* | |
* @param filePath The absolute file path | |
*/ | |
private fun fileExists(filePath: String) = File(filePath).exists() | |
/** | |
* Get content:// from segment list | |
* In the new Uri Authority of Google Photos, the last segment is not the content:// anymore | |
* So let's iterate through all segments and find the content uri! | |
* | |
* @param segments The list of segment | |
*/ | |
private fun getContentFromSegments(segments: List<String>): String { | |
var contentPath = "" | |
for (item in segments) { | |
if (item.startsWith("content://")) { | |
contentPath = item | |
break | |
} | |
} | |
return contentPath | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment