Wednesday, June 5, 2013

Android In-App Integration Tutorial

Android In-App Integration Tutorial

Today I am going post about In-App Billing.
In-app products are the digital goods that you offer for sale from inside your application to users. Examples of digital goods includes in-game currency, application feature upgrades that enhance the user experience, and new content for your application.

Below i explained step by step. How to Integrate In-App Billing in Your Application.

*Note.
Don't Test In-App In emulator or unsigned application.

AndroidManifest.xml file you need to give permission 
<uses-permission android:name="com.android.vending.BILLING" />

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.vj.test"
    android:versionCode="9"
    android:versionName="1.9" >

    <uses-permission android:name="com.android.vending.BILLING" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
        <activity
            android:name="com.vj.test.Mainactivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="com.vj.test.BillingService" />

        <receiver android:name="com.vj.test.BillingReceiver" >
            <intent-filter>
                <action android:name="com.android.vending.billing.IN_APP_NOTIFY" />
                <action android:name="com.android.vending.billing.RESPONSE_CODE" />
                <action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" />
            </intent-filter>
        </receiver>
    </application>

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="16" />


</manifest>


Then come to Activity class

here you need to add your in-app product ID
private static final CatalogEntry[] CATALOG = new CatalogEntry[] {
                    new CatalogEntry("Your Product ID", R.string.abc123, Managed.MANAGED),
                    new CatalogEntry("Your Product ID ", R.string.abc1234, Managed.MANAGED),
                    new CatalogEntry("Your Product ID ", R.string.ab123200, Managed.MANAGED)

       };


Full Source Code Activity Class 

package com.vj.test;

import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.SimpleCursorAdapter;
import android.widget.Spinner;
import android.widget.Toast;

import com.vj.test.BillingService.RequestPurchase;
import com.vj.test.BillingService.RestoreTransactions;
import com.vj.test.Consts.PurchaseState;
import com.vj.test.Consts.ResponseCode;

public class Mainactivity extends Activity implements OnClickListener,
             OnItemSelectedListener {
       private static final String TAG = "Dungeons";
       private static final String DB_INITIALIZED = "db_initialized";
       private DungeonsPurchaseObserver mDungeonsPurchaseObserver;
       private Handler mHandler;
       private BillingService mBillingService;
       private Button mBuyButton;
       private Spinner mSelectItemSpinner;
       private PurchaseDatabase mPurchaseDatabase;
       private Cursor mOwnedItemsCursor;
       private Set<String> mOwnedItems = new HashSet<String>();
       private String mPayloadContents = null;
       private static final int DIALOG_CANNOT_CONNECT_ID = 1;
       private static final int DIALOG_BILLING_NOT_SUPPORTED_ID = 2;
       private static final int DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID = 3;

       private enum Managed {
             MANAGED, UNMANAGED, SUBSCRIPTION
       }

       private class DungeonsPurchaseObserver extends PurchaseObserver {
             public DungeonsPurchaseObserver(Handler handler) {
                    super(Mainactivity.this, handler);
             }

             @Override
             public void onBillingSupported(boolean supported, String type) {
                    if (Consts.DEBUG) {
                           Log.i(TAG, "supported: " + supported);
                    }
                    if ((type == null) || type.equals(Consts.ITEM_TYPE_INAPP)) {
                           if (supported) {
                                 restoreDatabase();
                                 mBuyButton.setEnabled(true);
                           } else {
                                 showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID);
                           }
                    } else if (type.equals(Consts.ITEM_TYPE_SUBSCRIPTION)) {
                           mCatalogAdapter.setSubscriptionsSupported(supported);
                    } else {
                           showDialog(DIALOG_SUBSCRIPTIONS_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);

                           for (CatalogEntry e : CATALOG) {
                                 if (e.sku.equals(itemId)
                                               && e.managed.equals(Managed.SUBSCRIPTION)) {
                                 }
                           }
                    }
                    mCatalogAdapter.setOwnedItems(mOwnedItems);
                    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");
                           }
                           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);
                           }
                    }
             }
       }

       private static class CatalogEntry {
             public String sku;
             public int nameId;
             public Managed managed;

             public CatalogEntry(String sku, int nameId, Managed managed) {
                    this.sku = sku;
                    this.nameId = nameId;
                    this.managed = managed;
             }
       }

       private static final CatalogEntry[] CATALOG = new CatalogEntry[] {
                    new CatalogEntry("your product id", R.string.abc123, Managed.MANAGED),
                    new CatalogEntry("your product id", R.string.abc1234, Managed.MANAGED),
                    new CatalogEntry("your product id", R.string.ab123200, Managed.MANAGED)

       };

       private String mItemName;
       private String mSku;
       private Managed mManagedType;
       private CatalogAdapter mCatalogAdapter;

       @SuppressWarnings("deprecation")
       @Override
       public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.main);

             mHandler = new Handler();
             mDungeonsPurchaseObserver = new DungeonsPurchaseObserver(mHandler);
             mBillingService = new BillingService();
             mBillingService.setContext(this);

             mPurchaseDatabase = new PurchaseDatabase(this);
             setupWidgets();
             ResponseHandler.register(mDungeonsPurchaseObserver);
             if (!mBillingService.checkBillingSupported()) {
                    showDialog(DIALOG_CANNOT_CONNECT_ID);
             }

             if (!mBillingService
                           .checkBillingSupported(Consts.ITEM_TYPE_SUBSCRIPTION)) {
                    showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID);
             }
       }

       @Override
       protected void onStart() {
             super.onStart();
             ResponseHandler.register(mDungeonsPurchaseObserver);
             initializeOwnedItems();
       }

       @Override
       protected void onStop() {
             super.onStop();
             ResponseHandler.unregister(mDungeonsPurchaseObserver);
       }

       @Override
       protected void onDestroy() {
             super.onDestroy();
             mPurchaseDatabase.close();
             mBillingService.unbind();
       }

       @Override
       protected void onSaveInstanceState(Bundle outState) {
             super.onSaveInstanceState(outState);

       }

       @Override
       protected void onRestoreInstanceState(Bundle savedInstanceState) {
             super.onRestoreInstanceState(savedInstanceState);

       }

       @Override
       protected Dialog onCreateDialog(int id) {
             switch (id) {
             case DIALOG_CANNOT_CONNECT_ID:
                    return createDialog(R.string.cannot_connect_title,
                                 R.string.cannot_connect_message);
             case DIALOG_BILLING_NOT_SUPPORTED_ID:
                    return createDialog(R.string.billing_not_supported_title,
                                 R.string.billing_not_supported_message);
             case DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID:
                    return createDialog(R.string.subscriptions_not_supported_title,
                                 R.string.subscriptions_not_supported_message);
             default:
                    return null;
             }
       }

       private Dialog createDialog(int titleId, int 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 = new AlertDialog.Builder(this);
             builder.setTitle(titleId)
                           .setIcon(android.R.drawable.stat_sys_warning)
                           .setMessage(messageId)
                           .setCancelable(false)
                           .setPositiveButton(android.R.string.ok, null)
                           .setNegativeButton(R.string.learn_more,
                                        new DialogInterface.OnClickListener() {
                                               @Override
                                               public void onClick(DialogInterface dialog,
                                                            int which) {
                                                     Intent intent = new Intent(Intent.ACTION_VIEW,
                                                                   helpUri);
                                                     startActivity(intent);
                                               }
                                        });
             return builder.create();
       }

       private String replaceLanguageAndRegion(String str) {
             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 void setupWidgets() {

             mBuyButton = (Button) findViewById(R.id.buy_button);
             mBuyButton.setEnabled(false);
             mBuyButton.setOnClickListener(this);
             mSelectItemSpinner = (Spinner) findViewById(R.id.item_choices);
             mCatalogAdapter = new CatalogAdapter(this, CATALOG);
             mSelectItemSpinner.setAdapter(mCatalogAdapter);
             mSelectItemSpinner.setOnItemSelectedListener(this);

             mOwnedItemsCursor = mPurchaseDatabase.queryAllPurchasedItems();
             startManagingCursor(mOwnedItemsCursor);
             String[] from = new String[] {
                           PurchaseDatabase.PURCHASED_PRODUCT_ID_COL,
                           PurchaseDatabase.PURCHASED_QUANTITY_COL };
             int[] to = new int[] { R.id.item_name, R.id.item_quantity };
             new SimpleCursorAdapter(this, R.layout.item_row, mOwnedItemsCursor,
                           from, to);

       }

       private void prependLogEntry(CharSequence cs) {
             SpannableStringBuilder contents = new SpannableStringBuilder(cs);
             contents.append('\n');

       }

       private void logProductActivity(String product, String activity) {
             SpannableStringBuilder contents = new SpannableStringBuilder();
             contents.append(Html.fromHtml("<b>" + product + "</b>: "));
             contents.append(activity);
             prependLogEntry(contents);
       }

       private void restoreDatabase() {
             SharedPreferences prefs = getPreferences(MODE_PRIVATE);
             boolean initialized = prefs.getBoolean(DB_INITIALIZED, false);
             if (!initialized) {
                    mBillingService.restoreTransactions();
                    Toast.makeText(this, R.string.restoring_transactions,
                                 Toast.LENGTH_LONG).show();
             }
       }

       private void initializeOwnedItems() {
             new Thread(new Runnable() {
                    @Override
                    public void run() {
                           doInitializeOwnedItems();
                    }
             }).start();
       }

       private void doInitializeOwnedItems() {
             Cursor cursor = mPurchaseDatabase.queryAllPurchasedItems();
             if (cursor == null) {
                    return;
             }

             final Set<String> ownedItems = new HashSet<String>();
             try {
                    int productIdCol = cursor
                                  .getColumnIndexOrThrow(PurchaseDatabase.PURCHASED_PRODUCT_ID_COL);
                    while (cursor.moveToNext()) {
                           String productId = cursor.getString(productIdCol);
                           ownedItems.add(productId);
                    }
             } finally {
                    cursor.close();
             }

             mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                           mOwnedItems.addAll(ownedItems);
                           mCatalogAdapter.setOwnedItems(mOwnedItems);
                    }
             });
       }

       @Override
       public void onClick(View v) {
             if (v == mBuyButton) {
                    if (Consts.DEBUG) {
                           Log.d(TAG, "buying: " + mItemName + " sku: " + mSku);
                    }

                    if ((mManagedType != Managed.SUBSCRIPTION)
                                 && !mBillingService.requestPurchase(mSku,
                                               Consts.ITEM_TYPE_INAPP, mPayloadContents)) {
                           showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID);
                    } else if (!mBillingService.requestPurchase(mSku,
                                 Consts.ITEM_TYPE_SUBSCRIPTION, mPayloadContents)) {
                           showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID);
                    }
             }

       }

       @Override
       public void onItemSelected(AdapterView<?> parent, View view, int position,
                    long id) {
             mItemName = getString(CATALOG[position].nameId);
             mSku = CATALOG[position].sku;
             mManagedType = CATALOG[position].managed;
       }

       @Override
       public void onNothingSelected(AdapterView<?> arg0) {
       }

       private static class CatalogAdapter extends ArrayAdapter<String> {
             private CatalogEntry[] mCatalog;
             private Set<String> mOwnedItems = new HashSet<String>();
             private boolean mIsSubscriptionsSupported = false;

             public CatalogAdapter(Context context, CatalogEntry[] catalog) {
                    super(context, android.R.layout.simple_spinner_item);
                    mCatalog = catalog;
                    for (CatalogEntry element : catalog) {
                           add(context.getString(element.nameId));
                    }
                    setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
             }

             public void setOwnedItems(Set<String> ownedItems) {
                    mOwnedItems = ownedItems;
                    notifyDataSetChanged();
             }

             public void setSubscriptionsSupported(boolean supported) {
                    mIsSubscriptionsSupported = supported;
             }

             @Override
             public boolean areAllItemsEnabled() {
                    return false;
             }

             @Override
             public boolean isEnabled(int position) {

                    CatalogEntry entry = mCatalog[position];
                    if ((entry.managed == Managed.MANAGED)
                                 && mOwnedItems.contains(entry.sku)) {
                           return false;
                    }
                    if ((entry.managed == Managed.SUBSCRIPTION)
                                 && !mIsSubscriptionsSupported) {
                           return false;
                    }
                    return true;
             }

             @Override
             public View getDropDownView(int position, View convertView,
                           ViewGroup parent) {

                    View view = super.getDropDownView(position, convertView, parent);
                    view.setEnabled(isEnabled(position));
                    return view;
             }
       }
}

Then Add In-App product 
Go to Developer account  -> Add Apk Signed Apk -> Then you can able to see In-App product Navigation then click -> Add New Product - >Manage Product -> Enter Product ID and Mandatory field -> Then Make it  Active Product.
Check the below Screen Shot 

*This product  ID only you have to add in above the code for every product.



Then you need base64EncodedPublicKey 

This key you need to add Security.Java class line no 81

Cehck this screen shot 




Copy this key paste it. 

Security.Java class full source code

package com.vj.test;

import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.HashSet;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.text.TextUtils;
import android.util.Log;

import com.example.dungeons.util.Base64;
import com.example.dungeons.util.Base64DecoderException;
import com.vj.test.Consts.PurchaseState;

public class Security {
       private static final String TAG = "Security";

       private static final String KEY_FACTORY_ALGORITHM = "RSA";
       private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
       private static final SecureRandom RANDOM = new SecureRandom();

       private static HashSet<Long> sKnownNonces = new HashSet<Long>();

       public static class VerifiedPurchase {
             public PurchaseState purchaseState;
             public String notificationId;
             public String productId;
             public String orderId;
             public long purchaseTime;
             public String developerPayload;

             public VerifiedPurchase(PurchaseState purchaseState,
                           String notificationId, String productId, String orderId,
                           long purchaseTime, String developerPayload) {
                    this.purchaseState = purchaseState;
                    this.notificationId = notificationId;
                    this.productId = productId;
                    this.orderId = orderId;
                    this.purchaseTime = purchaseTime;
                    this.developerPayload = developerPayload;
             }
       }

       public static long generateNonce() {
             long nonce = RANDOM.nextLong();
             sKnownNonces.add(nonce);
             return nonce;
       }

       public static void removeNonce(long nonce) {
             sKnownNonces.remove(nonce);
       }

       public static boolean isNonceKnown(long nonce) {
             return sKnownNonces.contains(nonce);
       }

       public static ArrayList<VerifiedPurchase> verifyPurchase(String signedData,
                    String signature) {
             if (signedData == null) {
                    Log.e(TAG, "data is null");
                    return null;
             }
             if (Consts.DEBUG) {
                    Log.i(TAG, "signedData: " + signedData);
             }
             boolean verified = false;
             if (!TextUtils.isEmpty(signature)) {

                    String base64EncodedPublicKey = "Here Your Public KEey";
                    PublicKey key = Security.generatePublicKey(base64EncodedPublicKey);
                    verified = Security.verify(key, signedData, signature);
                    if (!verified) {
                           Log.w(TAG, "signature does not match data.");
                           return null;
                    }
             }

             JSONObject jObject;
             JSONArray jTransactionsArray = null;
             int numTransactions = 0;
             long nonce = 0L;
             try {
                    jObject = new JSONObject(signedData);
                    nonce = jObject.optLong("nonce");
                    jTransactionsArray = jObject.optJSONArray("orders");
                    if (jTransactionsArray != null) {
                           numTransactions = jTransactionsArray.length();
                    }
             } catch (JSONException e) {
                    return null;
             }

             if (!Security.isNonceKnown(nonce)) {
                    Log.w(TAG, "Nonce not found: " + nonce);
                    return null;
             }

             ArrayList<VerifiedPurchase> purchases = new ArrayList<VerifiedPurchase>();
             try {
                    for (int i = 0; i < numTransactions; i++) {
                           JSONObject jElement = jTransactionsArray.getJSONObject(i);
                           int response = jElement.getInt("purchaseState");
                           PurchaseState purchaseState = PurchaseState.valueOf(response);
                           String productId = jElement.getString("productId");
                           @SuppressWarnings("unused")
                           String packageName = jElement.getString("packageName");
                           long purchaseTime = jElement.getLong("purchaseTime");
                           String orderId = jElement.optString("orderId", "");
                           String notifyId = null;
                           if (jElement.has("notificationId")) {
                                 notifyId = jElement.getString("notificationId");
                           }
                           String developerPayload = jElement.optString(
                                        "developerPayload", null);

                           if ((purchaseState == PurchaseState.PURCHASED) && !verified) {
                                 continue;
                           }
                           purchases.add(new VerifiedPurchase(purchaseState, notifyId,
                                        productId, orderId, purchaseTime, developerPayload));
                    }
             } catch (JSONException e) {
                    Log.e(TAG, "JSON exception: ", e);
                    return null;
             }
             removeNonce(nonce);
             return purchases;
       }

       public static PublicKey generatePublicKey(String encodedPublicKey) {
             try {
                    byte[] decodedKey = Base64.decode(encodedPublicKey);
                    KeyFactory keyFactory = KeyFactory
                                 .getInstance(KEY_FACTORY_ALGORITHM);
                    return keyFactory
                                 .generatePublic(new X509EncodedKeySpec(decodedKey));
             } catch (NoSuchAlgorithmException e) {
                    throw new RuntimeException(e);
             } catch (InvalidKeySpecException e) {
                    Log.e(TAG, "Invalid key specification.");
                    throw new IllegalArgumentException(e);
             } catch (Base64DecoderException e) {
                    Log.e(TAG, "Base64 decoding failed.");
                    throw new IllegalArgumentException(e);
             }
       }

       public static boolean verify(PublicKey publicKey, String signedData,
                    String signature) {
             if (Consts.DEBUG) {
                    Log.i(TAG, "signature: " + signature);
             }
             Signature sig;
             try {
                    sig = Signature.getInstance(SIGNATURE_ALGORITHM);
                    sig.initVerify(publicKey);
                    sig.update(signedData.getBytes());
                    if (!sig.verify(Base64.decode(signature))) {
                           Log.e(TAG, "Signature verification failed.");
                           return false;
                    }
                    return true;
             } catch (NoSuchAlgorithmException e) {
                    Log.e(TAG, "NoSuchAlgorithmException.");
             } catch (InvalidKeyException e) {
                    Log.e(TAG, "Invalid key specification.");
             } catch (SignatureException e) {
                    Log.e(TAG, "Signature exception.");
             } catch (Base64DecoderException e) {
                    Log.e(TAG, "Base64 decoding failed.");
             }
             return false;
       }
}


Then publish the application 
Wait for several hours.

Then download application enjoy with In-App Billing













109 comments:

  1. Meine Hände wanderten in ihren Schritt und ich begann Sie zu
    fummeln. Der Mensch ist ein Gewohnheitstier. Jedoch wenn du mir unverschämt wirst, fahre ich unsere
    Krallen aus und kratze ebenso schon mal.

    Besuchen Sie auch meine Seite live strip cam

    ReplyDelete
  2. Ich stehe darаuf, gebumst zu dürften, bis ich einеn Höhepunkt nach ԁieѕem anderen bekomme.
    Willst du еbenfallѕ einmal meinen hеißen Arѕch nehmen und mich
    in meinen Hinteгn νerwöhnen. Ich hаbe schon unzählige
    Kеrlе glücklich gemасht und miсh vοn
    viеlеn Herren bumsen lassen.

    Moechteѕt du dir mеine Pаge ansehen? kleinanzeigen österreich

    ReplyDelete
  3. Hi I am so thrilled I found your blog page, I really found you by accident,
    while I was browsing on Google for something else, Regardless I am here now and would
    just like to say thanks a lot for a remarkable post and a all
    round exciting blog (I also love the theme/design), I
    don't have time to look over it all at the moment but I have book-marked it and also added your RSS feeds, so when I have time I will be back to read a lot more, Please do keep up the awesome work.

    Visit my web-site Louis vuitton pas Cher

    ReplyDelete
  4. I have done these tutorial today. Its really amazing to integrated a third party app into one app. Thanks for sharing.

    ReplyDelete
  5. In future,Android application development should be good level compared to other fields.Because Android application plays an important things in people life.
    Website Development Companies Bangalore

    ReplyDelete
  6. This is the clear lesson of all.
    But I do not understand how this script access to the full application and all buttons after purchase.
    Please reply to email weeeeeez@yandex.ru

    ReplyDelete
  7. Thanks for sharing this tutorial

    ReplyDelete
  8. Now the process of Android application development is very difficult.why i saying like this because,more competition going on this field.
    Outsourcing Website Designer

    ReplyDelete
  9. Thanks for sharing this great post dude.
    SEO Company Bangalore

    ReplyDelete
  10. A well trained android developers should be having ability to make the useful mobile application to people.
    Web Design Companies | Web Designing Company

    ReplyDelete
  11. This comment has been removed by the author.

    ReplyDelete
  12. Hy vọng các bạn sẽ có nhiều bài viết hay và thú vị hơn. Xin cảm ơn rất nhiều

    chó Bull Pháp

    bán chó bull pháp

    chó bull pháp giá bao nhiêu

    mua chó bull pháp

    ReplyDelete
  13. Aquest és un dels millors articles de la història. Gràcies per compartir. Li desitjo sort i èxit!


    giảo cổ lam 5 lá

    giảo cổ lam 7 lá

    giảo cổ lam khô

    giảo cổ lam 9 lá

    ReplyDelete
  14. Your post is just outstanding! thanks for such a post, its really going great work.

    Website Designing Company in Delhi

    ReplyDelete
  15. This comment has been removed by the author.

    ReplyDelete
  16. Vanskeligheter( van bi ) vil passere. På samme måte som( van điện từ ) regnet utenfor( van giảm áp ) vinduet, hvor nostalgisk( van xả khí ) er det som til slutt( van cửa ) vil fjerne( van công nghiệp ) himmelen.

    ReplyDelete
  17. It is amazing and wonderful to visit your Blog.Thanks for sharing this information,this is useful to us. Keep posting!

    ReplyDelete
  18. Ngày nay, bàn học không chỉ có chức năng để học nữa mà còn như một vật trang trí trong phòng của các bé. Khác hẳn với bé gái, bàn học dành cho bé trai mang vẻ năng động và tinh nghịch. Vì vậy, hãy tham khảo những mẫu bàn học dành cho bé trai dưới đây để tìm ra mẫu phù hợp cho con mình nhé!

    ReplyDelete
  19. Hello,
    I love your blog so much. i would like to inform you that there's plenty of good information on this blog, I loved to read it and I think people will get a lot more support from this blog. Thanks for sharing this informative blog, please keep it up and share some special posts with us in the future.
    about us
    Mobile app development companies in Bangalore
    we are also best Mobile app development company in Bangalore
    Mobile app development companies in India
    Mobile app development companies in India

    ReplyDelete
  20. This is a very good article and nice collection of information , would love to read more of such blogs and also know our services

    Mobile application development in India
    Mobile application development in Delhi

    ReplyDelete
  21. Hi, I Read your blog and I feel it is a very wonderful, informative blog . There are a lot of good information on this blog, I'd like to Share and I think people will get a lot of support from this blog. Thank you for sharing this informative blog, please keep up and share some unique posts with us in the future.
    Mobile app development companies in Bangalore
    Digital Marketing Company In Jaipur
    Mobile app development company in Bangalore
    Mobile app development company in India
    Mobile app development companies in India

    ReplyDelete
  22. A well informative blog which speaks about Mobile applications hands-on experience like these helps us to become an Android application developer.

    ReplyDelete
  23. Hey Nice Blog!! Thanks For Sharing!!!Wonderful blog & good post.Its really helpful for me, waiting for a more new post. Keep Blogging!
    industrial automation systems

    bockchain development


    ReplyDelete
  24. With today's modern society, the demanding needs of people are increasing. Not only beauty, eating and playing, but choosing a child's bedroom also requires a lot of factors. Because the bedroom is a place to rest, relax, study and sometimes also play a place for your baby. More: Phòng ngủ trẻ em, Giường tầng bé traiNội thất trẻ em

    ReplyDelete
  25. HI guys,
    This is a very good post, and I like it very much. For us, it's insightful and helpful. For a long time, I've been looking for this and I'm just really pleased to say that I'm working with this stuff as well. Thanks for sharing this with us.

    Digital Marketing Company in Jaipur
    Digital Marketing company In Delhi
    Digital Marketing Company in Bangalore
    SEO Company in Jaipur
    Website development Company in Jaipur
    PPC Company in Jaipur
    Digital Marketing Company in USA

    ReplyDelete
  26. Our company specializes in manufacturing and executing works of aluminum and glass, tempered glass, rolling doors ... with a commitment to the construction team of over 5 years of experience, genuine products, long-term warranty.More: Cửa nhôm xingfa, cầu thang kính cường lực

    ReplyDelete
  27. Nice post really useful information. We are the leading seo agency in dubai. Hire our seo company in dubai today for seo services in dubai

    ReplyDelete
  28. Hung was formerly an official distributor of industrial lubricants of Shell in the North. Currently, in addition to oil trading, we also trade in transportation and equipment trading. After nearly 12 years of establishment and development, Yen Hung is now a prestigious partner of nearly 10,000 large and small domestic and international factories. Main products:
    giá dầu truyền nhiệt
    dầu bánh răng
    dầu tuần hoàn
    dầu dẫn nhiệt
    dầu thủy lực shell
    mỡ bò bôi trơn chịu nhiệt

    ReplyDelete
  29. Thanks for your great post.We are the leading seo company in mumbai. Hire our seo agency in mumbai today for seo services in mumbai

    ReplyDelete
  30. I things and data online that you might not have heard before on the web.

    Hi, I found your site by means of Google

    indeed, even as searching for a comparative matter, your site arrived up, it is by all accounts incredible.

    bhai aapke liye hai. lagao or jeeto.I have bookmarked it in my google bookmarks.

    game is drawing and guisses based generally match-up,

    anyway currentlyit's arranged in best, and satta lord desawar is presently horribly eminent

    furthermore, to a great extent participating in game across the globe people ar insane with respect to this game.

    Yet, as of now the principal essential factor is that this game is satta king neglected to keep the law and

    decide guideline that to keep the conventions and rule. Presently right now people need to depend on it,

    on the off chance that the game doesn't follow the conventions they need not play the games anyway people are still

    partaking in the game,they play the games on the QT people have answer on it to quit participating

    in this kind of games, consistently help work and worked with individuals that might want facilitated,do something for

    your country do perpetually reasonable thing and be everlastingly happy.satta king

    Much obliged to you for visiting Our Website sattaking,Most most likely similar to our visitor from Google search.Maybe you are

    visting here to become more acquainted with about gali satta number today.to know gali disawar ka satta number please visting

    your landing page of site and look down . You will see boxed sorts data which is show satta number

    of particular game. There you will likewise see number of today yesterday satta number of, for example, gali disawar, new

    mumbai, disawar gold and loads of game you have wagered on the game.If you play your own gali disawar satta game and

    need us to put your own board on your website.Please satta king

    get in touch with us on showed number which you will discover in footer part of website.Apna game dalwane k liye hamse

    contact kre on google pay,phonepe, paytm jaise aap chahe pehle installment karen. aapka board moment site pr update

    kr diya jayega jaisi hey aapka installment done hota haiWe greet you wholeheartedly and exceptionally pleased to have you our

    website.satta kingPlease bookmark our site and stay tuned and refreshed to know.

    you might have perceived the cycle to play disawar satta gali game and caught wind of fix spill jodi disawar gali from

    your companions, family members. Actaully individuals favors disawar gali games as It is exceptionally well known in Indian subcontinent.

    also, is considered illegal.by having appended with our site .satta kingYou

    will discover magnificient content in regards to all the games.satta king We have staggering

    data of satta results and gali disawar diagrams as the are open for public and refreshed.

    ReplyDelete
  31. Nice post really useful information. We are the leading Ecommerce Website Designers in Bangalore . Hire our ecommerce website designers in bangalore for ecommerce website development services.

    ReplyDelete
  32. Build your Home Service On-Demand App from the best App Development Agency in India
    https://lilacinfotech.com/what-we-do/home-service-on-demand-app-development-india

    ReplyDelete
  33. Nice post really useful information. We are the leading Mobile App Development Company in Bangalore . Hire our Mobile App Development Company in Bangalore - Brandstory

    ReplyDelete
  34. Mobile apps have become an important part of every business. Mobile apps have been affecting business for quite a while and help in expanding scalability. Developing an astonishing-looking app with robust security and modern technology is a tough task. For this QuikieApps, the leading Mobile app development company has the best expertise in mobile app development. To develop the finest applications with attractive interfaces and smooth operations, you can count on us. QuikieApps is the top React Native app development company in the market. We have influenced various fields including travel, sports, eCommerce, enterprise, marketing, social media, gaming, etc. As a top Reactjs Development Services Companyprovider, QuikieApps design, and develop Web Apps and Mobile apps that get featured in the app store and win the marketplace. We build apps that get noticed. We excel in strategy, design, and development for iPhone & Android apps, and work for startups and enterprises as well.
    Web development company

    ReplyDelete

  35. Công ty Tất Phú là nhà phân phối tấm cemboard, tấm xi măng cemboard thái lan chất lượng, uy tín hiện nay tại Hà Nội:

    Địa chỉ: Tầng 3, tòa nhà Shodex, số 245 Tam Trinh, Hoàng Mai, Hà Nội

    Điện thoại: 024 66604646 – 0977 688 695

    ReplyDelete
  36. The advancement of technology has positively influenced the growth of businesses all over the planet. With the help of modern technologies like websites and mobile applications, every firm can sell its products or services online without hassle. We, QuikieApps, have acquired recognition and reputation through the reliance of our respected clients as the top Web development company. Also Quikieapps is the top front-end development company and flutter app development company in Bangalore, India, USA, UK, Dubai. Adapting the dynamic technology of the web and mobile applications is the first step to success in this modish and competitive world.

    ReplyDelete
  37. Wonderful article. It's very useful.
    It looks like you have put lot of work into this.
    An individual from any corner of the world can start up a business with the help of an eCommerce website or an eCommerce app. Every ongoing business has stepped forward to advance their current way of business to eCommerce to adapt to the fast-growing market. Now is the right time to advance your business to establish yourself as the best. Hire us!! We, QuikieApps, a top ecommerce development company in Bangalore, will help your journey towards the throne in the competitive market.

    ReplyDelete
  38. Trải qua hơn 40 năm xây dựng và phát triển, Công Ty Cổ Phần Tư Vấn Điện 3 đã không ngừng lớn mạnh, luôn hoàn thành xuất sắc mọi nhiệm vụ được giao và đã được Nhà Nước nhiều HC lao động và nhiều bằng khen.

    ReplyDelete
  39. thanks for sharing this.
    get the best blog to read -https://lilacinfotech.com/blog/90/top-android-app-development-trends-to-lookout-in-2021

    ReplyDelete
  40. Máy tính Hải long là địa chỉ bán máy tính cũ uy tín chất lượng, có đội ngũ tư vấn chuyên sâu, am hiểu tường tận về từng chi tiết của máy tính. Đặc biệt, thời gian bảo hành cũng như chính sách đổi trả, không kém việc bạn mua máy mới 100% tại các siêu thị lớn.
    Top những máy tính bàn cũ làm việc chất lượng
    Bỏ túi ngay một số mẹo kiểm tra khi mua máy tính bàn cũ
    Địa chỉ: Số 9 ngách 109 ngõ 69a Hoàng Văn Thái, Thanh Xuân, Hà Nội
    Điện thoại: 0972 105 943

    ReplyDelete
  41. Bài viết rất hay: Chúng tôi chuyên cung cấp các sản phẩm chất lượng


    Giảo cổ lam giá rẻ tại Hà Nội

    Bao nhiêu tiền 1 kg giảo cổ lam

    ReplyDelete
  42. Công Ty CP Thương Mại Kỹ Thuật Công Nghệ Đông Nam chuyên tư vấn,thiết kế, cung cấp, lắp đặt các thiết bị phòng cháy chữa cháy bao gồm: Hệ Thống Báo Cháy, Hệ Thống Chữa Cháy, Hệ Thống Chống Sét, Hệ Thống Báo Trộm.

    Xem thêm tại đây: Địa chỉ bán các thiết bị phòng cháy chữa cháy tốt nhất

    Những thiết bị phong cháy chữa cháy bạn nên biết

    Phone: 0917.911.114 - 0976.247.114

    ReplyDelete
  43. I've been looking for photos and articles on this topic over the past few days due to a school assignment, 파워볼사이트 and I'm really happy to find a post with the material I was looking for! I bookmark and will come often! Thanks :D

    ReplyDelete
  44. Công ty Cổ Phần Thảo Dược Thiên Nhiên Việt Nam trân trọng truyền thống y học cổ truyền, chắt lọc từ những bài thuốc Đông y gia truyền, các bài thuốc cổ phương với những dược liệu quý hiếm từ thiên nhiên ban tặng kết hợp cùng công nghệ hiện đại của những nhà máy chế biến Dược liệu đạt tiêu chuẩn GMP để đưa ra những sản phẩm tốt, giá trị và mang đậm tính nhân văn đến với người sử dụng.

    *** Địa chỉ liên hệ ***
    Trụ sở: Số 8, liền kề 12, Khu nhà ở cán bộ chiến sĩ Tổng cục 5 Bộ Công An, Yên Xá, Tân Triều, Thanh Trì, Hà Nội
    Hotline: 0962.364.141

    ReplyDelete
  45. Công ty Cổ Phần Tư Vấn Điện Tử 3 gồm các lĩnh vực nổi bật:

    Khảo sát địa hình

    lưới điện

    trạm biến áp

    Trụ sở chính: Số 32, Đường Ngô Thời Nhiệm, Phường Võ Thị Sáu, Quận 3, Thành phố Hồ Chí Minh

    ReplyDelete
  46. Đá Mỹ Nghệ 35 là đơn vị cung cấp mẫu lăng mộ đá chất lượng hàng đầu hiện nay:

    Mẫu mộ đá đôi đẹp

    Mẫu mộ đá đơn

    Lăng mộ đá đẹp

    Hotline: 0912.984.468

    ReplyDelete
  47. This is genuinely an awesome read for me. I have bookmarked it and I am anticipating perusing new articles. Keep doing awesome!
    SEO Company Pune
    SEO Pune

    ReplyDelete
  48. I like the valuable information you provided in your article. I am sure I will learn many new things here! Good luck
    Wall Mirror
    Whiskey Glass

    Venetian Mirror

    ReplyDelete
  49. Very interesting information I like this blog very much so much superb information. Call center software | Best call center software

    ReplyDelete
  50. Hey, Thanks for asking.

    Did you know that there are limited number of global biomedical researchers and scientists to biospecimen suppliers across the globe.

    Cancer Samples
    FFPE Samples
    Human Specimens

    ReplyDelete
  51. Hire ReactJS Developers from CronJ to leverage 9+ years of React handling and 15+ industrial experience at just $8 per hour!

    hire reactjs developers
    hire react developer

    ReplyDelete
  52. I’ve been surfing on the web more than 3 hours today, yet I never found any stunning article like yours. It’s alluringly worth for me. As I would see it,
    if all web proprietors and bloggers made puzzling substance as you did, the net will be in a general sense more beneficial than at whatever point in late memory.

    Packers and movers in Nagpur | Movers and packers in Nagpur
    home shifting services in Nagpur

    ReplyDelete
  53. Great article. Keep writing such kind of information on your blog.
    Unified Communications

    ReplyDelete

Check out this may be help you

Related Posts Plugin for WordPress, Blogger...