How to upload file from downloads folder in Android Oreo and above? - android

I am trying to write the code to upload file from Android Oreo and Above. First I am running a intent to get the uri of the file.
intent_upload.action = Intent.ACTION_GET_CONTENT
return Intent.createChooser(intent_upload, pickerTitle)
But when I select file from downloads folder, it returns a null filepath from following code. It works perfectly for devices below Android Oreo but I cannot find any solution for android o and above.
Please help
val id = DocumentsContract.getDocumentId(uri)
val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id));
return getDataColumn(context, contentUri, null, null)
Code for getDataColumn is as follow
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)
val temp = context
if (cursor != null && cursor.moveToFirst()) {
val index = cursor.getColumnIndexOrThrow(column)
return cursor.getString(index)
}
}catch (e:Exception){
} finally {
cursor?.close()
}
return null
}
It is happening for API 26 and above.
My motive is to upload a pdf file from the downloads folder in Android.

I know this is past due. I also faced this issue and I was able to find a solution from this gist. FileUtils.java. Just use this util class in your project
Just leaving this here for people who face this issue in the future. Wasted a couple of hours working on this.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Uri uri = data.getData();
File originalFile = new File(FileUtils.getRealPath(this,uri));
}

Related

Android 12 crop image get SecurityExectipion

I'm trying to crop media images from MediaStore query,but got this Execption:
Caused by: java.lang.SecurityException: UID 10160 does not have permission to content://media/external/images/media/48 [user 0]
at android.os.Parcel.createExceptionOrNull(Parcel.java:2425)
at android.os.Parcel.createException(Parcel.java:2409)
at android.os.Parcel.readException(Parcel.java:2392)
at android.os.Parcel.readException(Parcel.java:2334)
at android.app.IActivityTaskManager$Stub$Proxy.startActivity(IActivityTaskManager.java:2326)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1758)
at android.app.Activity.startActivityForResult(Activity.java:5407)
at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:588)
at android.app.Activity.startActivityForResult(Activity.java:5365)
at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:574)
As I completely understand it's permission related problem, yet I have no idea how to fix it.As far as I can see,the Crop-App has no read permission to [content://media/external/images/media/48],which my-own-app hold.
the code cause this problem as below:
val su = srcUri ?: throw IllegalArgumentException("Source uri is Null")
val f = if (path.isNullOrBlank()) {
File(
FileTool.getAppCacheDir(act),
tempCropName()
).also { path = it.absolutePath }
} else {
File(path!!)
}
val cropUri =
FileTool.getFileUri(act, f) ?: throw IllegalArgumentException("Failed to get crop uri")
val intent = Intent("com.android.camera.action.CROP").also {
it.setDataAndType(su, MediaType.IMAGE.value())
it.putExtra("aspectX", 1)//ratio
it.putExtra("aspectY", 1)
it.putExtra("outputX", size)//size
it.putExtra("outputY", size)
it.putExtra("scale", true)
it.putExtra("return-data", false)//no thumbnail got from back intent
it.putExtra("outputFormat", format)
it.putExtra(MediaStore.EXTRA_OUTPUT, cropUri)
}
and the SecurityException located at it.setDataAndType(su, MediaType.IMAGE.value()).
as for su,the source uri,got like this
private fun loadImageUriList(
bucketId: Long,
context:Context
): List<Uri>? {
val selection = "${MediaStore.Images.Media.BUCKET_ID} = ?"
val sort = "${MediaStore.Images.Media._ID} DESC"
val selectionArgs = arrayOf(bucketId.toString())
val images = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val c = context.contentResolver.query(images, null, selection, selectionArgs, sort)?:return null
val imageUris = arrayListOf<Uri>()
try {
if (c.moveToFirst()) {
val iid = c.getColumnIndex(MediaStore.MediaColumns._ID)
do {
val imgId = c.getInt(iid)
val path = Uri.withAppendedPath(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imgId.toString()
)
imageUris.add(path)
} while (c.moveToNext())
}
} finally {
c.closeQuietly()
}
return imageUris
}
)
I tried
MediaStore.getRedactedUri(resolver,su)
but the problem still ocurred.
by the way, crop image from the system camera worked fine.
Now,I have to copy the image from source Uri to my app's directory,then do the crop job(the Intent above).it's so much work to do and ugly,I know I must have missed something,but I look through google developer doc,got nothing.
please ,help.

Why is my Cursor for loading images from external storage empty

I keep getting an empty cursor with this method used for loading external images, no idea why. It also worked for me on one emulator configuration before, but now when I try to Log the contents of ID it says:
android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 0. There's pictures installed on the emulator in 3 different external directories.
private suspend fun loadImages(): List<Image>
{
return withContext(Dispatchers.IO) {
val uri = if(Build.VERSION.SDK_INT >= 29) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else MediaStore.Images.Media.EXTERNAL_CONTENT_URI
requireActivity().contentResolver.query(uri, arrayOf(MediaStore.Images.Media._ID),
null, null, "${MediaStore.Images.Media.DATE_ADDED} ASC"
)?.use { cursor ->
val photos = mutableListOf<Image>()
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
while(cursor.moveToNext()) {
val id = cursor.getLong(idColumn)
val contentUri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
photos.add(Image(id, contentUri))
}
photos
} ?: listOf()
}
}
pic of working image gallery
As usual with my issues, it was something entirely different. The emulator bugged out, I reset it and uploaded images again and it works now. The code is a bit changed without the dispatcher now.
private fun loadImages(): List<Image>
{
val photos = mutableListOf<Image>()
val uri = if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else MediaStore.Images.Media.EXTERNAL_CONTENT_URI
requireActivity().contentResolver.query(uri, arrayOf(MediaStore.Images.Media._ID),
null, null, null
)?.use { cursor ->
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
while(cursor.moveToNext()) {
photos.add(Image(ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getLong(idColumn))))
}
return photos.toList()
} ?: return listOf()
}
this is the code now, it works fine

Get Image URI from Gallery and passing it to another activity, cannot see image, get an exception

I have an activity from which I launch the gallery, select an image, and want to display the selected image in another activity. I have referred to the following solution and implemented the same.
How to get Image URI from Gallery?
Though I am able to pass the URI to the next activity, I cannot see anything on the image view. Any help as to where I am going wrong, appreciated.
btn_launch_gallery.setOnClickListener {
val requestCode = 0
val launchGalleryIntent = Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(launchGalleryIntent, requestCode)
}
My OnActivityResult looks like this, basically implemented the same as given in the example cited above.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode === 0 && resultCode === Activity.RESULT_OK ) {
val selectedImage: Uri? = data?.data
val picturePath = getRealPathFromURI(
selectedImage,
this
)
val intent = Intent(this, LogFoodDetail::class.java)
intent.putExtra("image_from_gallery", picturePath)
try {
startActivity(intent)
}
catch (e: Exception)
{
e.printStackTrace()
Log.e("Error",e.printStackTrace().toString())
}
}
}
fun getRealPathFromURI(contentURI: Uri?, context: Activity): String? {
val projection =
arrayOf(MediaStore.Images.Media.DATA)
val cursor = context.managedQuery(
contentURI, projection, null,
null, null
)
?: return null
val column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
return if (cursor.moveToFirst()) {
// cursor.close();
cursor.getString(column_index)
} else null
// cursor.close();
}
In my next activity, I getting the intent like this and passing the URI to ImageView. However, I cannot see the image. I get the following error - W/System.err: java.io.FileNotFoundException: No content provider: /storage/emulated/0/DCIM/Camera/***.jpg
val resId = intent.getStringExtra("image_from_gallery")
val imgThumbnail: ImageView = findViewById(R.id.food_thumbnail)
try{
val imageStream: InputStream? = contentResolver.openInputStream(Uri.parse(resId))
val bitmap = BitmapFactory.decodeStream(imageStream)
imgThumbnail.setImageBitmap(bitmap)
}
catch (e: Exception)
{
e.printStackTrace()
}
I see the following image in the next activity:
UPDATE:
As commented by #blackapps in his answer passing the URI as a string to the next activity on an intent.putExtra() and resolving the URI in the subsequent activity solved it, the updated code in OnActivityResult() is,
...
val selectedImage: Uri? = data?.data
val intent = Intent(this, LogFoodDetail::class.java)
intent.putExtra("image_from_gallery",
selectedImage.toString())
startActivity(intent)
Dont convert a nice uri to a file system path.
Uri uri = data.getData();
Pass the obtained uri directly to the next activity.
And there you can use it for
imageView.setImageUri(uri);
Instead of the uri you can also pass the uri as string with uri.toString().
You can directly load an local image Uri using:
imgThumbnail.setImageUri(yourUri);
Instead of sending the string path to the activity, you should send the raw uri and then set it directly to the imageView.

select multiple images from gallery in KOTLIN (want image path) [duplicate]

This question already has answers here:
Error "must not be null" in Kotlin
(3 answers)
Closed 4 years ago.
I am working on an Application for making a video from multiple images in kotlin. I got many code of java but can not convert it in propare way to kotlin code. Alwayse got an error cursor.getString(column_index) must not be null. I am just beginner at Kotlin. so can anyone give a brief solution for my problem.
val cursor = contentResolver.query(uri, filePathColumn, null, null, null)
cursor!!.moveToFirst()
val columnIndex = cursor.getColumnIndex(filePathColumn[0])
Hey I m also suffering with same issue nd got the solution. just follow my code.
private var context: Context? = null
var PICK_IMAGE_MULTIPLE = 1
lateinit var imagePath: String
var imagesPathList: MutableList<String> = arrayListOf()
call gallery intent first
if (Build.VERSION.SDK_INT < 19) {
var intent = Intent()
intent.type = "image/*"
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
intent.action = Intent.ACTION_GET_CONTENT
startActivityForResult(
Intent.createChooser(intent, "Select Picture")
, PICK_IMAGE_MULTIPLE
)
} else {
var intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
startActivityForResult(intent, PICK_IMAGE_MULTIPLE);
}
now check onActivityResult
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// When an Image is picked
if (requestCode == PICK_IMAGE_MULTIPLE && resultCode == Activity.RESULT_OK
&& null != data
) {
if (data.getClipData() != null) {
var count = data.clipData.itemCount
for (i in 0..count - 1) {
var imageUri: Uri = data.clipData.getItemAt(i).uri
getPathFromURI(imageUri)
}
} else if (data.getData() != null) {
var imagePath: String = data.data.path
Log.e("imagePath", imagePath);
}
displayImageData()
}
}
private fun getPathFromURI(uri: Uri) {
var path: String = uri.path // uri = any content Uri
val databaseUri: Uri
val selection: String?
val selectionArgs: Array<String>?
if (path.contains("/document/image:")) { // files selected from "Documents"
databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
selection = "_id=?"
selectionArgs = arrayOf(DocumentsContract.getDocumentId(uri).split(":")[1])
} else { // files selected from all other sources, especially on Samsung devices
databaseUri = uri
selection = null
selectionArgs = null
}
try {
val projection = arrayOf(
MediaStore.Images.Media.DATA,
MediaStore.Images.Media._ID,
MediaStore.Images.Media.ORIENTATION,
MediaStore.Images.Media.DATE_TAKEN
) // some example data you can query
val cursor = contentResolver.query(
databaseUri,
projection, selection, selectionArgs, null
)
if (cursor.moveToFirst()) {
val columnIndex = cursor.getColumnIndex(projection[0])
imagePath = cursor.getString(columnIndex)
// Log.e("path", imagePath);
imagesPathList.add(imagePath)
}
cursor.close()
} catch (e: Exception) {
Log.e(TAG, e.message, e)
}
}
This is a solution using Github repo for your requirement.
In your app gradle file add these lines
implementation 'com.github.esafirm.android-image-picker:imagepicker:1.13.1'
// for experimental rx picker
implementation 'com.github.esafirm.android-image-picker:rximagepicker:1.13.1'
// If you have a problem with Glide, please use the same Glide version or simply open an issue
implementation 'com.github.bumptech.glide:glide:4.8.0'
in Java class call this to pick or take image
startActivityForResult(ImagePicker.create(getActivity())
.multi()
.folderMode(true)
.returnMode(ReturnMode.ALL)
.getIntent(getActivity()), IpCons.RC_IMAGE_PICKER);
and in onActivityResult() get the arraylist of selected images
#Override
protected void onActivityResult(int requestCode, final int resultCode, Intent data) {
if (ImagePicker.shouldHandle(requestCode, resultCode, data)) {
// Get a list of picked images
List<Image> images = ImagePicker.getImages(data)
// do your stuff here
// or get a single image only
//Image image = ImagePicker.getFirstImageOrNull(data)
}
super.onActivityResult(requestCode, resultCode, data);
}
This code is less complex and no need to handle image multiple selection , just adding multi() to enable multiple selection.
Note:- Copy this code and paste in your kotlin project , the converter will
automatically convert it to kotlin

URI from Intent.ACTION_GET_CONTENT into File

Launch photo picker using Intent.ACTION_GET_CONTENT
Retrieve URI of selected item
Retrieve PATH of URI so that I can POST it to my webserver
Code to launch browse
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, BROWSE_IMAGE_REQUEST_CODE);
Code to retrieve selected image
if (RESULT_OK == resultCode &&
BROWSE_IMAGE_REQUEST_CODE == requestCode) {
Uri uri = data.getData();
Code to send to the webserver
File file = new File(uri.getPath());
new FileSystemResourceFile(file);
I am currently able to retrieve the PATH from the URI no prob /external/images/media/24 but for some weird reason file is always null, help please?
I've done this method to convert Uri from Intent.ACTION_GET_CONTENT to real path:
public static String getRealPathFromUri(Activity activity, Uri contentUri) {
String[] proj = { MediaStore.Images.Media.DATA };
Cursor cursor = activity.managedQuery(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
Which in turn converted into File:
Uri filePathFromActivity = (Uri) extras.get(Intent.EXTRA_STREAM);
filePathFromActivity = Uri.parse(FileUtil.getRealPathFromUri( (Activity) IntentActivity.this, filePathFromActivity));
File imageFile = new File(filePathFromActivity.getPath());
I know its been a long time since the answer, but I faced the same wall and this is the solution I ended up with. The main benefit of this code is that avoids having parametrized (and hard-coded) all possible providers types and locations across all different android versions.
val act = getActivity() as Activity
object: AsyncTask<Uri, Void, File?>() {
override fun doInBackground(uris: Array<Uri>): File? {
if(act.isFinishing()) return null
try {
val dir = act.getCacheDir()
val file = File.createTempFile("PREFIX", "SUFFIX", dir)
val inputStream = act.getContentResolver().openInputStream(uris[0])
val fos = FileOutputStream(file)
BitmapFactory.decodeStream(inputStream)
.compress(Bitmap.CompressFormat.JPEG, 90, fos)
return file
} catch(e: Exception) { e.printStackTrace() }
return null
}
override fun onPostExecute(result: File?) {
result?.let { doSomethingWithFile(it) }
}
}.execute(uri)
I put it wrapped inside of an AsyncTask for the purpose of leaving unblocked ui thread during bitmap compress. Please be aware that using Bitmap.CompressFormat.JPEG means lossing alpha channel.
Hope it helps!
Pick any file using System File Picker:
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "*/*"
startActivityForResult(intent, 1)
onActivityResult:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 1 && resultCode == Activity.RESULT_OK) {
val file = data?.data?.let {
getFileFromUri(requireContext().contentResolver, uri, requireContext().cacheDir)
}
}
}
Get File:
private fun getFileFromUri(contentResolver: ContentResolver, uri: Uri, directory: File): File {
val file =
File.createTempFile("suffix", "prefix", directory)
file.outputStream().use {
contentResolver.openInputStream(uri)?.copyTo(it)
}
return file
}

Categories

Resources