Java Question is there a way to register this ResultForConnection?

NFOBoy

Active Member
Licensed User
Longtime User
When Google Game Services has an issue with the connection, this piece of code is run

B4X:
/**
     * Attempts to resolve a connection failure. This will usually involve
     * starting a UI flow that lets the user give the appropriate consents
     * necessary for sign-in to work.
     */
    void resolveConnectionResult() {
        // Try to resolve the problem
        debugLog("resolveConnectionResult: trying to resolve result: " + mConnectionResult);
        if (mConnectionResult.hasResolution()) {
            // This problem can be fixed. So let's try to fix it.
            debugLog("result has resolution. Starting it.");
            try {
                // launch appropriate UI flow (which might, for example, be the
                // sign-in flow)
                mExpectingActivityResult = true;
                mConnectionResult.startResolutionForResult(mActivity, RC_RESOLVE);
            } catch (SendIntentException e) {
                // Try connecting again
                debugLog("SendIntentException.");
                connectCurrentClient();
            }
        } else {
            // It's not a problem what we can solve, so give up and show an
            // error.
            debugLog("resolveConnectionResult: result has no resolution. Giving up.");
            giveUp();
        }
    }

I don't know how to grab the listener so that I can use onActivityResult (which then allows the code to continue the resolution process), since the startResolutionForResult takes an Activity for its argument( and not a listener that I can register from my Wrapper)

Am I able to do this?
 

NFOBoy

Active Member
Licensed User
Longtime User
Erel (or other gurus)
I have seen this
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);
        }

but if that's how I'm supposed to register the OnActivityResult, I don't see it? (looking to see the onActivityResult(int, int, Intent) part that I need to grab.

I do see BA.onActivityResult(int, int, Intent), but again, don't know how to grab it (or think that I'm supposed to grab it, I have a feeling it's what is used by the ION)

Ross
 

NFOBoy

Active Member
Licensed User
Longtime User
Martin,

I've looked at that, and if it is (which it probably is) I don't understand the callback to the Activity.

From this: (which was from that last post)
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);
        }


What do I use to Catch the return result?

I have to use this to start the ActivityForResult (although it is called startResolutionForResult) from my Class:
B4X:
mConnectionResult.startResolutionForResult(mActivity, RC_RESOLVE

I don't see anything in that call that requestCode would go into?

And I don't see where I'm supposed to put this part (according to the bottom of the link you gave, it does not show anything about how to grab the result)

B4X:
 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);

Sorry if I'm slow on this, but I get the feeling that IS the piece I'm looking for, but I don't see the whole enchilada...

Ross
 

Erel

B4X founder
Staff member
Licensed User
Longtime User

NFOBoy

Active Member
Licensed User
Longtime User
No go juice for me

Erel, here is how I understand the complete code block needs to be written, based on the post that talks about ion

B4X:
    /**
     * Attempts to resolve a connection failure. This will usually involve
     * starting a UI flow that lets the user give the appropriate consents
     * necessary for sign-in to work.
     */
    void resolveConnectionResult() {
        // Try to resolve the problem
        debugLog("resolveConnectionResult: trying to resolve result: " + mConnectionResult);
        Toast.makeText(mBA.context, "Before Has Resolution", Toast.LENGTH_SHORT).show();
        if (mConnectionResult.hasResolution()) {
            // This problem can be fixed. So let's try to fix it.
            debugLog("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) {
                    if (resultCode == Activity.RESULT_OK) {
 
                    }
                   Toast.makeText(mBA.context, "Got Connection Result", Toast.LENGTH_SHORT).show();
                }
            };
   
            try {
                mBA.startActivityForResult(ion, null); //<-- passing null instead of an intent
            } catch (NullPointerException npe) {
                //required...
            }
            BA.SharedProcessBA sba = mBA.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)
                mExpectingActivityResult = true;
                mConnectionResult.startResolutionForResult(mBA.activity, requestCode);
            } catch (SendIntentException e) {
                // Try connecting again
                debugLog("SendIntentException.");
                connectCurrentClient();
            }
        } else {
            // It's not a problem what we can solve, so give up and show an
            // error.
            debugLog("resolveConnectionResult: result has no resolution. Giving up.");
            giveUp();
        }
    }

However, when I try to run this piece:

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.
            //Toast.makeText(mBA.context, Integer.toString(requestCode), Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
(If I leave that piece out, and call the StartResolutionForResult normally, no error messages, but then again.. no registered listener :) )

I get this:

06-16 15:20:37.595: E/AndroidRuntime(26544): FATAL EXCEPTION: main
06-16 15:20:37.595: E/AndroidRuntime(26544): java.lang.RuntimeException: java.lang.NullPointerException: expected receiver of type anywheresoftware.b4a.BA$SharedProcessBA, but got null
06-16 15:20:37.595: E/AndroidRuntime(26544): at b4.pasagosoft.game.helper.GameHelper.resolveConnectionResult(GameHelper.java:855)
06-16 15:20:37.595: E/AndroidRuntime(26544): at b4.pasagosoft.game.helper.GameHelper.onConnectionFailed(GameHelper.java:752)
06-16 15:20:37.595: E/AndroidRuntime(26544): at com.google.android.gms.internal.p.a(Unknown Source)
06-16 15:20:37.595: E/AndroidRuntime(26544): at com.google.android.gms.internal.bj.a(Unknown Source)
06-16 15:20:37.595: E/AndroidRuntime(26544): at com.google.android.gms.internal.p$f.a(Unknown Source)
06-16 15:20:37.595: E/AndroidRuntime(26544): at com.google.android.gms.internal.p$f.a(Unknown Source)
06-16 15:20:37.595: E/AndroidRuntime(26544): at com.google.android.gms.internal.p$b.p(Unknown Source)
06-16 15:20:37.595: E/AndroidRuntime(26544): at com.google.android.gms.internal.p$a.handleMessage(Unknown Source)
06-16 15:20:37.595: E/AndroidRuntime(26544): at android.os.Handler.dispatchMessage(Handler.java:99)
06-16 15:20:37.595: E/AndroidRuntime(26544): at android.os.Looper.loop(Looper.java:137)
06-16 15:20:37.595: E/AndroidRuntime(26544): at android.app.ActivityThread.main(ActivityThread.java:4898)
06-16 15:20:37.595: E/AndroidRuntime(26544): at java.lang.reflect.Method.invokeNative(Native Method)
06-16 15:20:37.595: E/AndroidRuntime(26544): at java.lang.reflect.Method.invoke(Method.java:511)
06-16 15:20:37.595: E/AndroidRuntime(26544): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
06-16 15:20:37.595: E/AndroidRuntime(26544): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
06-16 15:20:37.595: E/AndroidRuntime(26544): at dalvik.system.NativeStart.main(Native Method)
06-16 15:20:37.595: E/AndroidRuntime(26544): Caused by: java.lang.NullPointerException: expected receiver of type anywheresoftware.b4a.BA$SharedProcessBA, but got null
06-16 15:20:37.595: E/AndroidRuntime(26544): at java.lang.reflect.Field.getIField(Native Method)
06-16 15:20:37.595: E/AndroidRuntime(26544): at java.lang.reflect.Field.getInt(Field.java:441)
06-16 15:20:37.595: E/AndroidRuntime(26544): at b4.pasagosoft.game.helper.GameHelper.resolveConnectionResult(GameHelper.java:851)
06-16 15:20:37.595: E/AndroidRuntime(26544): ... 15 more
06-16 15:20:37.630: E/android.os.Debug(2287): !@Dumpstate > dumpstate -k -t -z -d -o /data/log/dumpstate_app_error

I am at a loss on what to do to get this particular listener to engage.

(this is my import for the Field, as I can't tell exactly which is the right one)
import java.lang.reflect.Field;
 

NFOBoy

Active Member
Licensed User
Longtime User
It is indeed an ActivityObject (declared as @ActivityObject)

Here is the code as written now:
B4X:
 void resolveConnectionResult() {
        // Try to resolve the problem
        debugLog("resolveConnectionResult: trying to resolve result: " + mConnectionResult);
        Toast.makeText(mBA.context, "Before Has Resolution", Toast.LENGTH_SHORT).show();
        if (mConnectionResult.hasResolution()) {
            // This problem can be fixed. So let's try to fix it.
            debugLog("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) {
                    if (resultCode == Activity.RESULT_OK) {
 
                    }
                   Toast.makeText(mBA.context, "Got Connection Result", Toast.LENGTH_SHORT).show();
                }
            }; 
            try {
               mBA = mBA.processBA;
                mBA.startActivityForResult(ion, null); //<-- passing null instead of an intent
            } catch (NullPointerException npe) {
                //required...
            }
            BA.SharedProcessBA sba = mBA.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)
                mExpectingActivityResult = true;
                mConnectionResult.startResolutionForResult(mBA.activity, requestCode);
            } catch (SendIntentException e) {
                // Try connecting again
                debugLog("SendIntentException.");
                connectCurrentClient();
            }
        } else {
            // It's not a problem what we can solve, so give up and show an
            // error.
            debugLog("resolveConnectionResult: result has no resolution. Giving up.");
            giveUp();
        }
    }

Here is the result:

06-17 15:13:43.020: E/AndroidRuntime(5454): FATAL EXCEPTION: main
06-17 15:13:43.020: E/AndroidRuntime(5454): java.lang.NullPointerException
06-17 15:13:43.020: E/AndroidRuntime(5454): at com.google.android.gms.common.ConnectionResult.startResolutionForResult(Unknown Source)
06-17 15:13:43.020: E/AndroidRuntime(5454): at b4.pasagosoft.game.helper.GameHelper.resolveConnectionResult(GameHelper.java:837)
06-17 15:13:43.020: E/AndroidRuntime(5454): at b4.pasagosoft.game.helper.GameHelper.onConnectionFailed(GameHelper.java:754)
06-17 15:13:43.020: E/AndroidRuntime(5454): at com.google.android.gms.internal.p.a(Unknown Source)
06-17 15:13:43.020: E/AndroidRuntime(5454): at com.google.android.gms.internal.bj.a(Unknown Source)
06-17 15:13:43.020: E/AndroidRuntime(5454): at com.google.android.gms.internal.p$f.a(Unknown Source)
06-17 15:13:43.020: E/AndroidRuntime(5454): at com.google.android.gms.internal.p$f.a(Unknown Source)
06-17 15:13:43.020: E/AndroidRuntime(5454): at com.google.android.gms.internal.p$b.p(Unknown Source)
06-17 15:13:43.020: E/AndroidRuntime(5454): at com.google.android.gms.internal.p$a.handleMessage(Unknown Source)
06-17 15:13:43.020: E/AndroidRuntime(5454): at android.os.Handler.dispatchMessage(Handler.java:99)
06-17 15:13:43.020: E/AndroidRuntime(5454): at android.os.Looper.loop(Looper.java:137)
06-17 15:13:43.020: E/AndroidRuntime(5454): at android.app.ActivityThread.main(ActivityThread.java:4898)
06-17 15:13:43.020: E/AndroidRuntime(5454): at java.lang.reflect.Method.invokeNative(Native Method)
06-17 15:13:43.020: E/AndroidRuntime(5454): at java.lang.reflect.Method.invoke(Method.java:511)
06-17 15:13:43.020: E/AndroidRuntime(5454): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
06-17 15:13:43.020: E/AndroidRuntime(5454): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
06-17 15:13:43.020: E/AndroidRuntime(5454): at dalvik.system.NativeStart.main(Native Method)
06-17 15:13:43.225: E/android.os.Debug(2287): !@Dumpstate > dumpstate -k -t -z -d -o /data/log/dumpstate_app_error


I thought maybe I should create a temporary BA object by doing this
B4X:
BA tempBA = mBA.processBA;
but that is not the issue either.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Your code should be:
B4X:
void resolveConnectionResult() {
        // Try to resolve the problem
        debugLog("resolveConnectionResult: trying to resolve result: " + mConnectionResult);
        Toast.makeText(mBA.context, "Before Has Resolution", Toast.LENGTH_SHORT).show();
        if (mConnectionResult.hasResolution()) {
            // This problem can be fixed. So let's try to fix it.
            debugLog("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) {
                    if (resultCode == Activity.RESULT_OK) {
 
                    }
                   Toast.makeText(mBA.context, "Got Connection Result", Toast.LENGTH_SHORT).show();
                }
            }; 
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)
                mExpectingActivityResult = true;
                mConnectionResult.startResolutionForResult(mBA.activity, requestCode);
            } catch (SendIntentException e) {
                // Try connecting again
                debugLog("SendIntentException.");
                connectCurrentClient();
            }
        } else {
            // It's not a problem what we can solve, so give up and show an
            // error.
            debugLog("resolveConnectionResult: result has no resolution. Giving up.");
            giveUp();
        }
    }
 

NFOBoy

Active Member
Licensed User
Longtime User
Erel, awesome... works just like the Google Samples now.... :)

(off to study AbsObjectWrapper for the DataBuffers)
 

NFOBoy

Active Member
Licensed User
Longtime User
Spoke to soon

Erel,

after fighting that code for so long, I forgot why it was so important. I can get the login process to work correctly when there is a valid connection. But if there is not, the OnActivityResult that is activated by this piece does this:

B4X:
    public void onActivityResult(int requestCode, int responseCode, Intent intent) {
       Toast.makeText(getContext(), "ActivityResult running", Toast.LENGTH_LONG).show();
        if (requestCode == RC_RESOLVE) {
            // We're coming back from an activity that was launched to resolve a
            // connection
            // problem. For example, the sign-in UI.
            mExpectingActivityResult = false;
            debugLog("onActivityResult, req " + requestCode + " response " + responseCode);
            if (responseCode == Activity.RESULT_OK) {
                // Ready to try to connect again.
                debugLog("responseCode == RESULT_OK. So connecting.");
                connectCurrentClient();
            } else {
                // Whatever the problem we were trying to solve, it was not
                // solved.
                // So give up and show an error message.
                debugLog("responseCode != RESULT_OK, so not reconnecting.");
                giveUp();
            }
        }
    }

I am now using the code block from above here, and it does now at least Start the connection activity with no errors, but, the ion listener is not being activated:

B4X:
    /**
     * Attempts to resolve a connection failure. This will usually involve
     * starting a UI flow that lets the user give the appropriate consents
     * necessary for sign-in to work.
     */
    void resolveConnectionResult() {
        // Try to resolve the problem
        debugLog("resolveConnectionResult: trying to resolve result: " + mConnectionResult);
        Toast.makeText(mBA.context, "Before Has Resolution", Toast.LENGTH_SHORT).show();
        if (mConnectionResult.hasResolution()) {
            // This problem can be fixed. So let's try to fix it.
            debugLog("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) {
                    if (resultCode == Activity.RESULT_OK) {
                       Toast.makeText(mBA.context, "Got OK Result", Toast.LENGTH_SHORT).show();
                       
                    }else {
                       Toast.makeText(mBA.context, "Got Bad Result", Toast.LENGTH_SHORT).show();
                    }
                   
                }
            }; 
            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)
                mExpectingActivityResult = true;
                mConnectionResult.startResolutionForResult(mBA.activity, requestCode);
            } catch (SendIntentException e) {
                // Try connecting again
                debugLog("SendIntentException.");
                connectCurrentClient();
            }
        } else {
            // It's not a problem what we can solve, so give up and show an
            // error.
            debugLog("resolveConnectionResult: result has no resolution. Giving up.");
            giveUp();
        }
    }

The big problem comes when trying to login, and there is a problem (no internet connection, whatever). Without the listener, I am getting a death loop, and there is no way for me to see what the result code is that is being returned to OnActivityResult.

My current workaround is to detect if there is a problem a couple of times, and end the connection there. But, when the connection does become live again, my reconnect is not very robust (it takes a couple of times for everything to re-sync)

Now part of this could be the onStart and onStop methods (which if I understand correctly, need to be implemented), and I put the onStart in my Activity_Resume, and onStop in Activity_Pause.

B4X:
  /** Call this method from your Activity's onStart(). */
    public void onStart(BA ba, Activity act, Boolean AutoSignIn) {
        mActivity = act;
        mBA = ba;
        debugLog("onStart.");
        
        if(mExpectingActivityResult && AutoSignIn  && !lastTry) {
           connectCurrentClient();
        } else if (mExpectingActivityResult) {
            // this Activity is starting because the UI flow we launched to
            // resolve a connection problem has just returned. In this case,
            // we should NOT automatically reconnect the client, since
            // onActivityResult will handle that.
           Toast.makeText(getContext(), "mExpectingActivityResult", Toast.LENGTH_LONG).show();
            debugLog("onStart: won't connect because we're expecting activity result.");
            resolveConnectionResult();
        } else if (!AutoSignIn) {
            // The user specifically signed out, so don't attempt to sign in
            // automatically. If the user wants to sign in, they will click
            // the sign-in button, at which point we will try to sign in.
           Toast.makeText(getContext(), "No auto Sign in", Toast.LENGTH_LONG).show();
            debugLog("onStart: not signing in because user specifically signed out.");
        } else {
            // Attempt to connect the clients.
            debugLog("onStart: connecting clients.");
            Toast.makeText(getContext(), "starting Connections", Toast.LENGTH_LONG).show();
            startConnections();
        }
    }

    /** Call this method from your Activity's onStop(). */
    public void onStop() {
        debugLog("onStop: disconnecting clients.");

        // disconnect the clients -- this is very important (prevents resource
        // leaks!)
        killConnections(CLIENT_ALL);

        // no longer signed in
        mSignedIn = false;
        mSignInError = false;

        // destroy progress dialog -- we create it again when needed
        dismissDialog();
        mProgressDialog = null;

        // let go of the Activity reference
        mActivity = null;
        mBA = null;
    }

But I'm not sure until I can get the ion listener to actually engage, so I can get the resultcode.

Ross
 

NFOBoy

Active Member
Licensed User
Longtime User
Note to self, Toast messages do not work inside of the ion callback...

It is indeed working. But this does raise a new issue that I will ask in a new question.
 
Top