524

I recently discovered that onActivityResult is deprecated. What should we do to handle it?

Any alternative introduced for that?

Image showing code with onActivityResult striked out, indicating deprecation

Zoe
  • 27,060
  • 21
  • 118
  • 148
Amir Hossein Ghasemi
  • 20,623
  • 10
  • 57
  • 53
  • 1
    if I remove it an error lint appeared to add super call! – Amir Hossein Ghasemi Jul 01 '20 at 08:55
  • 43
    I don't know if there's ever been a deprecation that was un-deprecated but I'm holding out hope for `startActivityForResult`. This new way overly complicates the code and reduces readability. – spartygw Mar 05 '21 at 19:45
  • 1
    It's hard to test the thing now :( – acmpo6ou Apr 17 '21 at 11:53
  • 4
    I can see why Google decided to go with this route, it's trying to decouple `startActivityForResult` from the view lifecycle. I just wished that there's a more elegant way of doing this. – Morgan Koh Jul 06 '21 at 02:22
  • The [docs](https://developer.android.com/reference/android/app/Activity#startActivityForResult(android.content.Intent,%20int)) don't show it as deprecated. – grolschie Nov 16 '21 at 23:49
  • Please check this official documentation. Hope this helps you. https://developer.android.com/training/basics/intents/result – Yohannes Masterous Dec 31 '21 at 08:16
  • 1
    startActivityForResult also seems deprecated. Is it? – Mansi Raval Feb 10 '22 at 13:16
  • If anyone looking for Xamarin android solution : https://www.appliedcodelog.com/2022/02/startactivityforresult-onactivityresult.html – Suchith Apr 09 '22 at 09:17
  • Anyone know at what api level this function was deprecated ? – RonTLV Sep 15 '22 at 14:00
  • @grolschie They do. https://developer.android.com/reference/androidx/fragment/app/Fragment.html#startActivityForResult(android.content.Intent,int) – The incredible Jan Apr 17 '23 at 08:21

30 Answers30

745

A basic training is available at developer.android.com.

Here is an example on how to convert the existing code with the new one:

The old way:

public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    startActivityForResult(intent, 123);
}

@Override
protected void onActivityResult (int requestCode, int resultCode, Intent data) {
    if (resultCode == Activity.RESULT_OK && requestCode == 123) {
        doSomeOperations();
    }
}

The new way (Java):

public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    someActivityResultLauncher.launch(intent);
}

// You can do the assignment inside onAttach or onCreate, i.e, before the activity is displayed
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                if (result.getResultCode() == Activity.RESULT_OK) {
                    // There are no request codes
                    Intent data = result.getData();
                    doSomeOperations();
                }
            }
        });

The new way (Kotlin):

fun openSomeActivityForResult() {
    val intent = Intent(this, SomeActivity::class.java)
    resultLauncher.launch(intent)
}

var resultLauncher = registerForActivityResult(StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        // There are no request codes
        val data: Intent? = result.data
        doSomeOperations()
    }
}

EDIT. A better approach would be to make it more generalised so that we can reuse it. The snippet below is used in one of my projects but beware that it's not well-tested and may not cover all the cases.

BetterActivityResult.java

import android.content.Intent;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCaller;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContract;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class BetterActivityResult<Input, Result> {
    /**
     * Register activity result using a {@link ActivityResultContract} and an in-place activity result callback like
     * the default approach. You can still customise callback using {@link #launch(Object, OnActivityResult)}.
     */
    @NonNull
    public static <Input, Result> BetterActivityResult<Input, Result> registerForActivityResult(
            @NonNull ActivityResultCaller caller,
            @NonNull ActivityResultContract<Input, Result> contract,
            @Nullable OnActivityResult<Result> onActivityResult) {
        return new BetterActivityResult<>(caller, contract, onActivityResult);
    }

    /**
     * Same as {@link #registerForActivityResult(ActivityResultCaller, ActivityResultContract, OnActivityResult)} except
     * the last argument is set to {@code null}.
     */
    @NonNull
    public static <Input, Result> BetterActivityResult<Input, Result> registerForActivityResult(
            @NonNull ActivityResultCaller caller,
            @NonNull ActivityResultContract<Input, Result> contract) {
        return registerForActivityResult(caller, contract, null);
    }

    /**
     * Specialised method for launching new activities.
     */
    @NonNull
    public static BetterActivityResult<Intent, ActivityResult> registerActivityForResult(
            @NonNull ActivityResultCaller caller) {
        return registerForActivityResult(caller, new ActivityResultContracts.StartActivityForResult());
    }

    /**
     * Callback interface
     */
    public interface OnActivityResult<O> {
        /**
         * Called after receiving a result from the target activity
         */
        void onActivityResult(O result);
    }

    private final ActivityResultLauncher<Input> launcher;
    @Nullable
    private OnActivityResult<Result> onActivityResult;

    private BetterActivityResult(@NonNull ActivityResultCaller caller,
                                 @NonNull ActivityResultContract<Input, Result> contract,
                                 @Nullable OnActivityResult<Result> onActivityResult) {
        this.onActivityResult = onActivityResult;
        this.launcher = caller.registerForActivityResult(contract, this::callOnActivityResult);
    }

    public void setOnActivityResult(@Nullable OnActivityResult<Result> onActivityResult) {
        this.onActivityResult = onActivityResult;
    }

    /**
     * Launch activity, same as {@link ActivityResultLauncher#launch(Object)} except that it allows a callback
     * executed after receiving a result from the target activity.
     */
    public void launch(Input input, @Nullable OnActivityResult<Result> onActivityResult) {
        if (onActivityResult != null) {
            this.onActivityResult = onActivityResult;
        }
        launcher.launch(input);
    }

    /**
     * Same as {@link #launch(Object, OnActivityResult)} with last parameter set to {@code null}.
     */
    public void launch(Input input) {
        launch(input, this.onActivityResult);
    }

    private void callOnActivityResult(Result result) {
        if (onActivityResult != null) onActivityResult.onActivityResult(result);
    }
}

With the above approach, you still have to register it before or during launching the activity or fragment attachment. Once defined, it can be reused within the activity or fragment. For example, if you need to start new activities in most of the activity, you can define a BaseActivity and register a new BetterActivityResult like this:

BaseActivity.java

public class BaseActivity extends AppCompatActivity {
    protected final BetterActivityResult<Intent, ActivityResult> activityLauncher = BetterActivityResult.registerActivityForResult(this);
}

After that, you can simply launch an activity from any child activities like this:

public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    activityLauncher.launch(intent, result -> {
        if (result.getResultCode() == Activity.RESULT_OK) {
            // There are no request codes
            Intent data = result.getData();
            doSomeOperations();
        }
    })
}

Since you can set the callback function along with the Intent, you can reuse it for any activities.

Similarly, you can also use other activity contracts using the other two constructors.

GeneCode
  • 7,545
  • 8
  • 50
  • 85
Muntashir Akon
  • 8,740
  • 2
  • 27
  • 38
  • 564
    The new way looks unnecessarily complicated compared to the old way... – drmrbrewer Sep 11 '20 at 11:34
  • 9
    @drmrbrewer it removes the hassle of maintaining request codes and adds the ability to run tests. But I think its main problem is reusability. In my opinion `ActivityResultCallback` should be a part of `ActivityResultLauncher#launch()` so that we can reuse it for other purposes, eg., if I'd needed to check storage permissions for multiple actions, I could've use a single `ActivityResultContracts.RequestPermission()` for all of them. You could implement `ActivityResultCallback` in an attempt to solve this but that will be worse than the old way since you need something similar to request codes. – Muntashir Akon Sep 11 '20 at 18:18
  • 2
    If my activity is starting 2 new activties for result then how would I differentiate it on onActivityResult? – Bhargav Thanki Jan 22 '21 at 07:07
  • 3
    The only convenience that I see is removing `result codes`. We had to add different constants for every activity. Sometimes we could mix and two result codes became equal. That could lead to different bugs. – CoolMind Jan 25 '21 at 10:51
  • 6
    @drmrbrewer, agree. They made an unnecessaty tool that broke usual multi-activity application (especially MVC). Now we have to create many callbacks instead of one `onActivityResult`. One activity contains 10 fragments, some of them can start other activities and return result. Before we simply returned `setResult()` in fragment and handled in `onActivityResult`. Now we have to create many callbacks. – CoolMind Jan 25 '21 at 13:32
  • 123
    The new way is way worse than the old. It breaks code modularity and forces you to use much more codelines to cover use cases of previous version. Deprecation should be used when you provide better API design, but at Google are deprecated stuff arbitrarely on the base of opinable decisions not technically justified that look based on few use cases. – AndreaF Jan 28 '21 at 13:18
  • 3
    @AndreaF Yeah, I was absolutely shocked when Google deprecated `ViewPager` in favour of `ViewPager2` which created more problems than solving anything. – Muntashir Akon Jan 28 '21 at 13:56
  • 2
    @MuntashirAkon The list is very long, not talking about the SAF-maggedon, and not only on Android platform, and without any regard of backward compatibility of API interfaces, to the point that now I avoid to use any Google backed framework when possible, to avoid to face future hassles and waste of time in codebase migrations. – AndreaF Jan 28 '21 at 14:38
  • with help of lambda Java version can be reduced to Kotlin – Isaak Osipovich Dunayevsky Feb 25 '21 at 09:08
  • 1
    What if activity is started by a library? – YaMiN Mar 10 '21 at 07:51
  • @YaMiN The library has to provide some sort of initialiser that must be called before or during activity creation or fragment attachment. – Muntashir Akon Mar 11 '21 at 06:43
  • 89
    Was the dramatic change really necessary?! Google keeps changing things like some kind of baby diapers! – AtomX Apr 09 '21 at 10:34
  • 25
    Note that [as per the documentation](https://developer.android.com/training/basics/intents/result#register), that "BetterActivityResult" solution absolutely should never be used - the whole reason the `onActivityResult` is a separate callback and not a lambda you set at `launch` time is that it needs to exist after a configuration change or process death/recreation (both of which can happen while the other activity is open - just rotate your device for the simple case). Your lambda based approach will never, ever be able to handle those cases correctly. – ianhanniballake Jun 01 '21 at 04:00
  • @ianhanniballake Correct, the results may not be delivered correctly due to lifecycle issues. However, since `BetterActivityResult` also needs to be initialised during or before fragment/activity creation, there might be a way to handle them internally within the class. – Muntashir Akon Jun 01 '21 at 08:48
  • 18
    I'm surprised this API isn't called `startActivityForResult2`. If you think working with result codes was tedious, just wait till you get a load of this hot mess. – rmirabelle Jun 03 '21 at 17:26
  • I found this ZXing example of the new way https://github.com/journeyapps/zxing-android-embedded/issues/628 – Foxhunt Jun 14 '21 at 23:16
  • some times result code getting 0 instead -1 – PNR Jun 30 '21 at 07:06
  • 1
    @drmrbrewer : what happens if we keep using the old way, it seems to be working still and not getting caught in code inspection : code maturity – DragonFire Jul 17 '21 at 02:03
  • 5
    Wow Google!, Thanks for this new complex way! the code was much easier before. improved result code against re-usability... – Hardik Aug 09 '21 at 10:16
  • Leaking 'this' in constructor of non-final class BaseActivity – Iman Marashi Aug 27 '21 at 07:45
  • JAVA users: It's more complicated. KOTLIN users: It's easier. – Shubham Gupta Sep 20 '21 at 17:38
  • 1
    @ShubhamGupta It would be almost the same if you set 1.8 compatibility (lamda functions) in Java. – Muntashir Akon Sep 22 '21 at 05:43
  • 1
    So instead of request code now you will have name of the function to call. – DragonFire Oct 03 '21 at 09:40
  • 3
    Trying to figure out how to migrate this deprecated code to the new method in Kotlin. However, I am still not understanding how you migrate the `requestCode` situation, when there is no replacement for it or at least I see no clear one. @ShubhamGupta I am using Kotlin. Care to explain how to replace dependence on `requestCode`? – Akito Oct 07 '21 at 17:51
  • A more handy piece of code as a generalized solution: https://github.com/pseusys/contribution/blob/main/kotlin/android/app/src/main/java/me/pseusys/contribution/ResultManagers.kt – pseusys Nov 19 '21 at 20:56
  • 1
    There is no request code. How do I know where the result is coming from? – Anga Nov 23 '21 at 11:08
  • 1
    @Anga There is no concept of request code in the new design. It is expected that for each separate Intent, you will register a separate callback. This way, the activity/fragment knows where the result has to be sent. You can create alternatives like the one I've outlined above but as others have said, this new approach is totally useless and does not attempt to solve the actual issues. – Muntashir Akon Dec 10 '21 at 13:25
  • @MilTy This, like `BetterActivityResult`, would fall into the same trap if the device configurations have changed while the activity is running. A solution is to reintroduce request codes but only internally. (I will update my post to fix the issue later on.) – Muntashir Akon Dec 10 '21 at 13:50
  • @MuntashirAkon what kind of device configurations do you mean? – pseusys Dec 10 '21 at 16:58
  • @MilTy Changing screen size, orientation, etc. – Muntashir Akon Dec 10 '21 at 18:34
  • Change the existing working component which was super easy and working as expected and put it in the training/basic section, thanks google! – silentsudo Dec 11 '21 at 13:28
  • 2
    How i pass request code ? – Bipin Bharti Dec 17 '21 at 08:24
  • I have an issue: I have two activities: from the first one starts the second one by calling this BetterActivityResult functionality. Everythings going well until I rotate the device screen after second activity was launched. In this case I get result.getResultCode() = RESULT_CANCELLED - ALWAYS!!! And ofcourse doSomeOperations(); is nevere run. How I can resolve this issue? – Vitaly Feb 03 '22 at 11:05
  • Xamarin android solution https://www.appliedcodelog.com/2022/02/startactivityforresult-onactivityresult.html – Suchith Feb 23 '22 at 12:38
  • 1
    This remindes me the camera2 api :) – Uriel Frankel Mar 14 '22 at 07:45
  • 1
    Does anyone understand what is the benefit of the new API in terms of robustness? My tests show that when there's a configuration change after `startActivityForResult`, `onActivityResult` is correctly called on the *new* Activity instance (not the destroyed one). – Sébastien Mar 17 '22 at 14:30
  • 1
    Benefits: 1: You can now launch activities for result from fragments and receive the results in fragments without going through the activity. 2: Instead of distinguishing requests with fallible INT codes you now have strongly typed classes. 3: You can have separate callbacks for separate requests, eliminating the need for switch-case in response handling, There are probably more benefits. People should do some research before blindly upvoting "this looks more complicated" when it truly isn't (unless you're stuck in Java) – Nilzor Apr 19 '22 at 13:23
  • I used your "The new way (Java)" to pick a contact from the agenda and it works very well. Thanks – Marius Razvan Varvarei May 14 '22 at 13:26
  • 'Who' even gets to decided that this is now the 'new way' to do it? – 6rchid Aug 22 '22 at 00:35
  • Anyone know at what api level this function was deprecated ? – RonTLV Sep 15 '22 at 14:00
  • @RonTLV: This is shipped with the AndroidX libraries, not the Android SDKs. – Muntashir Akon Sep 19 '22 at 08:43
  • typical Google way of making perfectly well way of doing a thing unnecessarily complicated. I thought it was just me but good to know that others also feel the same. – nayakasu Mar 02 '23 at 10:44
  • Your code does not work in the same Activity, because it causes an infinite loop... – user2342558 Mar 19 '23 at 17:57
54

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

Kotlin Example

    fun openActivityForResult() {
        startForResult.launch(Intent(this, AnotherActivity::class.java))
    }


    val startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { 
    result: ActivityResult ->
        if (result.resultCode == Activity.RESULT_OK) {
            val intent = result.data
            // Handle the Intent
            //do stuff here
        }
    }
Filip Czaplicki
  • 189
  • 3
  • 12
Hardik Hirpara
  • 2,594
  • 20
  • 34
37

There are 4 simple steps to follow while replacing the deprecated method startActivityForResult(...).

  1. In place of overridden method onActivityResult(..) -

     ActivityResultLauncher<Intent> activityResultLaunch = registerForActivityResult(
             new ActivityResultContracts.StartActivityForResult(),
             new ActivityResultCallback<ActivityResult>() {
                 @Override
                 public void onActivityResult(ActivityResult result) {
                     if (result.getResultCode() == 123) {
                         // ToDo : Do your stuff...
                     } else if(result.getResultCode() == 321) {
                         // ToDo : Do your stuff...
                     }
                 }
    });
    

For multiple custom requests, append the condition as

if (result.getResultCode() == 123) {
..
} else if(result.getResultCode() == 131){
..
} // so on..
  1. Imports :

     import androidx.activity.result.ActivityResult;
     import androidx.activity.result.ActivityResultCallback;
     import androidx.activity.result.ActivityResultLauncher;
     import androidx.activity.result.contract.ActivityResultContracts;
    
  2. In place of startActivityForResult(intent, 123), use

     Intent intent = new Intent(this, SampleActivity.class);
     activityResultLaunch.launch(intent);
    
  3. In SampleActivity.java class, while returning back to source activity, code will remain the same like -

    Intent intent = new Intent();
    setResult(123, intent);
    finish();
    

Happy Coding! :)

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Abhijeet
  • 501
  • 4
  • 7
21

In Java 8 it can be written alike this:

ActivityResultLauncher<Intent> startActivityForResult = registerForActivityResult(
    new ActivityResultContracts.StartActivityForResult(),
    result -> {
        if (result.getResultCode() == AppCompatActivity.RESULT_OK) {
            Intent data = result.getData();
            // ...
        }
    }
);

Intent intent = new Intent( ... );
startActivityForResult.launch(intent);
Martin Zeitler
  • 1
  • 19
  • 155
  • 216
20

In KOTLIN I changed my code

startActivityForResult(intent, Constants.MY_CODE_REQUEST)

and

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode == Activity.RESULT_OK) {
        when (requestCode) {
            Constants.MY_CODE_REQUEST -> {
            ...
}

to

registerForActivityResult(StartActivityForResult()) { result ->
    onActivityResult(Constants.MY_CODE_REQUEST, result)
}.launch(intent)

and

private fun onActivityResult(requestCode: Int, result: ActivityResult) {
    if(result.resultCode == Activity.RESULT_OK) {
        val intent = result.data
        when (requestCode) {
            Constants.MY_CODE_REQUEST -> {
            ...

I hope it works for you. :D

Amir Hossein Ghasemi
  • 20,623
  • 10
  • 57
  • 53
Luis Moreno
  • 615
  • 6
  • 6
  • 3
    `onActivityResult` from your third code snippet on `registerForActivityResult` is deprecated. – Filipe Brito Feb 27 '21 at 18:57
  • 2
    @FilipeBrito onActivityResult is not an overwritten method, it is my own method, the name can be whatever ;) – Luis Moreno Mar 01 '21 at 17:24
  • 8
    the requestCode in the new way seems practically useless. – ThanosFisherman Apr 21 '21 at 09:38
  • 3
    This is not the right approach . If you set the launch together with the registerForActivityResult we might run into initialization error. It's always better to create a variable first and do the registration there. – Narendra_Nath Jun 22 '21 at 08:38
10

For those whose fragments have more than one requestCode, and if you are not sure how to handle multiple results by those requestCodes, you need to understand that requestCode is useless in the new approach.

I imagine the old way you code like this:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode == Activity.RESULT_CODE) {
        when (requestCode) {
            REQUEST_TAKE_PHOTO -> {
                // handle photo from camera
            }
            REQUEST_PICK_IMAGE_FROM_GALLERY -> {
                // handle image from gallery
            }
        }
    }
}

In the new API, you need to implement the result of every requests in a separate ActivityResultContract:

val takePhotoForResult = registerForActivityResult(StartActivityForResult()) { result: ActivityResult ->
    if (result.resultCode == Activity.RESULT_OK) {
        val intent = result.data
        // handle photo from camera
    }
}

val pickImageFromGalleryForResult = registerForActivityResult(StartActivityForResult()) { result: ActivityResult ->
    if (result.resultCode == Activity.RESULT_OK) {
        val intent = result.data
        // handle image from gallery
    }
}

Then you need to start those activities/intents like this:

private fun startTakePhotoActivity() {
    takePhotoForResult.launch(Intent(requireActivity(), TakePhotoActivity::class.java))
}

private fun pickImageFromGallery() {
    val pickIntent = Intent(Intent.ACTION_PICK)
    pickIntent.setDataAndType(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        "image/*"
    )
    pickImageFromGalleryForResult.launch(pickIntent)
}

By doing this, you can get rid of hundreds of const val REQUEST_ values in your project.

Egemen Hamutçu
  • 1,602
  • 3
  • 22
  • 34
  • 1
    You get rid of hundreds of const val REQUEST_ values in your project by replacing them by hundreds of ActivityResultContracts. Where's the advantage? – The incredible Jan Apr 17 '23 at 09:26
  • Managing the values of these `REQUEST_` integers takes time, and integers can be used for different purposes, messing up the project. However `ActivityResultContracts` are used only one purpose guiding the developer to the right point. That makes the difference. @TheincredibleJan – Egemen Hamutçu Apr 28 '23 at 12:55
9

onActivityResult, startActivityForResult, requestPermissions, and onRequestPermissionsResult are deprecated on androidx.fragment from 1.3.0-alpha04, not on android.app.Activity.
Instead, you can use Activity Result APIs with registerForActivityResult.

khcpietro
  • 1,459
  • 2
  • 23
  • 42
  • where do you get to know about this " Activity Result APIs" with "registerForActivityResult" whats the source ? – Sumit Oct 01 '21 at 11:03
  • @Sumit As I linked change log on text _deprecated_, Android team said 'Please use the Activity Result APIs' – khcpietro Oct 04 '21 at 00:28
7

Reference : Kotlin - Choose Image from gallery

The Simplest Alernative I've found so far

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.id.activity_main)

    var ivPhoto = findViewById<ImageView>(R.id.ivPhoto)
    var btnChoosePhoto = findViewById<Button>(R.id.btnChoosePhoto)

    

val getContent = registerForActivityResult(ActivityResultContracts.GetContent())  { uri: Uri? ->
            ivPhoto.setImageURI(uri)    // Handle the returned Uri
        }


    btnChoose.setOnClickListener {
        getContent.launch("image/*")
    }
    
    }
Allan_Aj5
  • 125
  • 1
  • 4
6

Here i explain the new way

private val scan =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult())
        { result: ActivityResult ->
            if (result.resultCode == AppCompatActivity.RESULT_OK && result.data != null) {

                var selected_hub = result!!.data!!.getParcelableExtra<ExtendedBluetoothDevice>(Utils.EXTRA_DEVICE)
                Log.d(TAG,"RECONNECT PROCESS "+selected_hub!!.name)
                reconnect(selected_hub!!)

            }
        }

call this from activity or fragment

private fun callScan() {
        val intent = Intent(requireActivity(), ScanningMeshDevices::class.java)
        scan.launch(intent)
    }
Anshul Nema
  • 281
  • 3
  • 3
5

The below code works in the Kotlin fragment to check the Bluetooth permission.

val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)

registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        // There are no request codes
        val data: Intent? = result.data
        bluetoothAdapter.enable()
        Toast.makeText(context, "Permission Granted: ", Toast.LENGTH_SHORT).show()
        dynamicButton()
    }
    else{Toast.makeText(context, "You have to enable bluetooth to use this app.", Toast.LENGTH_SHORT).show()}
    
}.launch(intent)
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
sandip
  • 394
  • 1
  • 4
  • 11
4

Here's my solution:

In our project, we had 20+ occurrences of startActivityForResult (and onActivityResult).

We wanted to change the code as little as possible (and keep using request codes), while introducing an elegant solution for future use.

Since lots of us, developers, use BaseActivity concept - why not take advantage of it?

Here is BaseActivity:

abstract class BaseActivity : AppCompatActivity()
{
    private var requestCode: Int = -1
    private var resultHandler: ActivityResultLauncher<Intent>? = null

    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)
        registerForActivityResult()
    }

    private fun registerForActivityResult()
    {
        if (shouldRegisterForActivityResult())
        {
            resultHandler = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->

                onActivityResult(result.data, requestCode, result.resultCode)
                this.requestCode = -1
            }
        }
    }

   fun startActivityForResult(requestCode: Int, intent: Intent)
   {
       this.requestCode = requestCode
       resultHandler?.launch(intent)
   }

   protected open fun onActivityResult(data: Intent?, requestCode: Int, resultCode: Int)
   {
       // For sub activities
   }

   protected open fun shouldRegisterForActivityResult(): Boolean
   {
      // Sub activities that need the onActivityResult "mechanism", should override this and return true
       return false
   }
}

Here is SubActivity:

class SubActivity : BaseActivity()
{
    companion object
    {
        private const val SOME_REQUEST_CODE = 300
    }

    private fun testActivityResult()
    {
        val intent = Intent(this, OtherActivity::class.java)
        startActivityForResult(SOME_REQUEST_CODE, intent)
    }

    override fun shouldRegisterForActivityResult(): Boolean
    {
        return true
    }

    override fun onActivityResult(data: Intent?, requestCode: Int, resultCode: Int)
    {
        if (requestCode == SOME_REQUEST_CODE)
        {
            // Yes!
        }
    }
}

Hope it helps someone

dor506
  • 5,246
  • 9
  • 44
  • 79
3

My goal was to reuse the current implementation of the startActivityForResult method with minimum code changes. For that purpose, I made a wrapper class and interface with an onActivityResultFromLauncher method.

interface ActivityResultLauncherWrapper {

    fun launchIntentForResult(activity: FragmentActivity, intent: Intent, requestCode: Int, callBack: OnActivityResultListener)

    fun unregister()

    interface OnActivityResultListener {
        fun onActivityResultFromLauncher(requestCode: Int, resultCode: Int, data: Intent?)
    }
}

class ActivityResultLauncherWrapperImpl : ActivityResultLauncherWrapper {
    private var weakLauncher: WeakReference<ActivityResultLauncher<Intent>>? = null

    override fun launchIntentForResult(
            activity: FragmentActivity,
            intent: Intent,
            requestCode: Int,
            callBack: ActivityResultLauncherWrapper.OnActivityResultListener
    ) {

        weakLauncher = WeakReference(
                activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
                    callBack.onActivityResultFromLauncher(requestCode, result.resultCode, result.data)
                }
        )

        weakLauncher?.get()?.launch(intent)
    }

    override fun unregister() {
        weakLauncher?.get()?.unregister()
    }
}

I am using Dagger in my project and I injected the wrapper where it is needed

@Inject
lateinit var activityResultLauncher: ActivityResultLauncherWrapper

But the wrapper also can be instantiated directly:

val activityResultLauncher = ActivityResultLauncherWrapper()

then you have to change the startActivityForResult method with launchIntentForResult. Here is example where it is called from a fragment:

activityResultLauncher.launchIntentForResult(
        requireActivity(),
        intent,
        REQUEST_CODE_CONSTANT,
        object: ActivityResultLauncherWrapper.OnActivityResultListener {
            override fun onActivityResultFromLauncher(requestCode: Int, resultCode: Int, data: Intent?) {
                /*do something*/
            }
        }
)

You will receive the result in the anonymous object. You could use OnActivityResultListener in a Fragment or an FragmentActivity if you implement the Interface and refactor the current implementation like this:

class MyFragment : Fragment(), OnActivityResultListener {
   
 ...
    
override fun onActivityResultFromLauncher(requestCode: Int, resultCode: Int, data: Intent?) {/*do somthing*/}

 ...

}

As we know, the Kotlin class ActivityResultLauncherWrapper could be used in java code as well. There are java classes in my project as well. There is an example with implementation of the callback interface in a Fragment:

public class MyFragment extends Fragment implements OnActivityResultListener {
    
...

    @Inject
    ActivityResultLauncherWrapper activityResultLauncher;
//ActivityResultLauncherWrapper activityResultLauncher = new ActivityResultLauncherWrapper()

...

public void launnchActivity(@NotNull Intent intent) {
        activityResultLauncher.launchIntentForResult(requireActivity(), intent, REQUEST_CODE_CONSTANT, this);
    }

...

 @Override
    public void onActivityResultFromLauncher(int requestCode, int resultCode, Intent data) {/*do somthing*/}
...
}

I hope this helps to build the solution for your case.

MeLean
  • 3,092
  • 6
  • 29
  • 43
3

You can use extension functions for Koltin. For example:

//random utils file
fun Fragment.buildGetContentRequest(function: (Uri) -> Unit): ActivityResultLauncher<String> {
    return this.registerForActivityResult(ActivityResultContracts.GetContent()) {
        function(it)
    }
}

fun Fragment.buildTakePhotoRequest(function: (Boolean) -> Unit): ActivityResultLauncher<Uri> {
    return this.registerForActivityResult(ActivityResultContracts.TakePicture()) {
        function(it)
    }
}

fun Fragment.buildSelectMultipleContentRequest(function: (MutableList<Uri>?) -> Unit): ActivityResultLauncher<String> {
    return this.registerForActivityResult(ActivityResultContracts.GetMultipleContents()) {
        function(it)
    }
}

And then in your fragment something like this

//your actual fragment logic
class YourFragment : Fragment() {
    //we can assign our request in init process
    private val mRequestSelectFiles = buildSelectMultipleContentRequest { 
        onFilesSelected(it) 
    }


    fun onSelectFiles() {
        val mime = "*/*"
        mRequestSelectFiles.launch(mime)
    }

    fun onFilesSelected(list: MutableList<Uri>?) {
        //your logic
    }
}
Dwane13
  • 190
  • 1
  • 2
  • 8
  • You will need to add these dependencies :- implementation "androidx.activity:activity-ktx:1.3.0" implementation "androidx.fragment:fragment-ktx:1.3.6" – Joshua Achoka Aug 01 '21 at 18:26
3

In my case I was trying to use the intent I was moving directly to the next activity without using the Google Sign In.

What worked for me :

Inside OnCreate set the onClickListener for the sign-in button :

     btnSignIn.setOnClickListener {
        signIn()
        }

    private fun signIn() {
        val intent = client.signInIntent
        mainActivityResultLauncher.launch(intent)
    }

In the above code I was writing the intent to go to the next activity but I had to write client.signInIntent

    var mainActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ result ->

        if(result.resultCode == Activity.RESULT_OK){
            val data = result.data
            val task = GoogleSignIn.getSignedInAccountFromIntent(data)
            try {
                // Google Sign In was successful, authenticate with Firebase
                val account = task.getResult(ApiException::class.java)!!
                Log.d(TAG, "firebaseAuthWithGoogle:" + account.id)
                firebaseAuthWithGoogle(account.idToken!!)
            } catch (e: ApiException) {
                // Google Sign In failed, update UI appropriately
                Log.w(TAG, "Google sign in failed", e)
            }
        }
    }
oyeraghib
  • 878
  • 3
  • 8
  • 26
3

This was what how I replaced multiple requestCodes (put this code in your Activity):

    ActivityResultLauncher<Intent> launchCameraActivity = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                if (result.getResultCode() == Activity.RESULT_OK) {
                    Intent data = result.getData();
                    Bitmap photoBitmap;
                    if(data != null && data.getExtras() != null){
                        photoBitmap = (Bitmap) data.getExtras().get("data");
                        if (photoBitmap != null) {
                            dataModel.setPhoto(ImageUtil.convert(photoBitmap));
                            imageTaken.setVisibility(View.VISIBLE);
                            imageTaken.setImageBitmap(photoBitmap);
                        }

                    }
                }
            }
        });

ActivityResultLauncher<Intent> launchCameraAndGalleryActivity = registerForActivityResult(
    new ActivityResultContracts.StartActivityForResult(),
    new ActivityResultCallback<ActivityResult>() {
        @Override
        public void onActivityResult(ActivityResult result) {
            if (result.getResultCode() == Activity.RESULT_OK) {
                
                Intent data = result.getData();
                Uri imageUri;
                if (data != null) {
                    imageUri = data.getData();
                    InputStream imageStream;
                    try {
                        imageStream = getContentResolver().openInputStream(imageUri);
                        Bitmap photoBitmap = BitmapFactory.decodeStream(imageStream);
                        dataModel.setOtherImage(ImageUtil.convert(photoBitmap));
                        documentImageTaken.setVisibility(View.VISIBLE);
                        documentImageTaken.setImageBitmap(photoBitmap);
                    }catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    });

I launch the activities like this:

                    Intent photoIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
                launchCameraAndGalleryActivity.launch(photoIntent );

Intent galleryIntent= new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    launchCameraActivity.launch(galleryIntent);
3

I figured how to do it properly from a Fragment in Kotlin, to capture an image and handle returned bitmap. It is pretty much the same in other cases too.

First, you have to register the fragment to listen for the activity results. This has to be done before initiating the fragment, which means creating a member variable instead of initiating within onCreate function.

class DummyFragment : Fragment() {

  //registering fragment for camera listener
  private val takePhoto = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
  ) {
    if (it.resultCode == Activity.RESULT_OK) {
      val imageBitmap = it.data?.extras?.get("data") as Bitmap
      // do your thing with the obtained bitmap
    }
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
  }

}

Then, call the camera intent as you would normally do. And use this above-created variable to launch the intent.

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  someRandomButton.setOnClickListener {
    val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    takePhoto.launch(takePictureIntent)
  }
}
VeeyaaR
  • 291
  • 4
  • 7
2

It seems that onActivityResult is deprecated in the super class but you did not mention the super class name and compileSdkVersion here in your question.

In Java and Kotlin every class or method could be marked as deprecated simply by adding @Deprecated to it so check your super class you may extend a wrong class.

When a class is deprecated all of its methods are deprecated too.

To see a quick solution click on deprecated method and press Ctrl+Q in Android studio to see documentation of method there should be a solution for it.


In my project using androidx and API 29 as compileSdkVersion, this method is NOT deprecated in activities and fragments

ygngy
  • 3,630
  • 2
  • 18
  • 29
  • 2
    Since now, in project, using androidx and API 29 as compileSdkVersion, this method is deprecated too. – Vlad Dec 24 '20 at 09:54
2

Kotlin version of @Muntashir Akon solution

class BetterActivityResult<Input, Result> private constructor(
  caller : ActivityResultCaller,
  contract : ActivityResultContract<Input, Result>,
  var onActivityResult : ((Result) -> Unit)?,
) {

private val launcher : ActivityResultLauncher<Input> =
   caller.registerForActivityResult(contract) { onActivityResult?.invoke(it) }

  /**
   * Launch activity, same as [ActivityResultLauncher.launch] except that it 
   * allows a callback
   * executed after receiving a result from the target activity.
   */
  /**
   * Same as [.launch] with last parameter set to `null`.
   */
  @JvmOverloads
  fun launch(
     input : Input,
     onActivityResult : ((Result) -> Unit)? = this.onActivityResult,
  ) {
    this.onActivityResult = onActivityResult
    launcher.launch(input)
  }

  companion object {
  /**
   * Register activity result using a [ActivityResultContract] and an in-place 
   * activity result callback like
   * the default approach. You can still customise callback using [.launch].
   */
  fun <Input, Result> registerForActivityResult(
    caller : ActivityResultCaller,
    contract : ActivityResultContract<Input, Result>,
    onActivityResult : ((Result) -> Unit)?,
  ) : BetterActivityResult<Input, Result> {
    return BetterActivityResult(caller, contract, onActivityResult)
  }

  /**
   * Same as [.registerForActivityResult] except
   * the last argument is set to `null`.
   */
  fun <Input, Result> registerForActivityResult(
    caller : ActivityResultCaller,
    contract : ActivityResultContract<Input, Result>,
  ) : BetterActivityResult<Input, Result> {
    return registerForActivityResult(caller, contract, null)
  }

  /**
   * Specialised method for launching new activities.
   */
  fun registerActivityForResult(
    caller : ActivityResultCaller,
  ) : BetterActivityResult<Intent, ActivityResult> {
    return registerForActivityResult(caller, StartActivityForResult())
  }
 }
}
Muhammad Helmi
  • 395
  • 2
  • 10
2

An alternate way to do this is in 3 steps. (Considering you have a startActivityForResult(0 and onActivityResult())

  1. create a variable in the form var resultLauncher:ActivityResultLauncher<Intent>
  2. create a private function where you initialize the resultLauncher in this basic format
resultLauncher=registerForActivityResult(ActivityResultContracts.StartActivityForResult()){result ->  

// copy paste the code from the onActivityResult replacing resultcode to result.resultCode  

if(result.resultcode==Activity.Result_OK){
val data=result.data // this data variable is of type intent and you can use it 

}else{
//code if you do not get the data 
}
}
  1. Go to the line with startActivityForResult() and replace it with the line resultLauncher.launch(intent)
Narendra_Nath
  • 4,578
  • 3
  • 13
  • 31
2

dor506 answer worked for me as i use BaseActivity in most of my projects so it is easier for me to change the code in single file rather than all my activites. I have written the java version of this code.

BaseActivity code :

private int requestCode = -1;
private ActivityResultLauncher<Intent> resultHandler = null;

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mContext = this;

    registerForActivityResult();
}
  private final void registerForActivityResult() {
    if (shouldRegisterForActivityResult()) {
        this.resultHandler = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
                new ActivityResultCallback() {

            public void onActivityResult(Object var1) {
                this.onActivityResult((ActivityResult)var1);
            }

            public final void onActivityResult(ActivityResult result) {
                Intrinsics.checkNotNullExpressionValue(result, "result");
                AppActivityClass.onActivityResult(result.getData(), AppActivityClass.this.requestCode, result.getResultCode());
                AppActivityClass.this.requestCode = -1;
            }
        });
    }
}

public final void startActivityForResult(int requestCode, Intent intent) {
    this.requestCode = requestCode;
    if (resultHandler != null) {
        resultHandler.launch(intent);
    }
}

protected static void onActivityResult(Intent intent, int requestCode, int resultCode) {
}

protected Boolean shouldRegisterForActivityResult() {
    return false;
}

Now in any activity use this code like this:

 @Override
protected Boolean shouldRegisterForActivityResult() {
    return true;  // this will override the baseactivity method and we can use onactivityresult
}

  private void someMethod(){
    Intent i = new Intent(mContext,SomeOtherClassActivity.class);
    startActivityForResult(101,i);
}

  @Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 101) {
        if (resultCode == RESULT_OK) {
            //revert from called class
        }
    }
}
Tarun Yadvendu
  • 230
  • 3
  • 11
2

Sharing solution that I've found

First, register this activity for result using registerForActivityResult This will return an object of type ActivityResultLauncher<Intent!> Like this,

private val getResult =
        registerForActivityResult(
            ActivityResultContracts.StartActivityForResult()
        ) {
            if (it.resultCode == Activity.RESULT_OK) {
                val value = it.data?.getStringExtra("input")
            }
        }

Now where ever we want to launch activity for result we can use getResult.launch(intent)

Rizwan Amjad
  • 329
  • 4
  • 11
2

Combine with the above answer, I have a approach that compatible with the old way startActivityForResult() keep using requestCode without changing old code structure:

ActivityLauncher.class

public class ActivityLauncher {

private final ActivityResultLauncher<Intent> launcher;
private ActivityResultCallback<ActivityResult> activityResultCallback;

private ActivityLauncher(@NonNull ActivityResultCaller caller,
                         @NonNull ActivityResultContract<Intent, ActivityResult> contract,
                         @Nullable ActivityResultCallback<ActivityResult> activityResultCallback) {
    this.activityResultCallback = activityResultCallback;
    this.launcher = caller.registerForActivityResult(contract, this::onActivityResult);
}

public static ActivityLauncher registerActivityForResult(
        @NonNull ActivityResultCaller caller) {
    return new ActivityLauncher(caller, new ActivityResultContracts.StartActivityForResult(), null);
}

public void launch(Intent intent, @Nullable ActivityResultCallback<ActivityResult> activityResultCallback) {
    if (activityResultCallback != null) {
        this.activityResultCallback = activityResultCallback;
    }
    launcher.launch(intent);
}

private void onActivityResult(ActivityResult result) {
    if (activityResultCallback != null) activityResultCallback.onActivityResult(result);
}

public interface OnActivityResult {
    void onActivityResultCallback(int requestCode, int resultCode, Intent data);
}

}

Code in BaseActivity.java

private final ActivityLauncher activityLauncher = ActivityLauncher.registerActivityForResult(this);

public void startActivityForResult(Intent intent, int requestCode, ActivityLauncher.OnActivityResult onActivityResult) {
    activityLauncher.launch(intent, result -> onActivityResult.onActivityResultCallback(requestCode, result.getResultCode(), result.getData()));
}

And finally in each Activity that extends BaseActivity, implements ActivityLauncher.OnActivityResult and change the name of override function "onActivityResult" to "onActivityResultCallback". Also rember to remove super.onActivityResult()

How to use: startActivityForResult(intent, requestCode, this)

Ho Luong
  • 726
  • 8
  • 12
1

startActivityForResult and onActivityResult is deprecated in android 10 API 30 now we have a new way to get the result using registerForActivityResult

resultContract =
    registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            // There are no request codes
            val country = result.data?.getParcelableExtra<Country>("Country")
            showLiveDemoDialogue(country)
        }
    }

and to launch activity

val intent = Intent(this, CountriesListActivity::class.java)
        resultContract.launch(intent)

but you should register before you call launch And launch wherever you want. otherwise, you will get this exception

attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
Mirza Ahmed Baig
  • 5,605
  • 3
  • 22
  • 39
1
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                if (result.getResultCode() == Activity.RESULT_OK) {

                }
            }
        });
Mazhar Iqbal
  • 813
  • 7
  • 7
1

If you implement your base Activity like this, you may continure using startActivityForResult in old fashion. The only limitation is you will have to use setResult(result, intent) to set the result within your activity. The key is to let the result carry the request code back to the result consumer.

public class MyBaseActivity extends AppCompatActivity {
    private ActivityResultLauncher<Intent> activityLauncher;
    protected static String ACTIVITY_REQUEST_CODE = "my.activity.request.code";
    protected _originalIntent; 

    public void launchActivityForResult(Intent intent, int requestCode){
        intent.putExtra(UGM_ACTIVITY_REQUEST_CODE, requestCode);
        activityLauncher.launch(intent);
    }

    //
    //In order to be signature compatible for the rest of derived activities, 
    //we will override the deprecated method with our own implementation!
    //
    @SuppressWarnings( "deprecation" )
    public void startActivityForResult(Intent intent, int requestCode){
        launchActivityForResult(intent, requestCode);
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    _originalIntent = getIntent();
        //set the default result
        setResult(Activity.RESULT_OK, _originalIntent);

        activityLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                Intent intent = result.getData();
                int requestCode = intent.getIntExtra(ACTIVITY_REQUEST_CODE, -1);
                MyBaseActivity.this.onActivityResult(requestCode, result.getResultCode(), intent);
            }
        });
    }

}
John Zhang
  • 21
  • 2
0

Simple Example of registerForActivityResult for both StartActivityForResult & RequestMultiplePermissions from Activity and Fragment [in Kotlin]

Requesting activity for result from Activity

registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { activityResult ->
    if (activityResult.resultCode == Activity.RESULT_OK) {
        //...
    }
}

Check out ActivityResult

Requesting for permissions from Activity?

registerForActivityResult(
    ActivityResultContracts.RequestMultiplePermissions()
) {
    //it: Map<String, Boolean>
}

From Fragment?

Use same methods but make sure you put these implementations in initialization, onAttach(), or onCreate()

Ngima Sherpa
  • 1,397
  • 1
  • 13
  • 34
0

In case you are using SMS consent API then use the following code (Kotlin):

resultLauncher.launch( consentIntent
                            )

    var resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        // There are no request codes
    //    val data: Intent? = result.data
        val message = result.data?.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE)
        getOtpFromMessage(message)

    }
}
0

I am using kotlin extension to make it very simple. Add below extensiton fucntion in your Extenstions.kt file:

fun AppCompatActivity.startForResult(intent: Intent,
    onResult: (resultCode: Int, data: Intent?) -> Unit
) {
    this.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {result ->
        onResult(result.resultCode, result.data)
    }.launch(intent)
}

Now, inside any activity that inherits AppCompatActivity, you can use below simple code:

val i = Intent(this, TargetActivity::class.java)
startForResult(i) { resultCode, data ->
   //put your code here like:
   if (resultCode == RESULT_OK) {
      //your code here...
      }
   }
}

Update Above implementaion may cause below exception: java.lang.IllegalStateException: LifecycleOwner xxxx is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.

So registerForActivityResult should be called in advance for example before onCreate. Here is the alternative solution.

Add below extensiton fucntion in your Extenstions.kt file:

fun AppCompatActivity.registerForResult(onResult: (resultCode: Int, data: Intent?) -> Unit):
        ActivityResultLauncher<Intent> {
    return this.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        onResult(result.resultCode, result.data)
    }
}

Now, inside any activity that inherits AppCompatActivity, you can use below simple code:

  1. Define a class memeber variable for every action requires result
private val myActionResult = registerForResult { resultCode, data ->
   //put your code here like:
   if (resultCode == RESULT_OK) {
      //your code here...
      }
   }
}
  1. Launch the action
val i = Intent(this, TargetActivity::class.java)
myActionResult.launch(i)
Ayman Al-Absi
  • 2,630
  • 24
  • 22
0

Adding on to the answers by muntashir akon and abhijeet, you can modify the new format to work like the old format by passing values in the intent, for example:

// calling class
....
val i = Intent(this@GEBShopActivity, BarcodeScannerActivity::class.java)
when(loadedFragment){   
   is ShopHomeFragment      -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_SCAN_LIST_MAINT) }
   is ShopListFragment      -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_SCAN_LIST_MAINT) }
   is ShopItemMaintFragment -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_SCAN_ITEM_MAINT) }
   is ShopPriceFragment     -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_PRICE_CAPTURE) }
   is ShopCompareFragment   -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_PRICE_CAPTURE) }
}
shopFragmentLauncher.launch(i)
....
// called class
....
val resultIntent = Intent()
val bundle = Bundle()
bundle.putStringArrayList("scanned_barcodes", scanned_barcodes)
bundle.putInt("scan_count", scan_count)
resultIntent.putExtras(bundle)
resultIntent.putExtra("myapp.result.code", intent.getIntExtra("myapp.result.code", 0))
setResult(Activity.RESULT_OK, resultIntent)
....

This will allow you to keep the class called the same with just the one extra line to add your original called result code. Also allows you to create a reusable launcher instance.

0

2023 Kotlin Answer to get an Uri.

An example for those who are searching for a way to fetch an image from the image library and handle the Uri because super.onActivityResult(requestCode, resultCode, data) is crossed out/deprecated

enter image description here

I got this answer from google docs and it works.

val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
    // Handle Uri
}

FYI you should be able to use this to get any Uri, not just for images. Here it is used below.

import androidx.activity.result.contract.ActivityResultContracts

class MainActivity : AppCompatActivity() {

    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        // ...

        binding.selectPhotoButton.setOnClickListener {
            selectPhoto()
        }
    }

    fun selectPhoto() {

        getContent.launch("image/*") // *** What Google said to do to launch the image library ***
    }

    // *** This is what Google posted to handle the Uri ***
    val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->

        // Handle the returned Uri

        binding.myImageView.setImageURI(uri)
    }
}
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256