Android Tutorial [java] Guide - Using onActivityResult

Erel

B4X founder
Staff member
Licensed User
Longtime User
Starting with B4A V1.1 libraries can use startActivityForResult and receive the onActivityResult event.

There is a new interface named: IOnActivityResult.

B4X:
public interface IOnActivityResult {
    void ResultArrived(int resultCode, Intent intent);
}
You should implement this interface and pass it to BA.startActivityForResult:
B4X:
public synchronized void startActivityForResult(IOnActivityResult iOnActivityResult, Intent intent)
This method starts the intent and also takes care of mapping this request with the given iOnActivityResult.
To avoid memory leaks this method only holds a WeakReference to iOnActivityResult. Which means that you need to hold a strong reference to it in your code.

When the result arrives the IOnActivityResult object will be called with the resultCode and Intent values.

Here is an example taken from VoiceRecognition object:
B4X:
         private IOnActivityResult ion;
        /**
         * Starts listening. The Ready event will be raised when the result arrives.
         */
        public void Listen(final BA ba) {
            if (eventName == null)
                throw new RuntimeException("VoiceRecognition was not initialized.");
            Intent i = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
            if (prompt != null && prompt.length() > 0)
                i.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
            if (language != null && language.length() > 0)
                i.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
            ion = new IOnActivityResult() {

                @SuppressWarnings("unchecked")
                @Override
                public void ResultArrived(int resultCode, Intent intent) {
                    List list = new List();
                    if (resultCode == Activity.RESULT_OK) {
                        ArrayList<String> t = intent.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
                        if (t.size() > 0) {
                            list.setObject((java.util.ArrayList)t);
                        }
                    }
                    ba.raiseEvent(VoiceRecognition.this, eventName + "_result", list.IsInitialized(), list);

                }
            };
            ba.startActivityForResult(ion, i);
        }
ion is an instance variable.
When the user calls Listen we create a new Intent with the required values.
We also initialize ion. The action done when the result arrives is to take the values from the result Intent and raise the "Result" event.
You should always check resultCode and make sure it is RESULT_OK.

The last step is to actually start the activity by calling ba.startActivityForResult.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
There are cases where the intent is sent from inside a library. I encountered it in the Dropbox Sync library. As you are not sending the intent directly, you cannot call ba.startActivityForIntent. However without calling this method the result will be ignored.

The following workaround allows you to register an IOnActivityResult without sending the intent yourself:

B4X:
try {
         ba.startActivityForResult(ion, null); //<-- passing null instead of an intent
      } catch (NullPointerException npe) {
         //required...
      }
      BA.SharedProcessBA sba = ba.sharedProcessBA;
      try {
         Field f = BA.SharedProcessBA.class.getDeclaredField("onActivityResultCode");
         f.setAccessible(true);
         int requestCode = f.getInt(sba) - 1;
         'requestCode holds the value that should be used to send the intent.
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
Hi Erel,

I have created a class that implements the functionality of the Google Play Services library wrapper that NFOBoy wrote. I originally had all the code in my Main activity module, but wanted to clean it up, so moved everything into a class. Everything works well except that when I call "beginUserInitiatedSignin" it throws a java.lang.nullpointerexception. The exception is being thrown when the library calls a startActivityForResult & passes a null instead of an intent. At this point, when the code was in my Main Activity, a dialog would come up for me to choose which user ID to use to log in to Google Play Services - so I'm thinking that it has some issue with trying to create this dialog since I put the code in the class.

Any ideas how I can get around this?

Thanks - Colin.
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
You will need to use an activity to handle the onActivityResult event as this is an Activity specific call.
Thanks Erel - don't really understand how to do this (yet), but I have discovered that it's not the null being passed to the startActivityForResult that is the issue. It's actually the:

B4X:
BA.SharedProcessBA sba = ba.sharedProcessBA;

that is causing it (the code in the library looks to be an exact copy of your example in post #2). Does this change your recommendation at all?

Thanks - Colin.
 

Informatix

Expert
Licensed User
Longtime User
Thanks Erel - don't really understand how to do this (yet), but I have discovered that it's not the null being passed to the startActivityForResult that is the issue. It's actually the:

B4X:
BA.SharedProcessBA sba = ba.sharedProcessBA;

that is causing it (the code in the library looks to be an exact copy of your example in post #2). Does this change your recommendation at all?

Thanks - Colin.
To solve this issue, add:
if (pBA == null) pBA = mBa;
after:
BA pBA = mBa.processBA;
Your code should work now in all cases.

I'm rewriting the Google Play Services library because I don't like many things in it. I will publish it in the forum once done.
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
To solve this issue, add:
if (pBA == null) pBA = mBa;
after:
BA pBA = mBa.processBA;
Your code should work now in all cases.

I'm rewriting the Google Play Services library because I don't like many things in it. I will publish it in the forum once done.

Thanks Informatix. I'm in the process of porting Yahtzee to iOS, so haven't looked at the library lately. I'm looking forward to seeing your re-write!

- Colin.
 

moster67

Expert
Licensed User
Longtime User
I know this thread is old but I think my question is better placed here than in a new thread.

I have seen many java code examples with onActivityResult and that also a requestCode (int) is returned. So far I have not needed it but apparently it returns the integer request code originally supplied to startActivityForResult() and allows us to identify who this result came from.

Can it be added to the IOnActivityResult interface?
 
Top