Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


Llamar startIntentSenderForResult de Fragment (Android Billing v3)

La nueva documentación de Android Billing v3 y el código de ayuda utilizan startIntentSenderForResult() al iniciar un flujo de compras. Quiero iniciar un flujo de compra (y recibir el resultado) de un Fragment .

Por ejemplo, la documentación sugiere llamar al

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

Y las llamadas de código de ayuda

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

Que llama a startIntentSenderForResult() .

El problema es que llamar a startIntentSenderForResult() hace que onActivityResult() sea ​​llamado en la Activity principal en lugar del Fragment en el que fue llamado (donde reside el IabHelper ).

Podría recibir el onActivityResult() en la Activity primaria y luego llamar manualmente al onActivityResult() en el Fragment , pero hay una manera de hacer una llamada a startIntentSenderForResult() de un Fragment que devuelve el resultado directamente a onActivityResult() del onActivityResult() ?

11 Solutions collect form web for “Llamar startIntentSenderForResult de Fragment (Android Billing v3)”

Sugiero dos soluciones:

1.) Ponga el mHelper IabHelper en la actividad y llame al IabHelper del fragmento.

Algo como:

Para utilizar esta solución, declare IabHelper como público en la actividad y utilice un método para llamar al lanzador desde el fragmento.

 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.) En onActivityResult, llame al fragmento apropiado que contiene el objeto IabHelper. El fragmento apropiado puede tener un método de acceso al objeto auxiliar.

 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) Usted debe modificar su resultCode (RC_REQUEST) para poner índice de fragmentos a la misma.

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

2) en IabHelper.launchPurchaseFlow (…)

 change mRequestCode = requestCode 

a

 mRequestCode = requestCode&0xffff; 

Respecto a la muy útil segunda solución de LEO anterior:

Si Google alguna vez soluciona el problema con startIntentSenderForResult y ahora encamina correctamente la llamada onActivityResult al fragmento, entonces esta solución debe estar a prueba de futuro para que el fragmento onActivityResult no se llame dos veces.

Me gustaría proponer la siguiente solución modificada propuesta por LEO.

En la implementación de la Actividad principal del Fragmento:

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

Luego, en la implementación de su Fragmento 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); } } 

Sugiero crear algún tipo de manejo genérico de este problema en su clase de actividad base si tiene acceso a él.

Por ejemplo:

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

Por supuesto, necesitará implementar la interfaz ActivityResultHandler por sus fragmentos y registrarlos en el inicio de la actividad.

Editar: android.support.v4.app.Fragment ahora contiene una versión compatible con versiones anteriores de startIntentSenderForResult() , por lo que esta respuesta es obsoleta.

Respuesta antigua:

A partir de la biblioteca de soporte 23.2.0, modificar el requestCode ya no funciona: FragmentActivity ahora realiza un seguimiento de las peticiones realizadas por sus fragmentos. Agregué este método a la FragmentActivity que estaba hospedando el Fragment (código basado en 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); } } 

Al llamar a esto, sólo el Fragment pasado recibirá la llamada onActivityResult() .

Necesita pasar el fragmento y los datos a la actividad de los padres, y luego llamar al fragmento onActivityResult de la actividad principal.

Me gusta esto

En fragmento:

 HomeActivity activity = (HomeActivity) getActivity(); activity.purchaseLauncher(this, mHelper, productDTO.getSku(), RC_REQUEST, mPurchaseFinishedListener, PAYLOAD); 
 if (requestCode == RC_REQUEST) { Intent intent = new Intent(ContainerAvtivity.this,ContainerAvtivity.class); startActivity(intent); finish(); } 

RC_REQUEST es el mismo que utilizó para iniciar el flujo de compras

Agregue esto en el onActivityResult de su actividad. El oyente del inventario producirá el resultado deseado para usted. (Sé que es un arreglo de la temperatura pero trabajado para mí)).

Desde SDK 24 y superior, hay un método startIntentSenderForResult disponible en Fragmento de soporte también, que funciona como se pretende. Tenga en cuenta que hay un parámetro Bundle adicional, que se puede pasar como nulo. Así, el código final será:

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

Por supuesto, para API 23 y más abajo, todavía necesitaremos usar los trucos descritos en otras respuestas.

Tienes que llamar

 super.onActivityResult(requestCode, resultCode, data); 

Al comienzo de Actividad y Fragmento onActivityResult para poner en cascada los resultados a los fragmentos.

En mi FragmentActivity esto se lee como

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

En mi caso, hice onActivityResult en Actividad:

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

Y lo mismo en fragmento y lo hace en trabajos de facturación de aplicaciones

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

Si desea obtener devolución de llamada en su fragmento que llamar a super.onActivityResult() de su actividad.

Esto llamará a sus fragmentos onActivityResult() .

Y no olvide llamar a startIntentSenderForResult del contexto de su fragmento.

No utilice el contexto de actividad getActivity().startIntentSenderForResult

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.