Android Tutorial [java] Guide - Using onActivityResult

Discussion in 'Libraries developers questions' started by Erel, Jan 12, 2011.

  1. Erel

    Erel Administrator Staff Member Licensed User

    Starting with B4A V1.1 libraries can use startActivityForResult and receive the onActivityResult event.

    There is a new interface named: IOnActivityResult.

    Code:
    public interface IOnActivityResult {
        void ResultArrived(int resultCode, 
    Intent intent);
    }
    You should implement this interface and pass it to BA.startActivityForResult:
    Code:
    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:
    Code:
    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.
     
  2. Erel

    Erel Administrator Staff Member Licensed 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:

    Code:
    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);
          }
     
  3. Computersmith64

    Computersmith64 Well-Known Member Licensed 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.
     
  4. Erel

    Erel Administrator Staff Member Licensed User

    You will need to use an activity to handle the onActivityResult event as this is an Activity specific call.
     
  5. Computersmith64

    Computersmith64 Well-Known Member Licensed 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:

    Code:
    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.
     
  6. Erel

    Erel Administrator Staff Member Licensed User

    Hard to say. Wrapping the game play services is a challenging task. You need to really be familiar with Java / Android and Basic4android libraries...
     
  7. Informatix

    Informatix Expert Licensed 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.
     
  8. Computersmith64

    Computersmith64 Well-Known Member Licensed User

    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.
     
  9. moster67

    moster67 Expert Licensed 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?
     
  10. Erel

    Erel Administrator Staff Member Licensed User

    The request code is used internally to map the result to the correct IOnActivityResult instance. In all practical cases there will always be a single request so the number itself will not provide any useful information.
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice