Java Question Handling onActivityResult in B4A

airblaster

Active Member
Licensed User
Longtime User
Hello,

I'm using a java library that need to call onActivityResult on the calling Activity.
The calling Activity is a B4A Activity.

I've already tried putting the following code into the B4A Activity, but it doesn't work:
B4X:
Sub onActivityResult(requestCode As Int, resultCode As Int, data As Intent )
 Log("ok")
End Sub

Is there any way to solve this?
 

airblaster

Active Member
Licensed User
Longtime User
Figured out some code by now, but still not working:
B4X:
public void registerCallback() {
          ion = new IOnActivityResult() {

              @SuppressWarnings("unchecked")
              @Override
              public void ResultArrived(int resultCode, Intent intent) {
                 Log.d("B4A", intent.toString());
                 C_RedemptionController.this.redemptionController.redeemCallback(C_RedemptionController.this.ba.activity, C_RedemptionController.this.requestCode, resultCode, intent, C_RedemptionController.this.listener);
              }
          };
          BA pBa = ba.processBA;
          try {
               pBa.startActivityForResult(ion, null); //<-- passing null instead of an intent
           } catch (NullPointerException npe) {
               //required...
           }
          
           BA.SharedProcessBA sba = pBa.sharedProcessBA;
           try {
               Field f = BA.SharedProcessBA.class.getDeclaredField("onActivityResultCode");
               f.setAccessible(true);
               requestCode = f.getInt(sba) - 1; //requestCode holds the value that should be used to send the intent.
               this.redemptionController.redeemCoupon_html(ba.activity, C_RedemptionController.this.c, true);
           } catch (Exception e) {
               throw new RuntimeException(e);
           }
           
   }

The log says:
onActivityResult: wi is null
What could this mean?
 

airblaster

Active Member
Licensed User
Longtime User
Hi Erel,

I'm writing a wrapper for a library.
I currently don't have access to its source code.
 

airblaster

Active Member
Licensed User
Longtime User
Hi Erel,

I declared it like this:
B4X:
public class C_RedemptionController {
   private IOnActivityResult ion;
{Code posted above and other code}
 

airblaster

Active Member
Licensed User
Longtime User
Hi Erel,

one more thing I just noticed:
The first call to this code, as well as the third and all following calls lead to the following:
B4X:
** Activity (coupon) Pause, UserClosed = false **
onActivityResult: wi is null
** Activity (coupon) Resume **
However, the second call always leads to the following:
B4X:
** Activity (coupon) Pause, UserClosed = false **
sending message to waiting queue (OnActivityResult)
running waiting messages (1)
onError
** Activity (coupon) Resume **
 

airblaster

Active Member
Licensed User
Longtime User
Hi Erel,

no, it just a normal global variable.
Because I need to pass the B4A activity to the library.
Or is there some way to do this with an object that is in Process_Globals?
 

airblaster

Active Member
Licensed User
Longtime User
Hi Erel,

I use it in line 6 (C_RedemptionController....).
It is cached in a class variable (private int requestCode; )
Is this wrong?
 

airblaster

Active Member
Licensed User
Longtime User
Hi Erel,

I believe the line you mentioned is the line that indirectly calls an Activity on the third party library.
Do I understand you correctly that this needs to called before the following code in order to retrieve the correct requestCode?:
B4X:
try {
                     Field f = BA.SharedProcessBA.class.getDeclaredField("onActivityResultCode");
                     f.setAccessible(true);
                     requestCode = f.getInt(sba) - 1; //requestCode holds the value that should be used to send the intent.               
                 } catch (Exception e) {
                     throw new RuntimeException(e);
                 }
 

NFOBoy

Active Member
Licensed User
Longtime User
That is absolutely correct, here is a call that is working for me on ActivityForResult that is inside a library :

B4X:
 void resolveConnectionResult() {
        // Try to resolve the problem
       if (bNuke) {
          return;
       }
        Log.d(TAG, "resolveConnectionResult: trying to resolve result: " + mConnectionResult);
        if (mConnectionResult.hasResolution()) {
            // This problem can be fixed. So let's try to fix it.
            Log.d(TAG, "result has resolution. Starting it.");
            int requestCode;
           // Toast.makeText(mBA.context, "Before ion", Toast.LENGTH_SHORT).show();
            ion = new IOnActivityResult() {
                @Override
                public void ResultArrived(int resultCode, Intent intent) {
                   Log.d(TAG, "Result Arrived");
                    if (resultCode == Activity.RESULT_OK) {
                       Log.d(TAG, "inside ok");
                       connectCurrentClient();
                    }else {
                       Log.d(TAG, "inside else");
                       bNuke = true;
                       giveUp();
                    }
                   
                }
            }; 
            BA pBa =  mBA.processBA;
            try {
                pBa .startActivityForResult(ion, null); //<-- passing null instead of an intent
            } catch (NullPointerException npe) {
                //required...
            }
            BA.SharedProcessBA sba = pBa.sharedProcessBA;
            try {
                Field f = BA.SharedProcessBA.class.getDeclaredField("onActivityResultCode");
                f.setAccessible(true);
                requestCode = f.getInt(sba) - 1;
                //requestCode holds the value that should be used to send the intent.
                //Toast.makeText(mBA.context, Integer.toString(requestCode), Toast.LENGTH_SHORT).show();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            try {
                // launch appropriate UI flow (which might, for example, be the
                // sign-in flow)
               Log.d(TAG, "Starting for result");
                mExpectingActivityResult = true;
                mConnectionResult.startResolutionForResult(mBA.activity, requestCode);
            } catch (SendIntentException e) {
                // Try connecting again
                Log.d(TAG, "SendIntentException.");
                connectCurrentClient();
            }
        } else {
            // It's not a problem what we can solve, so give up and show an
            // error.
            Log.d(TAG, "resolveConnectionResult: result has no resolution. Giving up.");
            giveUp();
        }
    }
 

airblaster

Active Member
Licensed User
Longtime User
Hi NFOBoy,

as far as I can see the only real difference is that you are passing requestCode directly to the library.
Guess that's also what Erel meant before.

I'll contact the library provider if there is anything they can do about that.
 

airblaster

Active Member
Licensed User
Longtime User
I've now tried a different approach, adding an activity in library.
However, the listener callback is never started.

The code looks like this:
B4X:
package de.smartshopping.coupies;

import java.lang.reflect.Field;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.Author;
import anywheresoftware.b4a.BA.ActivityObject;
import anywheresoftware.b4a.BA.DependsOn;
import anywheresoftware.b4a.IOnActivityResult;

@Author("Niklaus Stadler")
@DependsOn(values = { "coupies-framework" })
@ActivityObject
public class C_Redemption_Activity extends Activity {
   private int requestCode;
   private IOnActivityResult ion;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      Log.d("B4A", "onCreate");
      ion = new IOnActivityResult() {
         @Override
         public void ResultArrived(int resultCode, Intent intent) {
            if (resultCode == Activity.RESULT_OK) {
               Log.d("B4A", "ok");
               C_RedemptionController.redemptionController.redeemCallback(
                     C_RedemptionController.ba.activity,
                     C_Redemption_Activity.this.requestCode, resultCode,
                     intent, C_RedemptionController.listener);
            } else {
               Log.d("B4A", "nicht ok");
            }
         }
      };

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

      C_RedemptionController.redemptionController.redeemCoupon_html(this,
            C_RedemptionController.c, true);      
   }
}

B4X:
package de.smartshopping.coupies;

import android.util.Log;
import anywheresoftware.b4a.BA;
import de.coupies.framework.beans.Barcode;
import de.coupies.framework.beans.Coupon;
import de.coupies.framework.controller.redemption.RedemptionController.RedemptionListener;

 /**
       * This class implements the RedemptionListener.
       * Methods of this listener will be called by the COUPIES-Framework 
        * after successful completion of a redemption or if an error occurred.
       */
       public class C_RedemptionListener implements RedemptionListener {
             BA ba;
             String eventName;
                           
             public C_RedemptionListener(BA ba, String eventName) {
                this.ba = ba;
                this.eventName = eventName;
             }
             
             
             public void onComplete(String html) {
                /* This Method is called after the Redemption was successfully completet */
                Log.d("B4A", "onComplete");
                if (ba.subExists(eventName + "_complete")) {
                   ba.raiseEvent(ba.activity, eventName + "_complete", html);
                }
             }
             
             public void onComplete(Barcode barcode, Coupon coupon) {
                // never get called
                Log.d("B4A", "onComplete Barcode");
             }
             
             public void onComplete(Coupon coupon){
                // never get called
                Log.d("B4A", "onComplete Coupon");
             }

             public void onError(Exception e) {
                Log.d("B4A", "onError " + e.toString());
                if (ba.subExists(eventName + "_error")) {
                   ba.raiseEvent(ba.activity, eventName + "_error", e.toString());
               }
             }

             public void onCancel() {
                Log.d("B4A", "onCancel");
                if (ba.subExists(eventName + "_cancel")) {
                   ba.raiseEvent(ba.activity, eventName + "_cancel");
               }
             }
             
             public void onBadStickerRead() {
                Log.d("B4A", "onBadStickerRead");
                if (ba.subExists(eventName + "_badstickerread")) {
                   ba.raiseEvent(ba.activity, eventName + "_badstickerread");
               }
             }

       }

B4X:
package de.smartshopping.coupies;

import android.content.Intent;
import anywheresoftware.b4a.BA;
import de.coupies.framework.controller.redemption.RedemptionController;
import de.coupies.framework.beans.Coupon;
import anywheresoftware.b4a.BA.ActivityObject;
import anywheresoftware.b4a.BA.DependsOn;
import anywheresoftware.b4a.BA.Hide;
import anywheresoftware.b4a.BA.ShortName;

@ShortName("Coupies_RedemptionService")
@DependsOn(values = { "coupies-framework" })
@ActivityObject
public class C_RedemptionController {
   public static RedemptionController redemptionController;
   public static BA ba;
   public static C_RedemptionListener listener;
   public static Coupon c;

   public void Initialize(final BA ba, String eventName,
         C_PartnerSession session, C_ServiceFactory serviceFactory) {
      C_RedemptionController.redemptionController = RedemptionController
            .createInstance(session.getPartnerSession(),
                  serviceFactory.getServiceFactory());
      C_RedemptionController.ba = ba;
      C_RedemptionController.listener = new C_RedemptionListener(ba,
            eventName);
   }

   @Hide
   public RedemptionController getRedemptionController() {
      return C_RedemptionController.redemptionController;
   }

   public void redeemCoupon_html(String id, String action,
         String mAcceptSticker) {
      C_RedemptionController.c = new Coupon();
      C_RedemptionController.c.setId(Integer.parseInt(id));
      C_RedemptionController.c.setAction(Integer.parseInt(action));
      C_RedemptionController.c
            .setClosestLocationAcceptsSticker(mAcceptSticker.equals("1"));

      Intent intent = new Intent(ba.context, C_Redemption_Activity.class);
      ba.context.startActivity(intent);
   }

}
 

NFOBoy

Active Member
Licensed User
Longtime User
Do you have the original code (that calls the activityForResult?)

When I look through your code, I don't see the call using the "resultCode" that is returned when you do this:

B4X:
 BA.SharedProcessBA sba = pBa.sharedProcessBA;
        try {
            Field f = BA.SharedProcessBA.class
                    .getDeclaredField("onActivityResultCode");
            f.setAccessible(true);
            requestCode = f.getInt(sba) - 1; // requestCode holds the value that
            // should be used to send the intent.
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

That code needs to be used, and it will only call back that one instance.

For what I see here you are trying to implement the listener for CRedemptionController, and then you also have this:

B4X:
 ba.context.startActivity(intent);
Is this the piece you are trying to get the ActivityResult? If so, needs to look more like this: (with the associated setup to use ion

B4X:
ba.startActivityForResult(ion, i);

If it's a listener that you need to implement, then ion isn't what you want anyway, and probably need to do an interface inside the class that you are starting the new activity (then you can raise your events using that)

So, would be be helpful to see what you are trying to break-down in the original class(es)
 

airblaster

Active Member
Licensed User
Longtime User
Hi NFOBoy,

the problem is that I don't have the source of the library I'm wrapping.
The following call is indirectly starting the activity for result:
B4X:
C_RedemptionController.redemptionController.redeemCallback(C_RedemptionController.ba.activity, C_Redemption_Activity.this.requestCode, resultCode, intent, C_RedemptionController.listener);
Unfortunately, it doesn't support the requestCode parameter.

So I guess it's impossible to wrap this library for B4A :(
 

NFOBoy

Active Member
Licensed User
Longtime User
When I look , I think I see the listener being set here:

B4X:
public void Initialize(final BA ba, String eventName,
            C_PartnerSession session, C_ServiceFactory serviceFactory) {
        C_RedemptionController.redemptionController = RedemptionController
                .createInstance(session.getPartnerSession(),
                        serviceFactory.getServiceFactory());
        C_RedemptionController.ba = ba;
        C_RedemptionController.listener = new C_RedemptionListener(ba,
                eventName);
    }


If I have read that correctly, you set the listener to ba... don't think that is how you want to do it.


Instead set it to "this" and implement the RedemptionListener in the same class that you set to "this" (or, pass whatever class that you have implementing the listener), because ba does not have any code to actually listen for your callbacks.

That should then start activating your listeners, and then you can raise events using the ba at that point in time...
 
Top