Android 11 ActivityResult throwing only RESULT_CANCELED - android

We are selecting image/video files with this code
//Pick an image from storage
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType(type); //Can be image/* or video/*
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
activityResultLauncher.launch(Intent.createChooser(intent, "Select Item"));
Then obtain the file Uri(s) on new ActivityResult API
protected final ActivityResultLauncher<Intent> activityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
Log.wtf("WTF", result.toString());
if (result.getResultCode() == Activity.RESULT_OK) {
// There are no request codes
Intent data = result.getData();
if (data == null) {
Toast.makeText(getContext(), R.string.unknown_error, Toast.LENGTH_SHORT).show();
return;
}
activityResult(true, data);
}
else
activityResult(false, null);
});
Unfortunately after updating to Android 11 all we got is RESULT_CANCELED.
E/WTF: ActivityResult{resultCode=RESULT_CANCELED, data=null}

As what Ian said, you were not supposed to user Intent.createChooser with ACTION_OPEN_DOCUMENT though it works prior to Android 11.

Here is a helper class you use as a contract to select image/video/pdf using Activity Result API that will return you a list of Uri's. It can be configured for single or multiple selection of files.
Tested in android 10 and 11.
class SelectFileContract : ActivityResultContract<Pair<SELECTION_TYPE, Boolean>, List<Uri>?>() {
var TAG = this.javaClass.simpleName
override fun createIntent(context: Context, input: Pair<SELECTION_TYPE, Boolean>): Intent {
var intent: Intent? = null
intent = when (input.first) {
SELECTION_TYPE.IMAGE -> Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
)
SELECTION_TYPE.VIDEO -> Intent(
Intent.ACTION_PICK,
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
)
SELECTION_TYPE.PDF -> {
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = "application/pdf"
addCategory(Intent.CATEGORY_OPENABLE)
}
}
}
if (input.second == true) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
}
return intent!!
}
override fun parseResult(resultCode: Int, intent: Intent?): List<Uri>? = when (resultCode) {
Activity.RESULT_OK -> {
val uriList = mutableListOf<Uri>()
/**
* in case of single selection the Uri will be inside [intent.data]
*/
intent?.data?.let { uriList.add(it) }
/**
* in case of multiple selection the Uri will be inside [intent.clipData]
*/
intent?.clipData?.let { clipData: ClipData ->
for (i in 0 until clipData.itemCount) {
uriList.add(clipData.getItemAt(i).uri)
}
}
Log.d(TAG, uriList.toString())
uriList
}
else -> null
}
}
enum class SELECTION_TYPE {
IMAGE,
VIDEO,
PDF
}
Then in activity specific the contract:
private val simpleContractRegistration =
registerForActivityResult(SelectFileContract()) { result: List<Uri>? ->
result?.let {
Log.d(TAG, result.toString())
}
}
Then launch the registration:
binding.button.setOnClickListener {
simpleContractRegistration.launch(
Pair(
SELECTION_TYPE.IMAGE,
false
)
)
}

Related

How to receive path from internal storage in android and transform it into view?

I'm new to android development and I've been building an app for studies purposes that is supposed to display one of the images selected from the gallery previously in a recyclerview.
private fun configDocImageDialog(pathGallery: Int, pathCamera: Int) {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.camera_or_gallery))
.setMessage(getString(R.string.image_path))
.setPositiveButton(getString(R.string.gallery)) { _, _ ->
val intent = Intent()
intent.type = "image/*"
intent.action = Intent.ACTION_GET_CONTENT
startActivityForResult(Intent.createChooser(intent, "Select: "), pathGallery)
}.setNegativeButton(getString(R.string.camera)) { _, _ ->
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(Intent.createChooser(intent, "Take: "), pathCamera)
}.setNeutralButton(getString(R.string.cancel)) { dialog, _ ->
dialog.dismiss()
}.show()
}
Not I'm not worried about the camera result.
Then receiving the result as:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == RESULT_OK) {
val selectedImage = data!!.data
val pathImage = selectedImage.toString()
try {
when (requestCode) {
GALLERY_1 -> {
binding.imageDoc1.setImageURI(selectedImage)
}
CAMERA_1 -> {
binding.imageDoc1.setImageURI(selectedImage)
}
GALLERY_2 -> {
binding.imageDoc2.setImageURI(selectedImage)
}
CAMERA_2 -> {
binding.imageDoc2.setImageURI(selectedImage)
}
GALLERY_3 -> {
binding.imageDoc3.setImageURI(selectedImage)
}
CAMERA_3 -> {
binding.imageDoc3.setImageURI(selectedImage)
}
}
imageList.add(pathImage)
} catch (e: Exception){
e.printStackTrace()
}
}
}
I received as a result from the intent a list with this kind of content:
content://com.android.providers.media.documents/document/image%3A20
And those are saved into the database, the list of paths. /
Is it possible to use this path to my exhibit the image in my adapter? I've been trying different treatments but it always blank.
I've trying to use Picasso as:
override fun onBindViewHolder(holder: DocViewHolder, position: Int) {
val doc = docs[position]
holder.binding.apply {
Picasso.get()
.load(doc.docImageList[0])
.into(imageDoc)
textDocName.text = doc.title
textValidity.text = doc.validity
}
holder.itemView.setOnClickListener {
onItemClickListener?.let {
it(doc)
}
}
}
I want to display an image from internal storage in a recyclerview but the image is always blank.
Any ideas how to do it properly? Thanks

How to hardcode file type while creating it using ActivityResultContracts.CreateDocument()?

I'm creating file using
private val getContent = registerForActivityResult(ActivityResultContracts.CreateDocument()) { uri ->
uri?.let { writeToFile(requireActivity(), it) }
}
and then launch it:
getContent.launch("file.csv")
so when i'm in launcher i can rename file before saving and also change file type which is not what i expect. So can i let user change name but hardcore this type?
private val startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult())
{ result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
// make action
}
}
private fun createFile() {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/png"
intent.putExtra(Intent.EXTRA_TITLE, "AA.png")
startForResult.launch(intent)
}

Android Demo App not opening google pay application after I updated my google pay app

Getting an exception :
No Activity found to handle Intent { act=android.intent.action.VIEW dat=upi://pay?pa=xyx#idfcbank&pn=xyz&mc=5311&tr=txRefId&tn=txDesc&am=12.0&cu=INR pkg=com.google.android.apps.nbu.paisa.user }
Followed these steps for Gpay integration.
https://developers.google.com/pay/india/api/android/in-app-payments
fun gpay() {
val GOOGLE_PAY_PACKAGE_NAME = "com.google.android.apps.nbu.paisa.user"
try {
val uri = Uri.Builder()
.scheme(GooglePayMaker.SCHEME_UPI)
.authority(GooglePayMaker.AUTHORITY_PAY)
.appendQueryParameter(JSONConstants.PAYEE_VPA_KEY, JSONConstants.PAYEE_VPA_VALUE)
.appendQueryParameter(JSONConstants.PAYEE_NAME_KEY, JSONConstants.PAYEE_NAME_VALUE)
.appendQueryParameter(JSONConstants.PAYEE_MERCHANT_CODE_KEY, JSONConstants.PAYEE_MERCHANT_CODE_VALUE)
.appendQueryParameter(JSONConstants.PAYEE_TX_REF_ID_KEY, "xyx")
.appendQueryParameter(JSONConstants.PAYEE_TX_DESC_KEY, "xyx")
.appendQueryParameter(JSONConstants.PAYEE_TX_AMT_KEY, "12.0")
.appendQueryParameter(JSONConstants.PAYEE_CURR_KEY, JSONConstants.PAYEE_CURR_VALUE)
.build()
val intent = Intent(Intent.ACTION_VIEW)
intent.data = uri
intent.setPackage(GOOGLE_PAY_PACKAGE_NAME)
startForResult.launch(intent)
} catch (exception: Exception) {
Log.d("TAG", "" + exception.message)
exception.printStackTrace()
}
}
val startForResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
val intent = result.data
}
}
FYI : using valid credentials.

Method from OnActivityResult Fragment executed, but nothing happend in View

can you help me., ?
I am new in develop Android use kotlin, and still learning.,
this my code on Fragment.,
......
private fun takePhotoFromCamera() {
Dexter.withActivity(requireActivity())
.withPermissions(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
)
.withListener(object : MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
// Here after all the permission are granted launch the CAMERA to capture an image.
if (report!!.areAllPermissionsGranted()) {
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.putExtra("Document", 2)
startActivityForResult(intent, CAMERA)
}
}
override fun onPermissionRationaleShouldBeShown(
permissions: MutableList<PermissionRequest>?,
token: PermissionToken?,
) {
showRationalDialogForPermissions()
}
}).onSameThread()
.check()
}
private fun choosePhotoFromGallery() {
Dexter.withActivity(activity)
.withPermissions(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
.withListener(object : MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
// Here after all the permission are granted launch the gallery to select and image.
if (report!!.areAllPermissionsGranted()) {
val galleryIntent = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
)
galleryIntent.putExtra("Document", 2)
startActivityForResult(galleryIntent, GALLERY)
}
}
override fun onPermissionRationaleShouldBeShown(
permissions: MutableList<PermissionRequest>?,
token: PermissionToken?,
) {
showRationalDialogForPermissions()
}
}).onSameThread()
.check()
}
and this onActivityResult from parent Activity for Fragment
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
for (fragment in supportFragmentManager.fragments) {
fragment.onActivityResult(requestCode, resultCode, data)
}
}
And this OnActivityResult from Fragment
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
if (requestCode == GALLERY) {
if (data != null) {
val contentURI = data.data
try {
// Here this is used to get an bitmap from URI
#Suppress("DEPRECATION")
val selectedImageBitmap =
MediaStore.Images.Media.getBitmap(requireActivity().contentResolver,
contentURI)
// TODO (Step 3 : Saving an image which is selected from GALLERY. And printed the path in logcat.)
// START
val saveImageToInternalStorage =
saveImageToInternalStorage(selectedImageBitmap)
Log.i("Saved Image : ", "Path :: $saveImageToInternalStorage")
// END
binding.btnNpwpCaptureAgain.visibility=View.VISIBLE
binding.ivPvNpwp.foreground.clearColorFilter()
binding.cvNpwp.visibility=View.GONE
binding.btnCaptureNpwp.visibility=View.GONE
binding.ivNpwpPreview.setImageBitmap(selectedImageBitmap) // Set the selected image from GALLERY to imageView.
} catch (e: IOException) {
e.printStackTrace()
Toast.makeText(requireActivity(), "Failed!", Toast.LENGTH_SHORT).show()
}
}
} else if (requestCode == CAMERA) {
val thumbnail: Bitmap = data!!.extras!!.get("data") as Bitmap // Bitmap from camera
// TODO (Step 4 : Saving an image which is selected from CAMERA. And printed the path in logcat.)
// START
val saveImageToInternalStorage =
saveImageToInternalStorage(thumbnail)
Log.i("Saved Image : ", "Path :: $saveImageToInternalStorage")
//binding.btnCaptureKtp.text = getString(R.string.regist_step_2_KTP_retake).toString()
// END
binding.btnNpwpCaptureAgain.visibility=View.VISIBLE
binding.ivPvNpwp.foreground.clearColorFilter()
binding.btnCaptureNpwp.visibility=View.GONE
binding.cvNpwp.visibility=View.GONE
binding.ivNpwpPreview.setImageBitmap(thumbnail) // Set to the imageView.
}
} else if (resultCode == Activity.RESULT_CANCELED) {
Log.e("Cancelled", "Cancelled")
}
//
}
My Problem is why this block is executed, but nothing happend ??
....
binding.btnNpwpCaptureAgain.visibility=View.VISIBLE
binding.ivPvNpwp.foreground.clearColorFilter()
binding.btnCaptureNpwp.visibility=View.GONE
binding.cvNpwp.visibility=View.GONE
binding.ivNpwpPreview.setImageBitmap(thumbnail)
......
Thank you for respond my question.,
Try with putting another null check for "contentURI". Like :-
if(contentURI != null){
try {
// Here this is used to get an bitmap from URI
#Suppress("DEPRECATION")
val selectedImageBitmap =
MediaStore.Images.Media.getBitmap(requireActivity().contentResolver,
contentURI)
// TODO (Step 3 : Saving an image which is selected from GALLERY. And printed the path in logcat.)
// START
val saveImageToInternalStorage =
saveImageToInternalStorage(selectedImageBitmap)
Log.i("Saved Image : ", "Path :: $saveImageToInternalStorage")
// END
binding.btnNpwpCaptureAgain.visibility=View.VISIBLE
binding.ivPvNpwp.foreground.clearColorFilter()
binding.cvNpwp.visibility=View.GONE
binding.btnCaptureNpwp.visibility=View.GONE
binding.ivNpwpPreview.setImageBitmap(selectedImageBitmap) // Set the selected image from GALLERY to imageView.
} catch (e: IOException) {
e.printStackTrace()
Toast.makeText(requireActivity(), "Failed!", Toast.LENGTH_SHORT).show()
}
}
As onActivityResult() method is deprecated, its better to use registerForActivityResult(). Like below :
private val startForResultToLoadImage = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
try {
val selectedImage: Uri? = result.data?.data
if (selectedImage != null){
// From Gallery
// Use uri to get the image
}else{
// From Camera code goes here.
// Get the bitmap directly from camera
val bitmap: Bitmap = result.data?.extras?.get("data") Bitmap
}
} catch (error: Exception) {
Log.d("log==>>", "Error : ${error.localizedMessage}")
}
}
}
For gallery call it like:
val intent = Intent (Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startForResultToLoadImage.launch(intent)
For camera call it like:
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.putExtra(MediaStore.EXTRA_OUTPUT, it)
startForResultToLoadImage.launch(intent)
I believe you should:
Print log at the start of the function with all of the parameters printed to always know how the code will branch. Something like Log.i("onActivityResult: ", "requestCode=" + requestCode + “, resultCode=” + resultCode + “, data=” + data);
Check your request code before you check result code since different requests may have different results.
You’re assuming the result would be either Activity.RESULT_CANCELED or Activity.RESULT_OK but the result code might be a custom result, so please add another else branch after you check Activity.RESULT_CANCELED to be sure you’re covering all results.
Don’t call Fragment.onActivityResult from Activity.onActivityResult if your activity inherits from AppCompatActivity. Activity automatically forward the result to fragments (it even allows request code duplication across fragments without mixing the fragments).
The structure of onActivityResult should be mainly:
if (requestCode == REQUEST_1) {
if (resultCode == RESULT_1) {
// Do your thing
} else if (resultCode == RESULT_2) {
// Do your thing
} else if (resultCode == RESULT_LAST) {
// Do your thing
} else {
// Important!! Expect the unexpected
}
} else if (requestCode == REQUEST_2) {
...
} else if (requestCode == REQUEST_LAST) {
...
}
I would use switch-case instead if-else but it’s you code style so I won’t judge :)

android camera: onActivityResult() intent is null if it had extras

After searching a lot in all the related issues at Stack Overflow and finding nothing, please try to help me.
I created an intent for capture a picture. Then I saw different behavior at onActivityResult(): if I don't put any extra in the Intent (for small pics) the Intent in onActivityResult is ok, but when I put extras in the intent for writing the pic to a file, the intent in onActivityResult is null!
The Intent creation:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// without the following line the intent is ok
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
startActivityForResult(takePictureIntent, actionCode);
Why is it null, and how can I solve it?
It happens the same to me, if you are providing MediaStore.EXTRA_OUTPUT, then the intent is null, but you will have the photo in the file you provided (Uri.fromFile(f)).
If you don't specify MediaStore.EXTRA_OUTPUT then you will have an intent which contains the uri from the file where the camera has saved the photo.
Don't know if it as a bug, but it works that way.
EDIT: So in onActivityResult() you no longer need to check for data if null. The following worked with me:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case PICK_IMAGE_REQUEST://actionCode
if (resultCode == RESULT_OK && data != null && data.getData() != null) {
//For Image Gallery
}
return;
case CAPTURE_IMAGE_REQUEST://actionCode
if (resultCode == RESULT_OK) {
//For CAMERA
//You can use image PATH that you already created its file by the intent that launched the CAMERA (MediaStore.EXTRA_OUTPUT)
return;
}
}
}
Hope it helps
A sample written in Kotlin. You create a Uri for camera app, CameraFragment holds it until camera returns from saving your picture and gives it back to you in onActivityResult as you would expect.
CameraFragment.kt
Acts as an intermediary between consumer and camera app. Takes Uri as input and returns it in data Intent.
class CameraFragment : Fragment() {
companion object {
val TAG = CameraFragment::class.java.simpleName
private val KEY_URI = ".URI"
fun newInstance(uri: Uri, targetFragment: Fragment, requestCode: Int): CameraFragment {
val args = Bundle()
args.putParcelable(KEY_URI, uri)
val fragment = CameraFragment()
fragment.arguments = args
fragment.setTargetFragment(targetFragment, requestCode)
return fragment
}
}
private lateinit var uri: Uri
private var fired = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
fired = savedInstanceState?.getBoolean("fired") ?: false
if (!fired) {
val args = arguments
uri = args.getParcelable(KEY_URI)
val i = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
i.putExtra(MediaStore.EXTRA_OUTPUT, uri)
i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
context.grantUriPermission(i, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
startActivityForResult(i, targetRequestCode)
fired = true
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean("fired", fired)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == targetRequestCode) {
context.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
val newData = Intent()
newData.data = uri
targetFragment.onActivityResult(requestCode, resultCode, newData)
dismiss()
}
}
private fun dismiss() {
fragmentManager.beginTransaction().remove(this).commit()
}
}
/** Grant Uri permissions for all camera apps. */
fun Context.grantUriPermission(intent: Intent, uri: Uri, modeFlags: Int) {
val resolvedIntentActivities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (resolvedIntentInfo in resolvedIntentActivities) {
val packageName = resolvedIntentInfo.activityInfo.packageName;
grantUriPermission(packageName, uri, modeFlags);
}
}
Invoke camera intent
this is a fragment in your app which will trigger the camera. RC_CAMERA is your request code for this action.
val uri = /* Your output Uri. */
val f = CameraFragment.newInstance(uri, this, RC_CAMERA)
fragmentManager.beginTransaction().add(f, CameraFragment.TAG).commit()
Handle camera result
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode) {
RC_CAMERA -> {
if (resultCode == Activity.RESULT_OK) {
val uri = data?.data
// Do whatever you need.
}
}
}
}
Where did you create the ffor the Uri.fromFile(f)?
It must be a valid File object. Try to create it before the EXTRA_OUTPUT line.
File f = new File("valid path");
Try with something like this:
File file = new File(dataFile);
Uri outFileUri = Uri.fromFile(file);
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, outFileUri);
startActivityForResult(intent, TAKE_PHOTO);
use the following:
Bitmap bitmap = data.getExtras().getParcelable("data");
it works.

Categories

Resources