Note that there are some explanatory texts on larger screens.

plurals
  1. POAndroid cannot bind to service (In App-Billing)
    text
    copied!<p>I'm trying to implement in app-billing in my application, but I have a little problem with it. I'm using the example from android developer's site and everytime I start the activity which will connect to the billing service it's showing me a dialog that I cannot connect to server and when I press learn more it's going to a web page which is explaining me to update my android market app, but it's already the latest one. And the other thing, before implementing the code on my application I create a test application where I can connect with the same code and I don't get problems. But in my application I can't do that. Is there any connection with the developer api key, which you can use only in one application or something like that. Because I'm using the same for test and real app.</p> <p>And here is my code if you can see something which is not really like it should be :</p> <pre><code>public class StampiiStore extends Activity { String servername; int userId; int storageID; RPCCommunicator rpc; String path; Button envelope1, envelope2, envelope3; private static final String TAG = "STAMPII"; /** * The SharedPreferences key for recording whether we initialized the * database. If false, then we perform a RestoreTransactions request * to get all the purchases for this user. */ private static final String DB_INITIALIZED = "db_initialized"; private mStampiiPurchaseObserver mStampiiPurchaseObserver; private Handler mHandler; private BillingService mBillingService; private TextView mLogTextView; private Cursor mOwnedItemsCursor; private PurchaseDatabase mPurchaseDatabase; private Set&lt;String&gt; mOwnedItems = new HashSet&lt;String&gt;(); /** * The developer payload that is sent with subsequent * purchase requests. */ private static final int DIALOG_CANNOT_CONNECT_ID = 1; private static final int DIALOG_BILLING_NOT_SUPPORTED_ID = 2; @SuppressWarnings("static-access") @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.store); SystemDatabaseHelper sysDbHelper = new SystemDatabaseHelper(this, null, 1); sysDbHelper.initialize(this); ImageView icon = (ImageView) findViewById (R.id.store_img); final int collId = getIntent().getIntExtra("collection_id",0); Log.e("collId","collId : "+collId); // Getting all variables from SharedPreferences to build the right path to images servername = rpc.getCurrentServerName(this); Log.d("","Current Server Name : "+servername); userId = rpc.getUserId(this); Log.d("","User Id : "+userId); storageID = rpc.getCurrentStoragePath(this); Log.d("","storage ID : "+storageID); // TextView colltitle = (TextView) findViewById(R.id.collection_title); TextView sTitle = (TextView) findViewById(R.id.store_collId); TextView collInfo = (TextView) findViewById(R.id.store_coll_info); envelope1 = (Button) findViewById(R.id.buyone); envelope1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mBillingService.requestPurchase("android.test.purchased", ""); } }); envelope2 = (Button) findViewById(R.id.buytwo); envelope2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mBillingService.requestPurchase("android.test.canceled", ""); } }); envelope3 = (Button) findViewById(R.id.buythree); envelope3.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mBillingService.requestPurchase("com.stampii.stampii.envelopesthree", ""); } }); mHandler = new Handler(); mStampiiPurchaseObserver = new mStampiiPurchaseObserver(mHandler); mBillingService = new BillingService(); mBillingService.setContext(this); mPurchaseDatabase = new PurchaseDatabase(this); setupWidgets(); // Check if billing is supported. ResponseHandler.register(mStampiiPurchaseObserver); if (!mBillingService.checkBillingSupported()) { showDialog(DIALOG_CANNOT_CONNECT_ID); } Button back = (Button) findViewById(R.id.store_back_button); back.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this); boolean isLoggedIn = settings.getBoolean("isLoggedIn", false); ImageButton sync = (ImageButton) findViewById(R.id.sync_store); if(isLoggedIn){ sync.setVisibility(View.VISIBLE); } else if(!isLoggedIn){ sync.setVisibility(View.GONE); } sync.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(StampiiStore.this, Synchronization.class); intent.putExtra("process", 2); startActivity(intent); } }); String titleSql = "SELECT title FROM collection_lang WHERE collection_id= " + collId + " AND lang_code='en_US'"; Cursor title = sysDbHelper.executeSQLQuery(titleSql); if(title.getCount()==0){ title.close(); } else if(title.getCount()&gt;0){ for(title.move(0); title.moveToNext(); title.isAfterLast()){ String collectionTitle = title.getString(title.getColumnIndex("title")); sTitle.setText(collectionTitle); if (collectionTitle.length() &gt; 20){ String newTitle = collectionTitle.substring(0, 20); colltitle.setText(newTitle + "..."); } else { colltitle.setText(collectionTitle); } } } title.close(); String infoSql = "SELECT DISTINCT c.total_cards AS cardsCount, " + " c.cards_per_envelope AS cardsPerEnvelope " + "FROM collections AS c, collection_lang AS cl " + "WHERE c.collection_id = cl.collection_id AND c.collection_id=" + collId; Cursor info = sysDbHelper.executeSQLQuery(infoSql); if(info.getCount()==0){ info.close(); } else if (info.getCount()&gt;0){ info.moveToFirst(); int cardsCount = info.getInt(info.getColumnIndex("cardsCount")); int cardsPerEnvelope = info.getInt(info.getColumnIndex("cardsPerEnvelope")); collInfo.setText(cardsCount+" Stampii\n"+cardsPerEnvelope+" cards per envelope"); } String sqlite2 = "SELECT media_id FROM collection_media WHERE collection_id="+collId+" AND media_type_id="+3018; Cursor bg = sysDbHelper.executeSQLQuery(sqlite2); if (bg.getCount() == 0) { bg.close(); } else if (bg.getCount() &gt; 0) { for (bg.move(0); bg.moveToNext(); bg.isAfterLast()) { int objectId = Integer.parseInt(bg.getString(bg.getColumnIndex("media_id"))); String filename = "mediacollection-"+objectId+".png"; //if(storageID==1){ path = rpc.getPublicPathsInternal(servername, 3018, filename, StampiiStore.this); /*} else if(storageID==2){ path = rpc.getPublicPathsExternal(servername, 3007, objectId); }*/ } } bg.close(); BitmapFactory.Options options = new BitmapFactory.Options(); options.inTempStorage = new byte[3*1024]; Bitmap ops = BitmapFactory.decodeFile(path, options); BitmapDrawable bitmapDrawable = new BitmapDrawable(ops); icon.setBackgroundDrawable(bitmapDrawable); Button promoCode = (Button) findViewById(R.id.promo_code_btn); promoCode.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { SharedPreferences isSelectedCode = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); String isSelected = isSelectedCode.getString("isSelected", ""); Log.i("isSelected", "isSelected" + isSelected); EditText input = new EditText(StampiiStore.this); input.setText(isSelected); final int loggedOut = getIntent().getIntExtra("statement", 0); if(loggedOut==0){ new AlertDialog.Builder(getParent()) .setTitle("Promotional Code") .setView(input) .setPositiveButton("Confirm", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Log.d("AlertDialog", "Positive"); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Log.d("AlertDialog","Negative"); dialog.cancel(); } }).show(); } else if (loggedOut==1){ new AlertDialog.Builder(Collections.parentActivity) .setTitle("Promotional Code") .setView(input) .setPositiveButton("Confirm", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Log.d("AlertDialog", "Positive"); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Log.d("AlertDialog","Negative"); dialog.cancel(); } }).show(); } } }); Button savedCodes = (Button) findViewById(R.id.saved_codes_btn); savedCodes.setOnClickListener(new OnClickListener(){ public void onClick(View v){ Intent previewMessage = new Intent(getParent(), SavedCodes.class); TabGroupActivity parentActivity = (TabGroupActivity)getParent(); parentActivity.startChildActivity("Saved Codes", previewMessage); } }); } /** * Sets up the UI. */ private void setupWidgets() { //TODO: If need any changes in the UI! } /** * If the database has not been initialized, we send a * RESTORE_TRANSACTIONS request to Android Market to get the list of purchased items * for this user. This happens if the application has just been installed * or the user wiped data. We do not want to do this on every startup, rather, we want to do * only when the database needs to be initialized. */ private void restoreDatabase() { SharedPreferences prefs = getPreferences(MODE_PRIVATE); boolean initialized = prefs.getBoolean(DB_INITIALIZED, false); if (!initialized) { mBillingService.restoreTransactions(); Toast.makeText(this, "Restoring Transactions", Toast.LENGTH_LONG).show(); } } private void prependLogEntry(CharSequence cs) { SpannableStringBuilder contents = new SpannableStringBuilder(cs); contents.append('\n'); contents.append(mLogTextView.getText()); mLogTextView.setText(contents); } private void logProductActivity(String product, String activity) { SpannableStringBuilder contents = new SpannableStringBuilder(); contents.append(Html.fromHtml("&lt;b&gt;" + product + "&lt;/b&gt;: ")); contents.append(activity); prependLogEntry(contents); } //PurchaseObserver private class mStampiiPurchaseObserver extends PurchaseObserver { public mStampiiPurchaseObserver(Handler handler) { super(StampiiStore.this, handler); } @Override public void onBillingSupported(boolean supported) { if (Consts.DEBUG) { Log.i(TAG, "supported: " + supported); } if (supported) { restoreDatabase(); envelope1.setEnabled(true); envelope2.setEnabled(true); envelope3.setEnabled(true); } else { showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID); } } @Override public void onPurchaseStateChange(PurchaseState purchaseState, String itemId, int quantity, long purchaseTime, String developerPayload) { if (Consts.DEBUG) { Log.i(TAG, "onPurchaseStateChange() itemId: " + itemId + " " + purchaseState); } if (developerPayload == null) { logProductActivity(itemId, purchaseState.toString()); } else { logProductActivity(itemId, purchaseState + "\n\t" + developerPayload); } if (purchaseState == PurchaseState.PURCHASED) { mOwnedItems.add(itemId); } mOwnedItemsCursor.requery(); } @Override public void onRequestPurchaseResponse(RequestPurchase request, ResponseCode responseCode) { if (Consts.DEBUG) { Log.d(TAG, request.mProductId + ": " + responseCode); } if (responseCode == ResponseCode.RESULT_OK) { if (Consts.DEBUG) { Log.i(TAG, "purchase was successfully sent to server"); } logProductActivity(request.mProductId, "sending purchase request"); } else if (responseCode == ResponseCode.RESULT_USER_CANCELED) { if (Consts.DEBUG) { Log.i(TAG, "user canceled purchase"); } logProductActivity(request.mProductId, "dismissed purchase dialog"); } else { if (Consts.DEBUG) { Log.i(TAG, "purchase failed"); } logProductActivity(request.mProductId, "request purchase returned " + responseCode); } } @Override public void onRestoreTransactionsResponse(RestoreTransactions request, ResponseCode responseCode) { if (responseCode == ResponseCode.RESULT_OK) { if (Consts.DEBUG) { Log.d(TAG, "completed RestoreTransactions request"); } // Update the shared preferences so that we don't perform // a RestoreTransactions again. SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE); SharedPreferences.Editor edit = prefs.edit(); edit.putBoolean(DB_INITIALIZED, true); edit.commit(); } else { if (Consts.DEBUG) { Log.d(TAG, "RestoreTransactions error: " + responseCode); } } } } /** * Called when this activity becomes visible. */ @Override protected void onStart() { super.onStart(); ResponseHandler.register(mStampiiPurchaseObserver); } /** * Called when this activity is no longer visible. */ @Override protected void onStop() { super.onStop(); ResponseHandler.unregister(mStampiiPurchaseObserver); } @Override protected void onDestroy() { super.onDestroy(); mPurchaseDatabase.close(); mBillingService.unbind(); } @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_CANNOT_CONNECT_ID: return createDialog("Server cannot Connect", "Server cannot connect"); case DIALOG_BILLING_NOT_SUPPORTED_ID: return createDialog("Billing not supported", "Billing not supported"); default: return null; } } /** * Replaces the language and/or country of the device into the given string. * The pattern "%lang%" will be replaced by the device's language code and * the pattern "%region%" will be replaced with the device's country code. * * @param str the string to replace the language/country within * @return a string containing the local language and region codes */ private String replaceLanguageAndRegion(String str) { // Substitute language and or region if present in string if (str.contains("%lang%") || str.contains("%region%")) { Locale locale = Locale.getDefault(); str = str.replace("%lang%", locale.getLanguage().toLowerCase()); str = str.replace("%region%", locale.getCountry().toLowerCase()); } return str; } private Dialog createDialog(String titleId, String messageId) { String helpUrl = replaceLanguageAndRegion(getString(R.string.help_url)); if (Consts.DEBUG) { Log.i(TAG, helpUrl); } final Uri helpUri = Uri.parse(helpUrl); AlertDialog.Builder builder = null; final int loggedOut = getIntent().getIntExtra("statement", 0); if(loggedOut==0){ builder = new AlertDialog.Builder(getParent()); builder.setTitle(titleId) .setIcon(android.R.drawable.stat_sys_warning) .setMessage(messageId) .setCancelable(false) .setPositiveButton(android.R.string.ok, null) .setNegativeButton("Learn More", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(Intent.ACTION_VIEW, helpUri); startActivity(intent); } }); } else if(loggedOut==1){ builder = new AlertDialog.Builder(Collections.parentActivity); builder.setTitle(titleId) .setIcon(android.R.drawable.stat_sys_warning) .setMessage(messageId) .setCancelable(false) .setPositiveButton(android.R.string.ok, null) .setNegativeButton("Learn More", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(Intent.ACTION_VIEW, helpUri); startActivity(intent); } }); } return builder.create(); } @Override public void onRestart(){ super.onRestart(); Intent previewMessage = new Intent(StampiiStore.this, StampiiStore.class); TabGroupActivity parentActivity = (TabGroupActivity)getParent(); parentActivity.startChildActivity("StampiiStore", previewMessage); this.finish(); } } </code></pre> <p>And here is how I'm declaring the service in manifest file :</p> <pre><code>&lt;service android:name="BillingService" /&gt; &lt;receiver android:name="BillingReceiver"&gt; &lt;intent-filter&gt; &lt;action android:name="com.android.vending.billing.IN_APP_NOTIFY" /&gt; &lt;action android:name="com.android.vending.billing.RESPONSE_CODE" /&gt; &lt;action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" /&gt; &lt;/intent-filter&gt; &lt;/receiver&gt; </code></pre> <p>Any suggestions how to connect to the market?</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload