SecurityException using storage access framework in some devices - android

I want to get content (images & videos) of user selected folder. Code to select the folder is:
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
or Intent.FLAG_GRANT_READ_URI_PERMISSION
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
startActivityForResult(intent, 1088)
And onActivityResult, I am persisting the permission(even I tested without restarting the device but it is not working):
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == Activity.RESULT_OK) {
if (requestCode == 1088) {
val selectedDirUri = data!!.data
grantUriPermission(packageName, selectedDirUri, (Intent.FLAG_GRANT_READ_URI_PERMISSION
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION))
val takeFlags = (data.flags
and (Intent.FLAG_GRANT_READ_URI_PERMISSION
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION))
contentResolver.takePersistableUriPermission(selectedDirUri!!, takeFlags)
}
}
}
And I am traversing through all files using:
val rootUri: Uri = Uri.parse(selectedDir)
val contentResolver: ContentResolver = context.contentResolver
var childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, DocumentsContract.getTreeDocumentId(rootUri))
val dirNodes: MutableList<Uri> = LinkedList()
dirNodes.add(childrenUri)
while (!dirNodes.isEmpty()) {
childrenUri = dirNodes.removeAt(0) // get the item from top
val c = contentResolver.query(childrenUri,
arrayOf(
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_MIME_TYPE,
DocumentsContract.Document.COLUMN_LAST_MODIFIED),
null, null, null)
try {
while (c!!.moveToNext()) {
val docId = c.getString(0)
val fileName = c.getString(1)
val mimeType = c.getString(2)
val lastModified = c.getLong(3)
if (isDirectory(mimeType)) {
val newNode = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, docId)
dirNodes.add(newNode)
} else {
val newNode = DocumentsContract.buildDocumentUriUsingTree(rootUri, docId)
Logger.log("TAG", "========1 Images: $newNode")
val ***sourceFileUri*** = newNode.toString()
}
}
} finally {
closeQuietly(c)
}
}
Then I display images and videos using Glide, it is not displaying.
Even if I try to copy the image using below code:
val inputStream: InputStream? = context.contentResolver.openInputStream(***sourceFileUri***)
I am getting below error, the above line gives an error:
java.lang.SecurityException: com.android.externalstorage has no access
to content://media/external _primary/file/1000008384 at
android.os.Parcel.createException or Null(Parcel.java:2438) at 08
android.os.Parcel.createException(P arcel.java:2422) at
android.os.Parcel.readException(Par cel.java:2405) at
android.database.DatabaseUtils.rea dExceptionFromParcel(DatabaseUtil
s.java:190) at android.database.DatabaseUtils.rea dException
WithFileNotFoundExcepti on From Parcel(DatabaseUtils.java:15 3)
This is happening in Vivo, Oppo and specially in new Samsung phones only which has android OS 11 and 12. I am really frustrated, I tried all possible solution but not able to find any solution till now.
Any solution or advice would be really helpful and appreciated, please please help me.

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.

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.

Video from ACTION_VIDEO_CAPTURE return null video after play

I send intent for recording video in this way
private fun openCamera(url: String) {
if (!viewModel.isAttachmentSaved) {
val takeVideoIntent = Intent(MediaStore.ACTION_VIDEO_CAPTURE)
val file = File(url)
val videoURI = if (Build.VERSION_CODES.N <= android.os.Build.VERSION.SDK_INT) {
FileProvider.getUriForFile(
localActivity,
BuildConfig.APPLICATION_ID + ".fileprovider",
file
)
} else {
Uri.fromFile(file)
}
takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, videoURI)
val resInfoList = requireActivity()
.packageManager
.queryIntentActivities(takeVideoIntent, PackageManager.MATCH_DEFAULT_ONLY)
for (resolveInfo in resInfoList) {
val packageName = resolveInfo.activityInfo.packageName
localActivity?.grantUriPermission(
packageName, Uri.parse(url),
Intent.FLAG_GRANT_WRITE_URI_PERMISSION and Intent.FLAG_GRANT_READ_URI_PERMISSION
)
}
takeVideoIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION and
Intent.FLAG_GRANT_WRITE_URI_PERMISSION and
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION and
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
startActivityForResult(takeVideoIntent, REQUEST_VIDEO)
}
}
After I that record video and play it in Mi Video, press back and confirm video file. And try to get video in onActivityResult but there are null file. Intent contains uri like this content:///external_files/..... FileProvider path is external_files
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_VIDEO && resultCode == Activity.RESULT_OK) {
Log.d("bouxView", "onActivityResult $data $requestCode")
//Toast.makeText(localActivity, R.string.text_start_compressing, Toast.LENGTH_LONG).show()
if (isViewModelInitialized()) {
val path = viewModel.videoPathLiveData.value?.run {
substringBeforeLast(FilesRepository.DELIMITER) + "copied" + FilesRepository.DELIMITER + substringAfterLast(FilesRepository.DELIMITER)
}
data?.data?.let {
try {
val fis: InputStream? = context?.contentResolver?.openInputStream(data?.data)
val videoFile = File(path)
val fos = FileOutputStream(videoFile)
val buffer = ByteArray(1024)
var length: Int = 0
var oldLength:Int = 0
while (fis?.read(buffer).also { length = (it ?: 0) } ?: 0 > 0) {
if (oldLength==0){
oldLength = length
}
fos.write(buffer, 0, length)
}
fis?.close()
fos.close()
Toast.makeText(requireContext(),"oldLength = $oldLength, path = $path, data = ${data.data.toString()}, pathLiveData = ${viewModel.videoPathLiveData.value}", Toast.LENGTH_LONG).show()
} catch (e: IOException) {
}
}
viewModel.onVideoSaved(path?:viewModel.videoPathLiveData.value)
}
}
}
If I record video and confirm it without playing all fine. What can be wrong? I also tried to find video in videoUri path that I put into MediaStore.EXTRA_OUTPUT parameter, no success. Tried to get video path from uri (How to get the Full file path from URI), no success.
It reproduced only in MIUI 11, android version 9
Looks like MIUI bug. This issue was resolved after updating MIUI.

Kotlin: Exoplayer is not playing the video

I am learning Kotlin and Exoplayer and I am making an app that allows the user to select a video and play it on the next screen. However, after starting Intent, the selected video is not played.
First Activity:
private fun openGallery() {
intent = Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
intent.type = "video/*"
startActivityForResult(intent, 1)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK && requestCode == 1) {
val selectedMediaUri = data!!.getData()
if (selectedMediaUri.toString().contains("video")) {
//handle video
// Get selected gallery image
val selectedVideo = data.getData()
// Get and resize profile image
val filePathColumn = arrayOf(MediaStore.Video.Media.DATA)
val cursor = applicationContext.contentResolver.query(selectedVideo!!, filePathColumn, null, null, null)
cursor!!.moveToFirst()
val columnIndex = cursor.getColumnIndex(filePathColumn[0])
val videoStoragePath = cursor.getString(columnIndex)
cursor.close()
val intent = Intent(this#MainActivity,PostarVideoActivity::class.java)
intent.putExtra("path",videoStoragePath)
startActivity(intent)
}
}
Second Activity:
val path = intent.extras!!.getString("path")
Log.i("mypath", path)
val player = ExoPlayerFactory.newSimpleInstance(applicationContext)
videoPreview.player = player
val dataSourceFactory = DefaultDataSourceFactory(
applicationContext,
Util.getUserAgent(applicationContext, applicationContext.getString(R.string.app_name))
)
// This is the MediaSource representing the media to be played.
val videoSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(Uri.parse(path!!))
// Prepare the player with the source.
player.prepare(videoSource)
How to resolve this error? Thanks in advance.
The error was simple ...
Just request READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions
This can be easily resolved with the Dexter library.
Maybe this can help someone

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

Categories

Resources