it-swarm.com.de

StartIntentSenderForResult von Fragment aufrufen (Android Billing v3)

Die neue Dokumentation und der Hilfscode für Android Billing v3 verwenden startIntentSenderForResult(), wenn Sie einen Kaufablauf starten. Ich möchte einen Kaufablauf starten (und das Ergebnis erhalten) von einer Fragment.

Zum Beispiel schlägt die Dokumentation den Aufruf vor

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

und der Hilfscode Anrufe

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

was startIntentSenderForResult() aufruft.

Das Problem ist, dass der Aufruf von startIntentSenderForResult() bewirkt, dass onActivityResult() für die übergeordnete Activity und nicht für die Fragment aufgerufen wird, von der sie aufgerufen wurde (wo sich die IabHelper befindet).

Ich könnte onActivityResult() in der übergeordneten Activity empfangen und dann manuell onActivityResult() für die Fragment aufrufen, aber gibt es eine Möglichkeit, startIntentSenderForResult() von einer Fragment aus aufzurufen, die das Ergebnis direkt an onActivityResult() der Fragment zurückgibt?

53
ashughes

Ich schlage zwei Lösungen vor:

1.) Setzen Sie den IabHelper mHelper auf die Aktivität und rufen Sie den IabHelper aus dem Fragment auf.

So etwas wie:

Um diese Lösung zu verwenden, deklarieren Sie IabHelper in der Aktivität als öffentlich und verwenden Sie eine Methode, um den Launcher vom Fragment aus aufzurufen.

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.) Rufen Sie in onActivityResult das entsprechende Fragment auf, das das IabHelper-Objekt enthält. Ein geeignetes Fragment kann eine Zugriffsmethode auf das Hilfsobjekt haben.

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

1) Sie sollten Ihren resultCode (RC_REQUEST) ändern, um ihn mit einem Fragmentindex zu versehen.

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

zu 

mRequestCode = requestCode&0xffff;
9
Timur

In Bezug auf die sehr hilfreiche zweite Lösung von LEO:

Wenn Google das Problem mit startIntentSenderForResult behebt und den onActivityResult-Aufruf jetzt korrekt an das Fragment weiterleitet, sollte diese Lösung zukunftssicher sein, damit Das onActivityResult des Fragments nicht zweimal aufgerufen wird.

Ich möchte die folgende von LEO vorgeschlagene modifizierte Lösung vorschlagen.

In der übergeordneten Activity-Implementierung des Fragments:

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

Dann in der Implementierung Ihres IAB-Fragments:

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

Sie müssen Fragment und Daten an die übergeordnete Aktivität übergeben und dann das Fragment onActivityResult aus der übergeordneten Aktivität aufrufen.

so was

in fragment:

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

in übergeordneter Aktivität:

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (storeFragment != null) {
            storeFragment.onActivityResult(requestCode, resultCode, data);
        }
    }

    public void purchaseLauncher(StoreFragment storeFragment, IabHelper mHelper, String sku, int requestCode, IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener, String payload) {
        this.storeFragment = storeFragment;
        mHelper.launchPurchaseFlow(this, sku, requestCode, mPurchaseFinishedListener, payload);
    }

2
ayda

Ab SDK 24 und höher ist auch eine startIntentSenderForResult - Methode in support Fragment verfügbar, die wie beabsichtigt funktioniert. Beachten Sie, dass es einen zusätzlichen Bundle-Parameter gibt, der als Null übergeben werden kann. Der endgültige Code lautet also:

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

Natürlich müssen für API 23 und darunter die in den anderen Antworten beschriebenen Tricks verwendet werden.

2
User31689

Ich empfehle, eine generische Behandlung dieses Problems in Ihrer Basisaktivitätsklasse zu erstellen, wenn Sie darauf Zugriff haben.

Zum Beispiel:

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

Natürlich müssen Sie die ActivityResultHandler-Schnittstelle durch Ihre Fragmente implementieren und beim Aktivitätsstart registrieren.

2

Edit: Android.support.v4.app.Fragment enthält jetzt eine abwärtskompatible Version von startIntentSenderForResult(), daher ist diese Antwort veraltet.

Alte Antwort:

Ab der Unterstützungsbibliothek 23.2.0 funktioniert das Ändern von requestCode nicht mehr: FragmentActivity verfolgt jetzt die Anforderungen, die von den Fragmenten stammen. Ich habe diese Methode der FragmentActivity hinzugefügt, die die Fragment hostete (Code basierend auf 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);
    }
}

Beim Aufruf erhält nur die übergebene Fragment den Aufruf von onActivityResult().

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

RC_REQUEST ist derselbe wie beim Starten des Einkaufsablaufs

Fügen Sie dies in der Variable onActivityResult Ihrer Aktivität hinzu. Der Inventarlistenersteller wird das gewünschte Ergebnis für Sie produzieren. (Ich weiß, dass es ein temporärer Fix ist, aber für mich gearbeitet hat)).

1
Arun Salaria

wenn Sie einen Rückruf für Ihr Fragment erhalten möchten, rufen Sie super.onActivityResult() aus Ihrer Aktivität auf.

Dies wird Ihre Fragmente onActivityResult() nennen.

Vergessen Sie nicht, startIntentSenderForResult aus Ihrem Fragment-Kontext aufzurufen.

Verwenden Sie keinen Aktivitätskontext getActivity().startIntentSenderForResult

0
Nikhil Jain

Sie müssen anrufen

super.onActivityResult(requestCode, resultCode, data);

onActivityResult zu Beginn Ihrer Aktivität und Ihres Fragments, um die Ergebnisse mit den Fragmenten zu kaskadieren.

In meiner FragmentActivity liest sich dies als

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

In meinem Fall habe ich onActivityResult in Activity gemacht: 

@Override protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
        // not handled, so handle it ourselves (here's 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.");
    }

}

und das gleiche in fragment und es macht in app abrechnung funktioniert 

@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's 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.");
    }

}
0
Penzzz