Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to replace startActivityForResult with Activity Result APIs?

I have a main activity which serves as an entry point to call different activities, depending on condition. Among others, I use Firebase Auth to manage user sign in:

startActivityForResult(
            AuthUI.getInstance().createSignInIntentBuilder()
                    .setAvailableProviders(providers)
                    .build(),
            RC_SIGN_IN)

I overwrite onActivityResult() to distinguish the returned intent/data, for example:

 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {

        REQUEST_CODE_1 -> {
          // update UI and stuff
        }

        RC_SIGN_IN -> {
          // check Firebase log in
        }
        // ...
    }
}

With the Activity Result APIs which is strongly recommended by the documentation, I get that I should make prepareCall() before ActivityResultLauncher and to make sure the activity is in created state when I launch, but I still don't understand how to handle multiple activity results gracefully (at least, in one place) like in onActivityResult().

Looking at this article, it seems I need to implement multiple child inner classes of ActivityResultContract type (therefore multiple prepareCall()'s?), because they are suppose to be different contracts, am I correct? Can someone please show me some skeleton example that mirrors the above onActivityResult() logic?

like image 644
Neoh Avatar asked Apr 27 '20 09:04

Neoh


People also ask

What can I use instead of startActivityForResult?

But recently startActivityForResult() method is deprecated in AndroidX. Android came up with ActivityResultCallback (also called Activity Results API) as an alternative for it.

What can I use instead of startActivityForResult in Kotlin?

In this article, we will discuss the Activity Result API, an alternative to the deprecated startActivityForResult + onActivityResult methods. We will use Kotlin in this article for the code samples. Android introduced the Activity Result APIs as a major change in the androidx. activity:activity:1.2.

Can startActivityForResult still be used?

From now, startActivityForResult() has been deprecated so use new method instead of that.

How can I get result from startActivityForResult?

First you use startActivityForResult() with parameters in the first Activity and if you want to send data from the second Activity to first Activity then pass the value using Intent with the setResult() method and get that data inside the onActivityResult() method in the first Activity .


4 Answers

You can call as many Activities for result as you wish and have separate callback for each:

    val startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult())
    { result: ActivityResult ->
        if (result.resultCode == Activity.RESULT_OK) {
            //  you will get result here in result.data
            }

        }
    }

    startForResult.launch(Intent(activity, CameraCaptureActivity::class.java))

You just need to specify Activity class - CameraCaptureActivity::class.java

Update:

The prepareCall() method has been renamed to registerForActivityResult() in Activity 1.2.0-alpha04 and Fragment 1.3.0-alpha04. And it should be startForResult.launch(...) in the last line

Thanks Rafael Tavares for update

like image 99
Misha Akopov Avatar answered Oct 22 '22 17:10

Misha Akopov


From now, startActivityForResult() has been deprecated so use new method instead of that.

Example

public void openActivityForResult() {
    
 //Instead of startActivityForResult use this one
        Intent intent = new Intent(this,OtherActivity.class);
        someActivityResultLauncher.launch(intent);
    }


//Instead of onActivityResult() method use this one

    ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            new ActivityResultCallback<ActivityResult>() {
                @Override
                public void onActivityResult(ActivityResult result) {
                    if (result.getResultCode() == Activity.RESULT_OK) {
                        // Here, no request code
                        Intent data = result.getData();
                        doSomeOperations();
                    }
                }
            });
like image 33
Hardik Hirpara Avatar answered Oct 22 '22 15:10

Hardik Hirpara


First, don’t forget to add this to your Gradle dependency

implementation 'androidx.activity:activity-ktx:1.2.0-alpha05'
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha05'

Second, create your result contract by extending an abstract class called ActivityResultContract<I, O>. I mean the type of input and O means the type of output. And then you only need to override 2 methods

class PostActivityContract : ActivityResultContract<Int, String?>() {

    override fun createIntent(context: Context, input: Int): Intent {
        return Intent(context, PostActivity::class.java).apply {
            putExtra(PostActivity.ID, postId)
        }
    }

    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        val data = intent?.getStringExtra(PostActivity.TITLE)
        return if (resultCode == Activity.RESULT_OK && data != null) data
        else null
    }
}

And finally, the last step is registering the contract to Activity. You need to pass your custom contract and callback into registerForActivityResult.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
      
        start_activity_contract.setOnClickListener {
            openPostActivityCustom.launch(1)
        }
    }
  
    // Custom activity result contract
    private val openPostActivityCustom =
        registerForActivityResult(PostActivityContract()) { result ->
            // parseResult will return this as string?                                              
            if (result != null) toast("Result : $result")
            else toast("No Result")
        }
}

For More info check this Post

like image 20
Krishna Sony Avatar answered Oct 22 '22 15:10

Krishna Sony


In this case, what was returned by AuthUI was already an Intent, so, we use it like in the example below.

private val startForResult =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            when(result.resultCode){
                RESULT_OK -> {
                    val intent = result.data
                    // Handle the Intent...
                    mUser = FirebaseAuth.getInstance().currentUser
                }
                RESULT_CANCELED -> {

                } else -> {
            } }
        }

start the activity from anywhere (for example on button click) using:

 AuthUI.getInstance().createSignInIntentBuilder().setAvailableProviders(providers)
            .build().apply {
                startForResult.launch(this)
            }
like image 5
nyxee Avatar answered Oct 22 '22 17:10

nyxee