web-dev-qa-db-ja.com

フラグメントからstartIntentSenderForResultを呼び出す(Android Billing v3)

新しいAndroid Billing v3のドキュメントとヘルパーコードは、購入フローを起動するときにstartIntentSenderForResult()を使用します。Fragment

たとえば、 documentation は、

_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が存在します)。

ActivityonActivityResult()を受け取り、FragmentonActivityResult()を手動で呼び出すことができますが、呼び出しを行う方法はありますかFragmentstartIntentSenderForResult()に直接結果を返すFragmentからonActivityResult()へ?

53
ashughes

私は2つの解決策を提案します:

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

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

mRequestCode = requestCode&0xffff;
9
Timur

上記のLEOの非常に役立つ2番目のソリューションについて:

GoogleがstartIntentSenderForResultの問題を修正し、onActivityResult呼び出しをフラグメントに正しくルーティングするようになった場合、フラグメントのonActivityResultが2回呼び出されないように、このソリューションを将来的に保証する必要があります。

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

次に、IABフラグメントの実装で:

/**
 * 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

フラグメントとデータを親アクティビティに渡し、親アクティビティからフラグメントonActivityResultを呼び出す必要があります。

このような

断片的に:

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

親アクティビティで:

@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

SDK 24以降には、サポートフラグメントでも利用可能な startIntentSenderForResult メソッドがあり、意図したとおりに機能します。 nullとして渡すことができる追加のBundleパラメーターがあることに注意してください。したがって、最終的なコードは次のようになります。

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

もちろん、API 23以下では、他の回答で説明されているトリックを使用する必要があります。

2
User31689

基本アクティビティクラスにアクセスできる場合は、このアクティビティの一般的な処理を作成することをお勧めします。

例えば:

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インターフェイスを実装し、アクティビティの起動時にそれらを登録する必要があります。

2

編集: _Android.support.v4.app.Fragment_にはstartIntentSenderForResult()の後方互換バージョンが含まれているため、この回答は廃止されました。

旧回答:

サポートライブラリ23.2.0の時点で、requestCodeの変更は機能しなくなりました。FragmentActivityは、そのフラグメントによって行われたリクエストを追跡するようになりました。 FragmentActivityFragmentActivity.startActivityFromFragment(Fragment, Intent, int, Bundle)に基づくコード)をホストしていたFragmentにこのメソッドを追加しました:

_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()呼び出しを受け取ります。

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

RC_REQUESTは、購入フローの起動に使用したものと同じです

これをActivityのonActivityResultに追加します。インベントリリスナーは、目的の結果を生成します(一時的な修正はわかっていますが、うまくいきました)。

1
Arun Salaria

アクティビティからsuper.onActivityResult()を呼び出すよりもフラグメントでコールバックを取得したい場合。

これにより、フラグメントonActivityResult()が呼び出されます。

また、フラグメントコンテキストからstartIntentSenderForResultを呼び出すことを忘れないでください。

アクティビティコンテキストを使用しないgetActivity().startIntentSenderForResult

0
Nikhil Jain

電話する必要があります

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

私の場合、ActivityでonActivityResultを実行しました:

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

}

断片的にも同じで、アプリの請求書作成にも役立ちます

@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