Calling startIntentSenderForResult from Fragment (Android Billing v3)
I suggest two solutions:
1.) Put the IabHelper mHelper on the activity and call the IabHelper from the fragment.
Something like:
To use this solution, Declare IabHelper as public in the activity and use a method to call the launcher from the Fragment.
public class MyActivity extends Activity{
public IabHelper mHelper
public purchaseLauncher(){
mHelper.launchPurchaseFlow(this, SKU_GAS, 10001,
mPurchaseFinishedListener, "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");
}
/*The finished, query and consume listeners should also be implemented in here*/
}
public class FragmentActivity extends Fragment{
MyActivity myAct = (MyActivity) getActivity();
myAct.purchaseLauncher();
}
2.) In onActivityResult, call the appropriate fragment that contains the IabHelper object. Appropriate fragment can have an access method to the helper object.
protected void onActivityResult(int requestCode, int resultCode,Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag("YourTag");
if (fragment != null)
{
((MyFragmentWithIabHelper)fragment).onActivityResult(requestCode, resultCode,data);
}
}
1) You should modify your resultCode (RC_REQUEST) to put fragment index to it.
int rc_reqest = RC_REQUEST + ((getActivity().getSupportFragmentManager().getFragments().indexOf(this)+1)<<16) ;
mHelper.launchPurchaseFlow(getActivity(), sku, rc_reqest ,mPurchaseFinishedListener, payload);
2) in IabHelper.launchPurchaseFlow(...)
change mRequestCode = requestCode
to
mRequestCode = requestCode&0xffff;
From SDK 24 and above, there is a startIntentSenderForResult method available in support Fragment also, which works as intended. Note that there is an additional Bundle parameter, which can be passed as null. Thus, final code will be:
startIntentSenderForResult(pendingIntent.getIntentSender(),
1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
Integer.valueOf(0), null);
Of course, for API 23 and below, we will still need to use the tricks described in other answers.
Regarding LEO's very helpful 2nd solution above:
If Google ever fixes the issue with startIntentSenderForResult and it now correctly routes the onActivityResult call to the fragment, then this solution should be future-proofed so that the fragment's onActivityResult doesn't get called twice.
I would like to propose the following modified solution proposed by LEO.
In the Fragment's parent Activity implementation:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
boolean handled = false;
// The following is a hack to ensure that the InAppPurchasesFragment receives
// its onActivityResult call.
//
// For more information on this issue, read here:
//
// http://stackoverflow.com/questions/14131171/calling-startintentsenderforresult-from-fragment-android-billing-v3
//
// Note: If Google ever fixes the issue with startIntentSenderForResult() and
// starts forwarding on the onActivityResult to the fragment automatically, we
// should future-proof this code so it will still work.
//
// If we don't do anything and always call super.onActivityResult, we risk
// having the billing fragment's onActivityResult called more than once for
// the same result.
//
// To accomplish this, we create a method called checkIabHelperHandleActivityResult
// in the billing fragment that returns a boolean indicating whether the result was
// handled or not. We would just call Fragment's onActivityResult method, except
// its return value is void.
//
// Then call this new method in the billing fragment here and only call
// super.onActivityResult if the billing fragment didn't handle it.
if (inAppPurchasesFragment != null)
{
handled = inAppPurchasesFragment.checkIabHelperHandleActivityResult(requestCode, resultCode, data);
}
if (!handled)
{
super.onActivityResult(requestCode, resultCode, data);
}
}
Then in your IAB Fragment's implementation:
/**
* Allow the IabHelper to process an onActivityResult if it can
*
* @param requestCode The request code
* @param resultCode The result code
* @param data The data
*
* @return true if the IABHelper handled the result, else false
*/
public boolean checkIabHelperHandleActivityResult(int requestCode, int resultCode, Intent data)
{
return (iabHelper != null) && iabHelper.handleActivityResult(requestCode, resultCode, data);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (!checkIabHelperHandleActivityResult(requestCode, resultCode, data))
{
super.onActivityResult(requestCode, resultCode, data);
}
}