Подтвердить что ты не робот

Вызов startIntentSenderForResult из фрагмента (Android Billing v3)

В новой документации Android Billing v3 и вспомогательном коде используется startIntentSenderForResult() при запуске потока покупок. Я хочу начать поток покупки (и получить результат) с Fragment.

Например, документация предлагает позвонить

startIntentSenderForResult(pendingIntent.getIntentSender(),
    1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
    Integer.valueOf(0));

а вспомогательный код вызывает

mHelper.launchPurchaseFlow(this, SKU_GAS, 10001,   
    mPurchaseFinishedListener, "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");

который вызывает startIntentSenderForResult().

Проблема заключается в том, что вызов startIntentSenderForResult() вызывает onActivityResult() для родительского Activity, а не для Fragment, из которого он был вызван (где находится IabHelper).

Я мог бы получить onActivityResult() в родительском Activity, а затем вручную вызвать onActivityResult() в Fragment, но есть способ сделать вызов startIntentSenderForResult() из Fragment, который возвращает результат непосредственно к этому Fragment onActivityResult()?

4b9b3361

Ответ 1

Я предлагаю два решения:

1.) Поместите IabHelper mHelper в действие и вызовите IabHelper из фрагмента.

Что-то вроде:

Чтобы использовать это решение, объявите IabHelper как общедоступный в действии и используйте метод вызова пусковой установки из фрагмента.

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.) В onActivityResult вызовите соответствующий фрагмент, содержащий объект IabHelper. Соответствующий фрагмент может иметь метод доступа к вспомогательному объекту.

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);
    } 
}

Ответ 2

1) Вы должны изменить свой resultCode (RC_REQUEST), чтобы поместить в него индекс фрагмента.

int rc_reqest = RC_REQUEST +  ((getActivity().getSupportFragmentManager().getFragments().indexOf(this)+1)<<16)  ;      
mHelper.launchPurchaseFlow(getActivity(), sku, rc_reqest ,mPurchaseFinishedListener, payload);

2) в IabHelper.launchPurchaseFlow(...)

change mRequestCode = requestCode

to

mRequestCode = requestCode&0xffff;

Ответ 3

В отношении LEO очень полезно второе решение выше:

Если Google когда-либо исправляет проблему с помощью startIntentSenderForResult и теперь правильно направляет вызов onActivityResult на фрагмент, тогда это решение должно быть надежно проверено, чтобы фрагмент onActivityResult не вызывается дважды.

Я хотел бы предложить следующее модифицированное решение, предложенное LEO.

В реализации родительской активности фрагмента:

@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 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 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);
    }
}

Затем в реализации IAB Fragment:

/**
 * 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);
    }
}

Ответ 4

Я предлагаю создать некоторую общую обработку этой проблемы в вашем базовом классе активности, если у вас есть к ней доступ.

Например:

public abstract class BaseActivity extends Activity {
    private List<ActivityResultHandler> mResultHandlers 
        = new ArrayList<ActivityResultHandler>();

    public void registerActivityResultHandler(ActivityResultHandler resultHandler) {
        mResultHandlers.add(resultHandler);
    }

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

        for (ActivityResultHandler resultHandler : mResultHandlers) {
            resultHandler.handle();
        }
    }
}

Конечно, вам нужно будет реализовать интерфейс ActivityResultHandler своими фрагментами и зарегистрировать их при запуске активности.

Ответ 5

Изменить: android.support.v4.app.Fragment теперь содержит обратную совместимую версию startIntentSenderForResult(), поэтому этот ответ устарел.

Старый ответ:

Как и в библиотеке поддержки 23.2.0, изменение requestCode больше не работает: FragmentActivity теперь отслеживает запросы, сделанные его фрагментами. Я добавил этот метод к FragmentActivity, в котором размещался Fragment (код на основе FragmentActivity.startActivityFromFragment(Fragment, Intent, int, Bundle)):

public void startIntentSenderFromFragment(Fragment fragment, IntentSender intent, int requestCode, @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags) throws IntentSender.SendIntentException {
    if (requestCode == -1) {
        startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask, flagsValues, extraFlags);
        return;
    }

    if ((requestCode & 0xffff0000) != 0) {
        throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
    }

    try {
        Method method = FragmentActivity.class.getDeclaredMethod("allocateRequestIndex", Fragment.class);
        method.setAccessible(true);
        int requestIndex = (int) method.invoke(this, fragment);
        startIntentSenderForResult(intent, ((requestIndex + 1) << 16) + (requestCode & 0xffff), fillInIntent, flagsMask, flagsValues, extraFlags);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

При вызове этого, только прошедший Fragment получит вызов onActivityResult().

Ответ 6

Вам нужно передать фрагмент и данные родительской активности, а затем вызвать фрагмент onActivityResult из родительской активности.

как это

в фрагменте:

HomeActivity activity = (HomeActivity) getActivity();
activity.purchaseLauncher(this, mHelper, productDTO.getSku(), RC_REQUEST, mPurchaseFinishedListener, PAYLOAD);

Ответ 7

if (requestCode == RC_REQUEST) 
{
    Intent intent = new Intent(ContainerAvtivity.this,ContainerAvtivity.class);
    startActivity(intent);
    finish();
}

RC_REQUEST такой же, как вы использовали для запуска потока покупки

Добавьте это в onActivityResult своей деятельности. Слушатель инвентаря даст желаемый результат для вас (я знаю его временное исправление, но работал у меня)).

Ответ 8

Из SDK 24 и выше существует метод startIntentSenderForResult, доступный в поддержке Fragment, который работает по назначению. Обратите внимание, что есть дополнительный параметр Bundle, который может быть передан как null. Таким образом, конечный код будет:

startIntentSenderForResult(pendingIntent.getIntentSender(),
    1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
    Integer.valueOf(0), null);

Конечно, для API 23 и ниже нам все равно придется использовать трюки, описанные в других ответах.

Ответ 9

Вам нужно позвонить

super.onActivityResult(requestCode, resultCode, data);

в начале вашей активности и фрагмента onActivityResult для каскадирования результатов для фрагментов.

В моем FragmentActivity это читается как

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    // No action here, call super to delegate to Fragments
    super.onActivityResult(requestCode, resultCode, data);
}

Ответ 10

В моем случае я сделал onActivityResult в действии:

@Override protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
        // not handled, so handle it ourselves (here where you'd
        // perform any handling of activity results not related to in-app
        // billing...
        super.onActivityResult(requestCode, resultCode, data);


    }
    else {
        Log.d(TAG, "onActivityResult handled by IABUtil.");
    }

}

и то же самое в фрагменте, что и в приложениях для выставления счетов в приложении

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {

    // Pass on the activity result to the helper for handling
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
        // not handled, so handle it ourselves (here where you'd
        // perform any handling of activity results not related to in-app
        // billing...
        super.onActivityResult(requestCode, resultCode, data);


    }
    else {
        Log.d(ITEM_SKU, "onActivityResult handled by IABUtil.");
    }

}

Ответ 11

если вы хотите получить обратный вызов на фрагменте, чем вызов super.onActivityResult() из вашей активности.

Это вызовет ваши фрагменты onActivityResult().

И не забудьте вызвать startIntentSenderForResult из контекста фрагмента.

Не использовать контекст активности getActivity().startIntentSenderForResult