3

I have implemented some Google Play Game Services features in my Android app as a separate Activity and I am now trying to rewrite my code as an (Action Bar Sherlock) fragment. I am using the supplied GameHelper code in my fragment.

An auto sign in works correctly. A user initiated sign in fails because the StartResolutionForResult call in GameHelper returns to the Activity's onActivityResult rather than to the fragment. I have verified all this by using Log.D . My understanding of this is limited - what should I do to fix this ? I have tried passing a different context but the StartResolutionForResult seems to only accept an Activity as its context.

kablu
  • 629
  • 1
  • 7
  • 26
IanB
  • 3,489
  • 1
  • 20
  • 24

4 Answers4

8

The Google Play game services API should be tied to an Activity's lifecycle, not a Fragment's lifecycle. If your game logic is in a Fragment, you can implement onActivityResult on the Activity and call your Fragment from there. Take a look at our Type A Number Challenge sample, which, apart from being a highly exciting and addictive game</sarcasm>, demonstrates how to deal with Fragments. Each screen in Type A Number is a fragment, and they communicate with the Activity as needed.

In this particular case, all the interaction with the games API is made by the Activity. However, you could just as well make the Activity hand the GamesClient object to the Fragment so that it could implement its own logic.

In all cases, remember not to keep a persistent reference to GamesClient in the Fragment for longer than you need. It's probably best to query it from the Activity (via an interface, for instance) whenever you need it. This is to prevent it from leaking during the Activity's lifecycle.

Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
Bruno Oliveira
  • 5,056
  • 18
  • 24
  • 1
    Thanks Bruno. The solution proved easier than I had anticipated. I simply added the following code to my onActivityResult: fred fragment=(fred)getSupportFragmentManager().findFragmentByTag("tab4"); fragment.onActivityResult(request, response, data); – IanB Jun 10 '13 at 09:38
  • 1
    why the API should not be tied to Fragment's lifecycle? Why is it wrong? Is there any documentation about this? – osrl Feb 01 '16 at 12:31
  • @osrl I have the same question. I'm trying to encapsulate Nearby sharing functionality in my app in a DialogFragment, that only publishes while the dialog is shown. The inability to set a target Fragment for the result means I have to duplicate the same GoogleApiClient code in every single Activity where I want this dialog, and then manually forward callbacks in between the client and fragment that needs them. – Damien Diehl Jul 07 '16 at 21:34
4

Shifting 1<<16 is not supported anymore.

You can try call it from your Fragment

private static final int REQUEST_CHECK_SETTINGS = 0x1;

final ResolvableApiException rae = (ResolvableApiException) e;      
startIntentSenderForResult(rae.getResolution().getIntentSender(), REQUEST_CHECK_SETTINGS, null, 0, 0, 0, null);

And catch result on Fragment's onActivtyResult like this:

 @Override
    public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            // Check for the integer request code originally supplied to startIntentSenderForResult().
            case REQUEST_CHECK_SETTINGS:
                switch (resultCode) {
                    case Activity.RESULT_OK:
                        // Do smth
                        break;
                    case Activity.RESULT_CANCELED:
                        // show Error
                        break;
                }
                break;
        }
    }
A.N.R.I
  • 1,931
  • 2
  • 15
  • 20
1

You can forward the onActivityResult call to the fragment like this:

We should bitwise shift a request code by 16 bits.

public static final int REQUEST_CHECK_SETTINGS = 1<<16; //shifted 1 16 bits

Add this to the activity who owns the fragment.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
}

Note: I've figured this out from the source code of onActivityResult in FragmentActivity. It is shifting the requestCode 16 bits to the right.

/**
 * Dispatch incoming result to the correct fragment.
 */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    mFragments.noteStateNotSaved();
    int index = requestCode>>16;
    if (index != 0) {
        index--;
        final int activeFragmentsCount = mFragments.getActiveFragmentsCount();
        if (activeFragmentsCount == 0 || index < 0 || index >= activeFragmentsCount) {
            Log.w(TAG, "Activity result fragment index out of range: 0x"
                    + Integer.toHexString(requestCode));
            return;
        }
        final List<Fragment> activeFragments =
                mFragments.getActiveFragments(new ArrayList<Fragment>(activeFragmentsCount));
        Fragment frag = activeFragments.get(index);
        if (frag == null) {
            Log.w(TAG, "Activity result no fragment exists for index: 0x"
                    + Integer.toHexString(requestCode));
        } else {
            frag.onActivityResult(requestCode&0xffff, resultCode, data);
        }
        return;
    }

    super.onActivityResult(requestCode, resultCode, data);
}

Note 2: I would be glad if someone could tell me why using this method would be a bad approach

osrl
  • 8,168
  • 8
  • 36
  • 57
0

I'm not sure how similar the API for Games is compared to Nearby (I think this should work), but when creating a GoogleApiClient for Nearby, I found that the objects passed in error callbacks (Status, ConnectionResult) are conveniently Parcelable. So I created an Activity that can be launched to handle these errors, and then pass whatever result it receives back to the caller.

https://gist.github.com/damien5314/c13ce47bca035c517dfbdfb2af488a73

Usage:

Nearby.Messages.publish(mNearbyApi, mMessage)
        .setResultCallback(new ResultCallback<Status>() {
            @Override
            public void onResult(@NonNull Status status) {
                Log.d(TAG, "Nearby publish result: " + getStatusCodeString(status.getStatusCode()));
                if (status.getStatusCode() > 0) {
                    Intent intent = ResolveErrorActivity.buildIntent(getContext(), status);
                    startActivityForResult(intent, ResolveErrorActivity.REQUEST_RESOLVE_ERROR);
                }
            }
        });

As long as you call startActivityForResult from a Fragment, it should get the result as usual.

Damien Diehl
  • 383
  • 4
  • 13