I recently discovered that onActivityResult is deprecated. What should we do to handle it?
Any alternative introduced for that?
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.
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
}
}
There are 4 simple steps to follow while replacing the deprecated method startActivityForResult(...).
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..
Imports :
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
In place of startActivityForResult(intent, 123), use
Intent intent = new Intent(this, SampleActivity.class);
activityResultLaunch.launch(intent);
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! :)
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
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);
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.
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/*")
}
}
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.
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)
}
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.
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
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
}
}
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);
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)
}
}
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)
}
}
}
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
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())
}
}
}
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
}
}
}
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)
The below code works in the Kotlin fragment to check the Bluetooth permission. Year - 2022
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)
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.
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
}
}
});
An alternate way to do this is in 3 steps. (Considering you have a startActivityForResult(0 and onActivityResult())
create a variable in the form var resultLauncher:ActivityResultLauncher<Intent>
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
}
}
Go to the line with startActivityForResult() and replace it with the line resultLauncher.launch(intent)
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);
}
});
}
}
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)
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()
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)
}
}
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:
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...
}
}
}
Launch the action
val i = Intent(this, TargetActivity::class.java)
myActionResult.launch(i)
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.
Related
I recently discovered that onActivityResult is deprecated. What should we do to handle it?
Any alternative introduced for that?
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.
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
}
}
There are 4 simple steps to follow while replacing the deprecated method startActivityForResult(...).
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..
Imports :
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
In place of startActivityForResult(intent, 123), use
Intent intent = new Intent(this, SampleActivity.class);
activityResultLaunch.launch(intent);
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! :)
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
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);
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.
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/*")
}
}
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.
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)
}
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.
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
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
}
}
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);
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)
}
}
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)
}
}
}
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
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())
}
}
}
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
}
}
}
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)
The below code works in the Kotlin fragment to check the Bluetooth permission. Year - 2022
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)
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.
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
}
}
});
An alternate way to do this is in 3 steps. (Considering you have a startActivityForResult(0 and onActivityResult())
create a variable in the form var resultLauncher:ActivityResultLauncher<Intent>
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
}
}
Go to the line with startActivityForResult() and replace it with the line resultLauncher.launch(intent)
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);
}
});
}
}
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)
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()
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)
}
}
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:
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...
}
}
}
Launch the action
val i = Intent(this, TargetActivity::class.java)
myActionResult.launch(i)
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.
I recently discovered that onActivityResult is deprecated. What should we do to handle it?
Any alternative introduced for that?
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.
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
}
}
There are 4 simple steps to follow while replacing the deprecated method startActivityForResult(...).
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..
Imports :
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
In place of startActivityForResult(intent, 123), use
Intent intent = new Intent(this, SampleActivity.class);
activityResultLaunch.launch(intent);
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! :)
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
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);
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.
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/*")
}
}
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.
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)
}
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.
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
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
}
}
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);
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)
}
}
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)
}
}
}
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
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())
}
}
}
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
}
}
}
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)
The below code works in the Kotlin fragment to check the Bluetooth permission. Year - 2022
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)
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.
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
}
}
});
An alternate way to do this is in 3 steps. (Considering you have a startActivityForResult(0 and onActivityResult())
create a variable in the form var resultLauncher:ActivityResultLauncher<Intent>
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
}
}
Go to the line with startActivityForResult() and replace it with the line resultLauncher.launch(intent)
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);
}
});
}
}
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)
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()
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)
}
}
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:
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...
}
}
}
Launch the action
val i = Intent(this, TargetActivity::class.java)
myActionResult.launch(i)
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.
I recently discovered that onActivityResult is deprecated. What should we do to handle it?
Any alternative introduced for that?
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.
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
}
}
There are 4 simple steps to follow while replacing the deprecated method startActivityForResult(...).
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..
Imports :
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
In place of startActivityForResult(intent, 123), use
Intent intent = new Intent(this, SampleActivity.class);
activityResultLaunch.launch(intent);
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! :)
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
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);
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.
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/*")
}
}
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.
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)
}
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.
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
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
}
}
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);
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)
}
}
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)
}
}
}
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
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())
}
}
}
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
}
}
}
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)
The below code works in the Kotlin fragment to check the Bluetooth permission. Year - 2022
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)
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.
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
}
}
});
An alternate way to do this is in 3 steps. (Considering you have a startActivityForResult(0 and onActivityResult())
create a variable in the form var resultLauncher:ActivityResultLauncher<Intent>
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
}
}
Go to the line with startActivityForResult() and replace it with the line resultLauncher.launch(intent)
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);
}
});
}
}
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)
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()
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)
}
}
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:
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...
}
}
}
Launch the action
val i = Intent(this, TargetActivity::class.java)
myActionResult.launch(i)
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.
I recently discovered that onActivityResult is deprecated. What should we do to handle it?
Any alternative introduced for that?
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.
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
}
}
There are 4 simple steps to follow while replacing the deprecated method startActivityForResult(...).
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..
Imports :
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
In place of startActivityForResult(intent, 123), use
Intent intent = new Intent(this, SampleActivity.class);
activityResultLaunch.launch(intent);
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! :)
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
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);
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.
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/*")
}
}
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.
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)
}
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.
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
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
}
}
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);
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)
}
}
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)
}
}
}
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
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())
}
}
}
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
}
}
}
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)
The below code works in the Kotlin fragment to check the Bluetooth permission. Year - 2022
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)
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.
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
}
}
});
An alternate way to do this is in 3 steps. (Considering you have a startActivityForResult(0 and onActivityResult())
create a variable in the form var resultLauncher:ActivityResultLauncher<Intent>
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
}
}
Go to the line with startActivityForResult() and replace it with the line resultLauncher.launch(intent)
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);
}
});
}
}
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)
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()
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)
}
}
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:
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...
}
}
}
Launch the action
val i = Intent(this, TargetActivity::class.java)
myActionResult.launch(i)
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.
I have a webview component of React Native. The webview should support input type is file, so I do it as:
File Upload in WebView
and the webview implements ActivityEventListener and override onActivityResult.But the onActivityResult not working.
The Code is
class RNWebView extends WebView implements ActivityEventListener {
protected class GeoWebChromeClient extends WebChromeClient {
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
...
mActivity.startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);
return true;
}
}
public RNWebView(ReactContext reactContext, Activity activity) {
super(reactContext);
// Add the listener for `onActivityResult`
reactContext.addActivityEventListener(this);
...
}
#Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
// Your logic here
Log.d("Tanck", "requestCode:" + requestCode + "----" + "resultCode:" + resultCode);
}
}
Maybe late hope it helps.
native module's onActivityResult is called by ReactContext which is called by ReactInstanceManagerImpl which in 0.29 is called by ReactActivity. In the example above MyWb extends Activity not ReactActivity, so ReactInstanceManagerImpl is never called.
The solution is just in your activity's onActivityResult call ReactInstanceManager's onActivityResult since you already have your own ReactInstanceManager object reference.
The Activity you are launching React Native from should have an override like this:
// Manually pass along the `onActivityResult` event to React Native event listeners.
// We are not extending from `ReactActivity` so we need to do this manually.
//
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mReactInstanceManager.onActivityResult( this, requestCode, resultCode, data );
}
To make the solution complete:
in your MainActivity (or in the Activity, where RN is initialized and mReactInstanceManager is used if it is a part of a native app):
...
private ReactInstanceManager mReactInstanceManager;
...
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mReactInstanceManager.onActivityResult(requestCode, resultCode, data);
}
in your Module:
public class MyModule extends ReactContextBaseJavaModule implements ActivityEventListener {
static final int REQUEST_VIDEO_CAPTURE = 1;
final ReactApplicationContext reactContext;
Promise promise;
public GeneralIntentModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
this.reactContext.addActivityEventListener(this);
}
#Override
public String getName() {
return "MyModule ";
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
this.promise.resolve(data.getDataString());
}
}
If any one using kotlin with react-native module with Promise, plesae refer below code,
import android.app.Activity
import android.content.Intent
import android.util.Log
import com.facebook.react.bridge.*
import com.onboardinglib.HostActivity
class ConsistyOnboarding (reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
private val CODE = 999
private var promise: Promise? = null
private val reContext: ReactApplicationContext? = reactContext
fun dumpIntent(intent: Intent) {
LogPrint("Bundle data", "Dumping intent start")
val bundleData = intent.extras
if (bundleData != null) {
for (key in bundleData.keySet()) {
LogPrint(
"Bundle data-->",
key + " : " + if (bundleData[key] != null) bundleData[key] else "NULL"
)
}
}
}
override fun getName(): String {
return "ConsistyOnboarding"
}
private val mActivityEventListener: ActivityEventListener =
object : BaseActivityEventListener() {
override fun onActivityResult(
activity: Activity,
requestCode: Int,
resultCode: Int,
data: Intent
) {
LogPrint("mActivityEventListener", "Started")
if (data == null) {
resolve("01", "No action taken", "0")
return
}
dumpIntent(data)
if (resultCode == Activity.RESULT_OK) {
try {
val status = data.getBooleanExtra("status", false)
val response = data.getIntExtra("response", 0)
val message = data.getStringExtra("message")
resolve(status.toString(), response.toString(), message.toString())
return
} catch (e: Exception) {
e.printStackTrace()
resolve("01", "Exception occurred in on-boarding " + e.message, "0")
}
}
resolve("01", "No action taken", "0")
}
}
init {
reContext?.addActivityEventListener(mActivityEventListener)
}
#ReactMethod
fun Onboarding(
partnerId: String, partnerKey: String prm: Promise
) {
promise = prm
val currentActivity = currentActivity
val intent = Intent(currentActivity, HostActivity::class.java)
intent.putExtra("pId", partnerId)
intent.putExtra("ApiKey", partnerKey)
try {
currentActivity?.startActivityForResult(intent, CODE)
} catch (e: Exception) {
e.printStackTrace()
resolve("01", "No action taken", "0")
}
}
private fun resolve(
statusCode: String,
response: String,
message: String
) {
if (promise == null) {
return
}
val map = Arguments.createMap()
map.putString("statusCode", statusCode)
map.putString("response", response)
map.putString("message", message)
promise!!.resolve(map)
promise = null
}
private fun LogPrint(key: String?, value: String?) {
if (key == null || value == null) {
return
}
Log.i(key, value)
}
}
Main part is need to add event Listener,
init {
reContext?.addActivityEventListener(mActivityEventListener)
}