Android: Fragment is not receiving RESULT_OK in Activity result contract - android

I am using ActivityResultContract API in my fragment. Fragment A is in Activity A, from where Activity B is called which has Fragment B. Code in Fragment A is as follows:
val makePaymentContract = registerForActivityResult(CustomContract()) { uri: Uri? ->
Log.d("FragmentA", "DATA URI: ${uri.toString()}")
}
This is custom contract:
class CustomContract : ActivityResultContract<DataHolder, Uri>() {
override fun createIntent(context: Context, input: DataHolder?): Intent {
val intent = Intent(context, ActivityB::class.java)
intent.putExtra("data1", input?.data1)
intent.putExtra("data2", input?.data2)
intent.putExtra("data3", input?.data3)
intent.putExtra("data4", input?.data4)
return intent
}
override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
Log.d("CustomContract", "Result Code $resultCode")
when (resultCode) {
Activity.RESULT_OK -> return intent?.data // Return the data
else -> return null
}
}
}
where DataHolder is a simple data class. Below code is in Fragment B(which is in Activity B)
Uri uri = Uri.parse("custom://thank_you?id=" + id + "&amt_paid=" +
totalPaidAmt + "&order_type=" + orderType);
Intent intent = new Intent();
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
requireActivity().finish();
The problem is FragmentA always receives null uri. Upon debugging, found out returned resultCode is always RESULT_CANCELED. What am I doing wrong?

Found out the mistake. I was not setting result from Fragment B
Uri uri = Uri.parse("custom://thank_you?id=" + id + "&amt_paid=" +
totalPaidAmt + "&order_type=" + orderType);
Intent intent = new Intent();
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
requireActivity().setResult(Activity.RESULT_OK, intent);
requireActivity().finish();

Related

Android 11 ActivityResult throwing only RESULT_CANCELED

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
)
)
}

startActivityForResult putExtras being recieved as NULL

Hi I am creating a SideActivity to gather some results and pass them back to Main Activity. However, the two strings value from the putextra are NULL rather than the string "20" and the jamSize "medium". Is there a way of passing the data properly?
Here in my Main Activity I have a setOnClickListener and a onActivityResult function.
jamButton.setOnClickListener {
var intent = Intent(this#MainActivity, SideActivity::class.java)
intent.putExtra("jamName", "raspberry")
intent.putExtra("jamPrice", "12.00")
startActivityForResult(intent, 1) // passing request code value 1
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == RESULT_OK) {
val jamPrice:String = intent.getStringExtra("jamPrice").toString()
val jamSize:String = intent.getStringExtra("jamSize").toString()
val newJam = DataModel("Jam", "$jamSize", "$jamPrice")
list.add(0, newJam)
jamAdapter.notifyItemInserted(0)
}
}
Here is my Second Activity
completeBtn.setOnClickListener {
val jamPrice: String = textView2.text.toString()
val jamSize: String = textView3.text.toString()
val intent = Intent(this#SideActivity, MainActivity::class.java)
intent.putExtra("jamPrice", "20.00")
intent.putExtra("jamSize", jamSize)
setResult(Activity.RESULT_OK, intent)
finish()
}
In your onActivityResult don't use this :
val jamPrice:String = intent.getStringExtra("jamPrice").toString()
because the intent variable is the Intent of the activity instead use :
val jamPrice: String = data?.getStringExtra("jamPrice").toString()

how pass data between more than 2 activities Kotlin

I need to pass multiple variables between more than two activities.
I know how to pass between one to another, but not from the first to the third.
Activity One
btnNext.setOnClickListener {
val i = Intent(this, Activity2::class.java)
val variableOne= txtVariableOne.text.toString()
i.putExtra("VARIABLEONE",variableOne)
startActivity(i)
}
Activity TWO
btnNext.setOnClickListener {
val i = Intent(this, Activity3::class.java)
val VariableTwo = txtVariableTwo.text.toString()
i.putExtra("VARIABLETWO",variableTwo)
startActivity(i)
}
Activity THREE (This is what I need)
Log.d("DEBUG ", "Value: " + intent.getStringExtra("VARIABLEONE"))
Log.d("DEBUG ", "Value: " + intent.getStringExtra("VARIABLETWO"))
I need access VariableOne and VariableTwo from activity Three.
Thank
Activity TWO
btnNext.setOnClickListener {
val i = Intent(this, Activity3::class.java)
val VariableTwo = txtVariableTwo.text.toString()
//THIS WHAT YOU NEED:
i.putExtra("VARIABLEONE",intent.getStringExtra("VARIABLEONE"));
i.putExtra("VARIABLETWO",variableTwo)
startActivity(i)
}
Activity Two
override fun viewAllNotes() {
val intent : Intent = Intent(this, ViewAllNotesActivity::class.java)
intent .putExtra("VARIABLEONE",variableOne)
startActivityForResult(intent, VIEW_ALL_REQUEST_CODE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK){
if (requestCode == VIEW_ALL_REQUEST_CODE){
//get data here
}
}
}
}
Activity three
#Override
public void onBackPressed() {
Intent result = new Intent();
result .putExtra("VARIABLEONE",variableOne)
setResult(Activity.RESULT_OK, result);
this.finish();
}
Or else you can store the values using Shared-preference and get values any where in the project
I can say the same as we passing in java
There are many ways
From the one is, You can pass by intent when starting another activity which is proper when you think data at the time of starting the activity like bellow,
val intent = Intent(mActivity, LoginActivity::class.java);
intent.putExtra("keyName", data)
startActivity(intent)
Another way you can use preference or store data in preference and you can use where ever you need.

What can I use instead of onActivityResult in Fragment?

Crash What can i use instead of onActivityResult in Fragment?
i used this code on inCreate
Kotlin
photo.setOnClickListener {
val intent = Intent(Intent.ACTION_PICK)
intent.type = "image/"
startActivityForResult(intent, 0)
}
i wanna receive its result such like that,but in fragment
Kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 0 && resultCode == Activity.RESULT_OK && data != null) {
selectedPhotoUri = data.data
val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, selectedPhotoUri)
val bitmapDrawable = BitmapDrawable(bitmap)
photo.setBackgroundDrawable(bitmapDrawable)
if (selectedPhotoUri == null) return#onActivityResult
val filename = UUID.randomUUID().toString()
val ref = FirebaseStorage.getInstance().getReference("/images/$filename")
ref.putFile(selectedPhotoUri!!)
.addOnSuccessListener {
ref.downloadUrl.addOnSuccessListener {
uri = it.toString()
}
}
.addOnFailureListener {
}
}
}
your stack trace says when calling photo.setOnClickListener the photo is null. your problem is not relevant to onActivityResult just set photo like this before calling photo.setOnClickListener:
photo = findViewById(R.id.photo_id);// replace photo_id with id of photo
photo.setOnClickListener {
val intent = Intent(Intent.ACTION_PICK)
intent.type = "image/"
startActivityForResult(intent, 0)
}
you said:
this code is running if i use Activity NOT fragment
First you have to call startActivityForResult() from fragment to receive its result in that fragment.
Second when calling startActivityForResult() first onActivityResult() of Activity is called then onActivityResult() of fragment BUT if you have overridden the onActivityResult() of activity and not calling super.onActivityResult() the onActivityResult() of fragments will not be called
Thus do not override onActivityResult() from activity or if you have overridden it and also want the onActivityResult() of fragment to be called just call super.onActivityResult() in onActivityResult() of activity.
If you can not go this way you can simply move your code to an optional method of fragment then call that method from onActivityResult of Activity.

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