I tried to include in-app billing in my app and for the purpose of testing, based the whole procedure on the "TrivialDrive" example for version 3 of in-app billing (and implementing the unmodified versions of the IAB files as supplied in the "util" subdirectory of the demo), but it doesn't work for me - on LogCat, just before the app terminates with an error, it gives the message "In-app billing error: Illegal state for operation (launchPurchaseFlow): IAB Helper is not set up." (right after the startRegistered() function has been fired and given me the LOG message "Register button clicked; launching purchase flow for upgrade.")...
Any idea what goes wrong here?
Here are the relevant parts of my code:
package com.mytest; (..) import com.mytest.iab.IabHelper; // the originals from the demo example, unmodified import com.mytest.iab.IabResult; import com.mytest.iab.Inventory; import com.mytest.iab.Purchase; public class Result3 extends Activity implements OnClickListener { private static final String TAG = "BillingService"; private Context mContext; boolean mIsRegistered = false; // this has already been set up for my app at the publisher's console static final String IS_REGISTERED = "myregistered"; static final int RC_REQUEST = 10001; // The helper object IabHelper mHelper; /** Call when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.result3); mContext = this; String base64EncodedPublicKey = "[my public key]"; // (from publisher's console for my app) // Create the helper, passing it our context and the public key to verify signatures with Log.d(TAG, "Creating IAB helper."); mHelper = new IabHelper(this, base64EncodedPublicKey); // enable debug logging (for a production application, you should set this to false). mHelper.enableDebugLogging(true); // Start setup. This is asynchronous and the specified listener // will be called once setup completes. Log.d(TAG, "Starting setup."); mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { public void onIabSetupFinished(IabResult result) { Log.d(TAG, "Setup finished."); if (!result.isSuccess()) { complain("Problem setting up in-app billing: " + result); return; } // Hooray, IAB is fully set up. Now, let's get an inventory of stuff we own. Log.d(TAG, "Setup successful. Querying inventory."); mHelper.queryInventoryAsync(mGotInventoryListener); } }); // Set the onClick listeners findViewById(R.id.btnPurchase).setOnClickListener(this); } // Listener that's called when we finish querying the items we own IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() { public void onQueryInventoryFinished(IabResult result, Inventory inventory) { Log.d(TAG, "Query inventory finished."); if (result.isFailure()) { complain("Failed to query inventory: " + result); return; } Log.d(TAG, "Query inventory was successful."); // Do we have the premium upgrade? mIsRegistered = inventory.hasPurchase(IS_REGISTERED); Log.d(TAG, "User is " + (mIsRegistered ? "REGISTERED" : "NOT REGISTERED")); setWaitScreen(false); Log.d(TAG, "Initial inventory query finished; enabling main UI."); } }; // User clicked the "Register" button. private void startRegistered() { Log.d(TAG, "Register button clicked; launching purchase flow for upgrade."); setWaitScreen(true); mHelper.launchPurchaseFlow(this, IS_REGISTERED, RC_REQUEST, mPurchaseFinishedListener); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + 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(TAG, "onActivityResult handled by IABUtil."); } } // Callback for when a purchase is finished IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() { public void onIabPurchaseFinished(IabResult result, Purchase purchase) { Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase); if (result.isFailure()) { // Oh noes! complain("Error purchasing: " + result); setWaitScreen(false); return; } Log.d(TAG, "Purchase successful."); if (purchase.getSku().equals(IS_REGISTERED)) { Log.d(TAG, "User has registered.."); alert("Thank you."); mIsRegistered = true; setWaitScreen(false); } } }; // We're being destroyed. It's important to dispose of the helper here! @Override public void onDestroy() { // very important: Log.d(TAG, "Destroying helper."); if (mHelper != null) mHelper.dispose(); mHelper = null; } void complain(String message) { Log.e(TAG, "**** Register Error: " + message); alert("Error: " + message); } void setWaitScreen(boolean set) { // just a dummy for now } void alert(String message) { AlertDialog.Builder bld = new AlertDialog.Builder(this); bld.setMessage(message); bld.setNeutralButton("OK", null); Log.d(TAG, "Showing alert dialog: " + message); bld.create().show(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btnPurchase: startRegistered(); break; default: break; } }
}
Here more lines from Logcat:
12-20 01:06:36.701: D/dalvikvm(299): GC_FOR_MALLOC freed 4262 objects / 308592 bytes in 84ms 12-20 01:06:36.701: D/webviewglue(299): nativeDestroy view: 0x2ea718 12-20 01:06:36.771: W/webcore(299): Can't get the viewWidth after the first layout 12-20 01:07:07.111: W/webcore(299): Can't get the viewWidth after the first layout 12-20 01:07:18.510: D/webviewglue(299): nativeDestroy view: 0x2dd458 12-20 01:07:18.510: D/dalvikvm(299): GC_FOR_MALLOC freed 6042 objects / 544504 bytes in 50ms 12-20 01:07:18.530: D/webviewglue(299): nativeDestroy view: 0x2ea8d0 12-20 01:07:18.660: D/BillingService(299): Creating IAB helper. 12-20 01:07:18.660: D/BillingService(299): Starting setup. 12-20 01:07:18.660: D/IabHelper(299): Starting in-app billing setup. 12-20 01:07:19.621: W/webcore(299): Can't get the viewWidth after the first layout 12-20 01:07:20.160: W/webcore(299): Can't get the viewWidth after the first layout 12-20 01:07:32.481: D/webviewglue(299): nativeDestroy view: 0x3f88e8 12-20 01:07:32.491: D/dalvikvm(299): GC_FOR_MALLOC freed 5798 objects / 513640 bytes in 50ms 12-20 01:07:32.511: D/BillingService(299): Register button clicked; launching purchase flow for upgrade. 12-20 01:07:32.511: E/IabHelper(299): In-app billing error: Illegal state for operation (launchPurchaseFlow): IAB helper is not set up. 12-20 01:07:32.521: D/AndroidRuntime(299): Shutting down VM 12-20 01:07:32.521: W/dalvikvm(299): threadid=1: thread exiting with uncaught exception (group=0x4001d800) 12-20 01:07:32.541: E/AndroidRuntime(299): FATAL EXCEPTION: main 12-20 01:07:32.541: E/AndroidRuntime(299): java.lang.IllegalStateException: IAB helper is not set up. Can't perform operation: launchPurchaseFlow 12-20 01:07:32.541: E/AndroidRuntime(299): at com.test_ed.iab.IabHelper.checkSetupDone(IabHelper.java:673) 12-20 01:07:32.541: E/AndroidRuntime(299): at com.test_ed.iab.IabHelper.launchPurchaseFlow(IabHelper.java:315) 12-20 01:07:32.541: E/AndroidRuntime(299): at com.test_ed.iab.IabHelper.launchPurchaseFlow(IabHelper.java:294) 12-20 01:07:32.541: E/AndroidRuntime(299): at com.test_ed.Result3.startRegistered(Result3.java:157) 12-20 01:07:32.541: E/AndroidRuntime(299): at com.test_ed.Result3.onClick(Result3.java:248) 12-20 01:07:32.541: E/AndroidRuntime(299): at android.view.View.performClick(View.java:2408) 12-20 01:07:32.541: E/AndroidRuntime(299): at android.view.View$PerformClick.run(View.java:8816) 12-20 01:07:32.541: E/AndroidRuntime(299): at android.os.Handler.handleCallback(Handler.java:587) 12-20 01:07:32.541: E/AndroidRuntime(299): at android.os.Handler.dispatchMessage(Handler.java:92) 12-20 01:07:32.541: E/AndroidRuntime(299): at android.os.Looper.loop(Looper.java:123) 12-20 01:07:32.541: E/AndroidRuntime(299): at android.app.ActivityThread.main(ActivityThread.java:4627) 12-20 01:07:32.541: E/AndroidRuntime(299): at java.lang.reflect.Method.invokeNative(Native Method) 12-20 01:07:32.541: E/AndroidRuntime(299): at java.lang.reflect.Method.invoke(Method.java:521) 12-20 01:07:32.541: E/AndroidRuntime(299): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868) 12-20 01:07:32.541: E/AndroidRuntime(299): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626) 12-20 01:07:32.541: E/AndroidRuntime(299): at dalvik.system.NativeStart.main(Native Method)