Skip to content

Instantly share code, notes, and snippets.

@mirjalal
Created July 4, 2019 11:55
Show Gist options
  • Save mirjalal/a9d0054be0aadab2102b828167b8ffa6 to your computer and use it in GitHub Desktop.
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
/**
* 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