In the past I used "startActivityForResult" and "onActivityResult". Those methods are now deprecated. Therefore I want to use the modern alternative "registerForActivityResult".
Unfortunately, however, I cannot figure out a way to achieve the old functionality with this new model. I used to override "startActivityForResult" and "onActivityResult" of "AppCompatActivity" to run specific code before any activity was started and whenever any activity finished with a result. This was very convenient, since I didn't have to write the code for every activity launch.
As I see it now, I register the activity for a result and pass a callback to get a launcher that I can use to launch the activity later. This part I could solve by creating a function to use the launcher that always executes certain code. But there's a problem with the callback. As I see it, I would have to write this specific code into the callback of every callback I write for any activity launch.
Am I correct or am I missing something?
Theres a couple ways to do this:
1)Put the common code in its own function and just call that function in every callback. Reduces the code needed to be written to 1 line
2)Write a callback class GenericCallback. Then write your specific callback MySpecificCallback extends GenericCallback. Put the common code in the GenericCallback class and you just have to call super in the specific callback. Reduces the code to just calling super.callbackFunction, but means you can't use a lambda- you have to use a named or anonymous class. This is probably more of a Java approach though
3)Create a wrapper function for registerForActivityResult that takes a callback, and calls the real registerForActivityResult who's callback calls the common code and your callback Example:
fun registerWrapper(callback: ()->Unit) {
registerForActivityResult(() ->{
doCommonCode()
callback()
}
}
Related
I have implemented Google Pay in app and making paymentData request I am using AutoResolveHelper to display bottom sheet and than get results via onActivityResult. I am making this request from Fragment not from Activity. So I am passing parent activity like this.
paymentsClient?.loadPaymentData(gpayViewModel.paymentDataRequest)?.let { task ->
AutoResolveHelper.resolveTask(task, requireActivity(), LOAD_PAYMENT_DATA_REQUEST_CODE)
}
the problem is that this AutoResolveHelper is not calling onActivityResult on Fragment but only on Activity.
I have read something like this:
If you're calling startActivityForResult() from the fragment then you
should call startActivityForResult(), not
getActivity().startActivityForResult(), as it will result in fragment
onActivityResult().
So it suggest that when AutoResolveHelper is calling startActivityForResult() on passed activity then fragment's onActivityResult will never be called.
So now my only option is to implement onActivityResult in Activity and somehow pass control from this Activity to my child Fragment but this need some boilerplate code and as my Fragment is Reusable than this solution is not perfect.
Meanwhile I have spotted that this code startActivityForResult in correct way and than the Fragment's onActivityResult is called correctly:
val intent = Intent(activity, CardIOActivity::class.java)
intent.putExtra(CardIOActivity.EXTRA_REQUIRE_EXPIRY, true)
intent.putExtra(CardIOActivity.EXTRA_REQUIRE_CVV, true)
intent.putExtra(CardIOActivity.EXTRA_REQUIRE_CARDHOLDER_NAME, true)
startActivityForResult(intent, CARD_IO_REQUEST_CODE)
So can I replace this AutoResolveHelper.resolveTask() somehow to execute this task in such way that onActivityResult will not be necessary or I could startActivityForResult myself?
As of today, the receipt of a result is bound to the Activity. Part of the reason for that is that the library is not precisely using startActivityForResult to initiate this process; and Fragment support on that same functionality is at the moment limited.
There are basically two ways to circumvent this at the moment (these have been shared in other threads too). However, I personally feel that mixing responsibilities between the fragment and the activity does not provide for great code clarity and clear logic, so as of now, I'd only consider an approach where the activity is responsible for making the call to AutoResolveHelper, capturing the result and sharing it with the fragment. Instead of calling the activity from the fragment, I'd consider doing that through a contract / interface in order to reduce the level of coupling between the two.
A simplistic example could be something like:
interface PaymentsContract {
fun loadPaymentData(request: PaymentDataRequest, requestCode: Int)
}
Having the activity implement it, and passing it as a reference to the fragment at construction time allows your fragment to stay reusable and agnostic from the activity.
Once the result is ready, you can choose to find the fragment in question and propagate the onActivityResult method to it, or alternatively use a similar contract-based approach for the fragment too.
It'd be useful to learn more about your concrete use cases in order to better understand the rationale to handle this logic within fragments, and ultimately take that feedback back to the team to be considered for future developments.
Hope this is useful.
EDIT: Another alternative to interfaces is to use lambda expressions to let your fragment know about a callable that was defined somewhere else (eg.: in your activity), and that needs to be called when something happens (eg.: when your user hits the "Pay with Google Pay" button).
Here is a link to the working code. It may be not the perfect approach but it gets the work done.
https://readyandroid.wordpress.com/onactivityresult-is-not-being-called-in-fragment/
in my app, there are 5 classes and 1 activity. This activity has, as you all know, an OnDestroy() method. In this method I need to remove a test provider which is set up in another class called "mockingclass".
In "mockingclass" I have a method similar to this:
public void mocker()
{
xxx
location.RemoveTestProvider(location.GpsProvider);
xxx
}
While xxx stands for many other functions in this method, when the app is being destroyed I need to call ONLY for that ONE function in within this whole method.
Is there any way to do that at all? If not, what would be your suggestions?
THANKS!
Obviously you can't choose a single line from method to be executed. You need to extract it separate method which will be called separately. Maybe you should look at some injection? Let this call be injected as an delegate for example. In that way you could manipulate what should be called depending on situation.
I am working on an Android library and I am having an issue.
The main application calls an initialises a library. One of the parameters is the calling activity. This activity is then used as the context when needed.
When a certain event is triggered within the main application calls a method, which then calls a new activity. At the moment, the library class uses the activity that was passed into the initalise method to create the new activity (note that the passed in activity in the initialise may not be the activity that triggered the library as it only initialised once.
The activity that is started in the library, sets a result and finishes the activity. However, the onactivity result in the library class is not called (I presume because its not an activity and the result would go back to the activity that was passed in to the initialise.
What I need to be able to do is have the library class file to get the returned result from the libraries activity.
Android AccountManager does something like this. A request to AccountManager will invoke an authenticator plugin which can have an activity to get the user's username/password, etc.
AccountManager just uses a special Future called AccountManagerFuture to return the results asynchronously, and I would recommend you use a Future implementation as well to return your results asynchronously. Then you don't need to worry about how to make the two activities connect through the library.
I am trying to use this solution inside my Fragment, however I couldn't be sure where to call System.LoadLibrary(), finally I decided to call from onCreate method of the Fragment, I want to be sure if this would not raise some another weird error on different devices.
General it won't cause weird behaviours, as onCreate is mandatory callback when your fragment created by system. This make sure your native library is prepared before you do further operation in your fragment.
Take a look at fragment life cycle, it might be better if you put your library initialize routine on the first callback onAttach, you can use the attached Activity context to initialize your native library.
It is just a static initialization. You don't even have to have it inside your onCreate() at all. You can simply initialize it as the first part of your class next to your global variables as such:
class YourClass
{
static
{
System.loadLibrary( yourLib );
}
...
}
It is only important that it is done early or you may see UnsatisfiedLinkError followed by application's crash. That way whenever a call referenced later is made, the library itself has already been loaded. A static declaration at the top will do that as soon as an instance of the class has been requested; even before onCreate().
So, this is probably a very basic design question, but Im just not sure how to go about it.
Normally, when I use event listeners, I define it in whatever class, then I override the necessary methods in my Activity and instantiate the class and the listener as needed.
However, in this case, I have my MainActivity class (that also implements the listener), a class (called testClass) that implements the listener, and a broadcast receiver class. The broadcast receiver class instantiates the calls the testClass. Now, what I am trying to do is to update a TextView in MainActivity when a given function is called in the testClass.
Not sure how to go about this.
Hope this wobbly issue description makes sense.
This is a problem I've tackled in the past when using a background Service to update data that is displayed on screen. The general pattern I use is to add a member variable to your processing class (in this case, I think it's your TestClass) that is a Map (named something like mCallbackMap) with android.os.Handler as the key and your listener object as the value (normally this will be an interface that you define). The Handler, which is created in the Activity and thus associated with the main thread, is needed because you can't change the UI of an Activity from outside the main Thread; you'll use the Handler to post a runnable to the main thread instead of manipulating it directly.
When your activity gets going, probably in onCreate, onStart, or onResume, you'll register it as a callback with your TestClass by using the mCallbackMap's put() method. Simply instantiate a Handler, which you'll also store as a member variable of your activity, and use it as the key and your Activity as the value. You'll need to remove the callback in onPause or onStop so you don't leak the activity after it's out of view.
Then, once TestClass finishes handling whatever the broadcast gives it, you'll iterate through your mCallbackMap (maybe you have more than one callback, maybe you don't) and call Handler.post(Runnable). In the Runnable's run() method, you call the callback's methods as appropriate.