I was trying to get the real path from intent chooser in Xamarin Android and its working fine in every android version except android 10 . In android 10 content uri comes like
content://com.android.providers.downloads.documents/document/msf%3A180
I was trying to get the real path from intent chooser in Xamarin Android and its working fine in every android version except android 10 . In android 10 content uri comes like
content://com.android.providers.downloads.documents/document/msf%3A180
Android 10 has scoped storage enabled by default. This means you cannot directly get access to the external storage path - take a look here.
You can do what is suggested by setting requestLegacyExternalStorage to true in your app's AndroidManifest.xml, but keep in mind this will only work for targetSdk 29 and not for 30+.
If you're trying to access media, this should be done through the MediaStore. Otherwise, take a look at the scoped storage docs.
after getting the URI of the image file of course we need to edit this file by compressing it, after that getting a real file path.if you targeting SDK 30, this is my implementation using scoped storage ;) don't forget to upvoting my answer.
import android.content.ContentValues
import android.content.Context
import android.content.res.AssetFileDescriptor
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.ImageDecoder
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import androidx.annotation.RequiresApi
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
/**
* Created by Mostafa Anter on 11/5/20.
*/
object PhotoHelper{
@RequiresApi(Build.VERSION_CODES.Q)
/**
* mContext: context from activity or fragment,
* imageSelectedUri: uri that return from any pick picture library it usually return inside on activity response method
* appFolderName: name of folder that will generate to save compressing images ;)
* createdImageCompressedName : random name of new compressed image
*/
fun compressGetImageFilePath(
mContext: Context,
imageSelectedUri: Uri,
appFolderName: String,
createdImageCompressedName: String = System.currentTimeMillis().toString()
): String {
//region Getting the photo to process it
val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
ImageDecoder.decodeBitmap(ImageDecoder.createSource(mContext.contentResolver, imageSelectedUri))
} else {
mContext.contentResolver.openInputStream(imageSelectedUri)?.use { inputStream ->
BitmapFactory.decodeStream(inputStream)
}
}
// endregion
//region save photo to gallery using Scoped storage
val values = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, createdImageCompressedName)
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/$appFolderName/")
put(MediaStore.Images.Media.IS_PENDING, 1)
}
val collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val imageUri = mContext.contentResolver.insert(collection, values)
//endregion
//region save image
mContext.contentResolver.openOutputStream(imageUri!!).use { out ->
bitmap!!.compress(Bitmap.CompressFormat.PNG, 100, out)
}
values.clear()
values.put(MediaStore.Images.Media.IS_PENDING, 0)
mContext.contentResolver.update(imageUri, values, null, null)
//endregion
//region get file path of content uri
val file = File(mContext.cacheDir, "$createdImageCompressedName.png")
try {
val assetFileDescriptor: AssetFileDescriptor = mContext.contentResolver.openAssetFileDescriptor(imageUri, "r")!!
val inputStream = FileInputStream(assetFileDescriptor.fileDescriptor)
val outputStream = FileOutputStream(file)
inputStream.copyTo(outputStream)
inputStream.close()
outputStream.close()
} catch (e: FileNotFoundException) {
e.printStackTrace()
}
//endregion
return file.path
}
}
so easy if you want to target old and new devices simply make if statement to check android version if greater or equal android Q use my method else use your old method.
You can query the fileName, and then build the path.
Query the file name:
public static string getFileName(Context context, Android.Net.Uri uri)
{
ICursor cursor = null;
string[] projection = {
MediaStore.MediaColumns.DisplayName
};
try
{
cursor = context.ContentResolver.Query(uri, projection, null, null,
null);
if (cursor != null && cursor.MoveToFirst())
{
int index = cursor.GetColumnIndexOrThrow(MediaStore.MediaColumns.DisplayName);
return cursor.GetString(index);
}
}
finally
{
if (cursor != null)
if (cursor != null)
{
cursor.Close();
}
}
return null;
}
Build the path:
string fileName = getFileName(context, uri);
string filePath = Android.OS.Environment.ExternalStorageDirectory + "/Download/" + fileName;