Created
October 6, 2022 08:50
-
-
Save AmolPardeshi99/5996812509929f69645c91cd62545dff to your computer and use it in GitHub Desktop.
PathUtils for file upload in Android
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
package com.leap.android_commons.helper | |
import android.annotation.SuppressLint | |
import android.content.ContentUris | |
import android.content.Context | |
import android.database.Cursor | |
import android.net.Uri | |
import android.os.Build | |
import android.os.Environment | |
import android.provider.DocumentsContract | |
import android.provider.MediaStore | |
import android.provider.OpenableColumns | |
import android.text.TextUtils | |
import android.util.Log | |
import java.io.File | |
import java.io.FileOutputStream | |
import java.io.InputStream | |
import kotlin.math.min | |
public class PathUtils { | |
private var contentUri: Uri? = null | |
var context: Context? = null | |
fun FileUtils(context: Context) { | |
this.context = context | |
} | |
@SuppressLint("NewApi") | |
fun getPath(uri: Uri): String? { | |
// check here to KITKAT or new version | |
val isKitKat: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT | |
var selection: String? = null | |
var selectionArgs: Array<String>? = null | |
// DocumentProvider | |
if (isKitKat) { | |
// ExternalStorageProvider | |
if (isExternalStorageDocument(uri)) { | |
val docId: String = DocumentsContract.getDocumentId(uri) | |
val split = docId.split(":".toRegex()).toTypedArray() | |
val type = split[0] | |
val fullPath = getPathFromExtSD(split) | |
return if (fullPath !== "") { | |
fullPath | |
} else { | |
null | |
} | |
} | |
// DownloadsProvider | |
if (isDownloadsDocument(uri)) { | |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | |
val id: String | |
var cursor: Cursor? = null | |
try { | |
cursor = context?.getContentResolver()?.query( | |
uri, | |
arrayOf<String>(MediaStore.MediaColumns.DISPLAY_NAME), | |
null, | |
null, | |
null | |
) | |
if (cursor != null && cursor.moveToFirst()) { | |
val fileName: String = cursor.getString(0) | |
val path: String = Environment.getExternalStorageDirectory().toString() | |
.toString() + "/Download/" + fileName | |
if (!TextUtils.isEmpty(path)) { | |
return path | |
} | |
} | |
} finally { | |
if (cursor != null) cursor.close() | |
} | |
id = DocumentsContract.getDocumentId(uri) | |
if (!TextUtils.isEmpty(id)) { | |
if (id.startsWith("raw:")) { | |
return id.replaceFirst("raw:".toRegex(), "") | |
} | |
val contentUriPrefixesToTry = arrayOf( | |
"content://downloads/public_downloads", | |
"content://downloads/my_downloads" | |
) | |
for (contentUriPrefix in contentUriPrefixesToTry) { | |
return try { | |
val contentUri: Uri = ContentUris.withAppendedId( | |
Uri.parse(contentUriPrefix), | |
java.lang.Long.valueOf(id) | |
) | |
getDataColumn(context, contentUri, null, null) | |
} catch (e: NumberFormatException) { | |
//In Android 8 and Android P the id is not a number | |
uri.getPath()?.replaceFirst("^/document/raw:", "") | |
?.replaceFirst("^raw:", "") | |
} | |
} | |
} | |
} else { | |
val id: String = DocumentsContract.getDocumentId(uri) | |
if (id.startsWith("raw:")) { | |
return id.replaceFirst("raw:".toRegex(), "") | |
} | |
try { | |
contentUri = ContentUris.withAppendedId( | |
Uri.parse("content://downloads/public_downloads"), | |
java.lang.Long.valueOf(id) | |
) | |
} catch (e: NumberFormatException) { | |
e.printStackTrace() | |
} | |
if (contentUri != null) { | |
return getDataColumn(context, contentUri, null, null) | |
} | |
} | |
} | |
// MediaProvider | |
if (isMediaDocument(uri)) { | |
val docId: String = DocumentsContract.getDocumentId(uri) | |
val split = docId.split(":".toRegex()).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 | |
} | |
selection = "_id=?" | |
selectionArgs = arrayOf(split[1]) | |
return getDataColumn( | |
context, contentUri, selection, | |
selectionArgs | |
) | |
} | |
if (isGoogleDriveUri(uri)) { | |
return getDriveFilePath(uri) | |
} | |
if (isWhatsAppFile(uri)) { | |
return getFilePathForWhatsApp(uri) | |
} | |
if ("content".equals(uri.getScheme(), ignoreCase = true)) { | |
if (isGooglePhotosUri(uri)) { | |
return uri.getLastPathSegment() | |
} | |
if (isGoogleDriveUri(uri)) { | |
return getDriveFilePath(uri) | |
} | |
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { | |
// return getFilePathFromURI(context,uri); | |
copyFileToInternalStorage(uri, "userfiles") | |
// return getRealPathFromURI(context,uri); | |
} else { | |
getDataColumn(context, uri, null, null) | |
} | |
} | |
if ("file".equals(uri.getScheme(), ignoreCase = true)) { | |
return uri.getPath() | |
} | |
} else { | |
if (isWhatsAppFile(uri)) { | |
return getFilePathForWhatsApp(uri) | |
} | |
if ("content".equals(uri.getScheme(), ignoreCase = true)) { | |
val projection = arrayOf<String>( | |
MediaStore.Images.Media.DATA | |
) | |
var cursor: Cursor? = null | |
try { | |
cursor = context?.getContentResolver() | |
?.query(uri, projection, selection, selectionArgs, null) | |
val column_index: Int? = | |
cursor?.getColumnIndexOrThrow(MediaStore.Images.Media.DATA) | |
if (cursor != null) { | |
if (cursor.moveToFirst()) { | |
return column_index?.let { cursor.getString(it) } | |
} | |
} | |
} catch (e: Exception) { | |
e.printStackTrace() | |
} | |
} | |
} | |
return null | |
} | |
private fun fileExists(filePath: String): Boolean { | |
val file = File(filePath) | |
return file.exists() | |
} | |
private fun getPathFromExtSD(pathData: Array<String>): String { | |
val type = pathData[0] | |
val relativePath = "/" + pathData[1] | |
var fullPath = "" | |
// 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 | |
} | |
private fun getDriveFilePath(uri: Uri): String? { | |
val returnUri: Uri = uri | |
val returnCursor: Cursor? = | |
context?.contentResolver?.query(returnUri, 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. | |
* */ | |
val nameIndex: Int? = returnCursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME) | |
val sizeIndex: Int? = returnCursor?.getColumnIndex(OpenableColumns.SIZE) | |
returnCursor?.moveToFirst() | |
val name: String? = nameIndex?.let { returnCursor.getString(it) } | |
val size = sizeIndex?.let { returnCursor.getLong(it).toString() } | |
val file = File(context?.cacheDir, name) | |
try { | |
val inputStream: InputStream? = context?.contentResolver?.openInputStream(uri) | |
val outputStream = FileOutputStream(file) | |
var read = 0 | |
val maxBufferSize = 1 * 1024 * 1024 | |
val bytesAvailable: Int? = inputStream?.available() | |
//int bufferSize = 1024; | |
val bufferSize = bytesAvailable?.let { min(it, maxBufferSize) } | |
val buffers = bufferSize?.let { ByteArray(it) } | |
while (inputStream?.read(buffers).also { | |
if (it != null) { | |
read = it | |
} | |
} != -1) { | |
outputStream.write(buffers, 0, read) | |
} | |
Log.e("File Size", "Size " + file.length()) | |
inputStream?.close() | |
outputStream.close() | |
Log.e("File Path", "Path " + file.getPath()) | |
Log.e("File Size", "Size " + file.length()) | |
} catch (e: Exception) { | |
e.message?.let { Log.e("Exception", it) } | |
} | |
return file.getPath() | |
} | |
/*** | |
* Used for Android Q+ | |
* @param uri | |
* @param newDirName if you want to create a directory, you can set this variable | |
* @return | |
*/ | |
private fun copyFileToInternalStorage(uri: Uri, newDirName: String): String? { | |
val returnUri: Uri = uri | |
val returnCursor: Cursor? = context?.contentResolver?.query( | |
returnUri, arrayOf<String>( | |
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE | |
), 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. | |
* */ | |
val nameIndex: Int? = returnCursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME) | |
val sizeIndex: Int? = returnCursor?.getColumnIndex(OpenableColumns.SIZE) | |
returnCursor?.moveToFirst() | |
val name: String? = nameIndex?.let { returnCursor.getString(it) } | |
val size = sizeIndex?.let { returnCursor.getLong(it).toString() } | |
val output: File | |
if (newDirName != "") { | |
val dir = File(context?.filesDir.toString() + "/" + newDirName) | |
if (!dir.exists()) { | |
dir.mkdir() | |
} | |
output = File(context?.filesDir.toString() + "/" + newDirName + "/" + name) | |
} else { | |
output = File(context?.filesDir.toString() + "/" + name) | |
} | |
try { | |
val inputStream: InputStream? = context?.getContentResolver()?.openInputStream(uri) | |
val outputStream = FileOutputStream(output) | |
var read = 0 | |
val bufferSize = 1024 | |
val buffers = ByteArray(bufferSize) | |
if (inputStream != null) { | |
while (inputStream.read(buffers).also { read = it } != -1) { | |
outputStream.write(buffers, 0, read) | |
} | |
} | |
inputStream?.close() | |
outputStream.close() | |
} catch (e: Exception) { | |
e.message?.let { Log.e("Exception", it) } | |
} | |
return output.getPath() | |
} | |
private fun getFilePathForWhatsApp(uri: Uri): String? { | |
return copyFileToInternalStorage(uri, "whatsapp") | |
} | |
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 = uri?.let { | |
context?.contentResolver?.query( | |
it, projection, | |
selection, selectionArgs, null | |
) | |
} | |
if (cursor != null && cursor.moveToFirst()) { | |
val index: Int = cursor.getColumnIndexOrThrow(column) | |
return cursor.getString(index) | |
} | |
} finally { | |
if (cursor != null) cursor.close() | |
} | |
return null | |
} | |
private fun isExternalStorageDocument(uri: Uri): Boolean { | |
return "com.android.externalstorage.documents" == uri.getAuthority() | |
} | |
private fun isDownloadsDocument(uri: Uri): Boolean { | |
return "com.android.providers.downloads.documents" == uri.getAuthority() | |
} | |
private fun isMediaDocument(uri: Uri): Boolean { | |
return "com.android.providers.media.documents" == uri.getAuthority() | |
} | |
private fun isGooglePhotosUri(uri: Uri): Boolean { | |
return "com.google.android.apps.photos.content" == uri.getAuthority() | |
} | |
fun isWhatsAppFile(uri: Uri): Boolean { | |
return "com.whatsapp.provider.media" == uri.getAuthority() | |
} | |
private fun isGoogleDriveUri(uri: Uri): Boolean { | |
return "com.google.android.apps.docs.storage" == uri.getAuthority() || "com.google.android.apps.docs.storage.legacy" == uri.getAuthority() | |
} | |
fun getFile(context: Context, uri: Uri?): File? { | |
if (uri != null) { | |
val path = FileUtils.getPath(context, uri) | |
if (path != null) { | |
return File(path) | |
} | |
} | |
return null | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment