com.jetheis.android.grades.billing.googleplay.GooglePlayBillingWrapper.java Source code

Java tutorial

Introduction

Here is the source code for com.jetheis.android.grades.billing.googleplay.GooglePlayBillingWrapper.java

Source

/*
 * Copyright (C) 2012 Jimmy Theis. Licensed under the MIT License:
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.jetheis.android.grades.billing.googleplay;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;

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

import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.format.DateFormat;
import android.util.Log;

import com.jetheis.android.grades.Constants;
import com.jetheis.android.grades.billing.BillingWrapper;
import com.jetheis.android.grades.billing.Security;
import com.jetheis.android.grades.billing.googleplay.GooglePlayBillingConstants.GooglePlayPurchaseState;
import com.jetheis.android.grades.billing.googleplay.GooglePlayBillingConstants.GooglePlayResponseCode;
import com.jetheis.android.grades.billing.googleplay.GooglePlayBillingService.OnGooglePlayBillingSupportResultListener;

public class GooglePlayBillingWrapper implements BillingWrapper {

    private static GooglePlayBillingWrapper sInstance;

    private Context mContext;
    private ServiceConnection mConnection;
    private Collection<OnBillingReadyListener> mOnReadyListeners;
    private Collection<OnPurchaseStateChangedListener> mOnPurchaseStateChangedListeners;
    private GooglePlayBillingService mBoundService;

    public static GooglePlayBillingWrapper initializeInstance(Context context) {
        sInstance = new GooglePlayBillingWrapper(context);

        return sInstance;
    }

    public static GooglePlayBillingWrapper getInstance() {
        if (sInstance == null) {
            throw new IllegalStateException("Billing wrapper has not been initialized");
        }

        return sInstance;
    }

    public static boolean isInstanceInitialized() {
        return sInstance != null;
    }

    public GooglePlayBillingWrapper(Context context) {
        mContext = context;
        mOnReadyListeners = new HashSet<OnBillingReadyListener>();
        mOnPurchaseStateChangedListeners = new HashSet<OnPurchaseStateChangedListener>();

        mContext.startService(new Intent(mContext, GooglePlayBillingService.class));

        mConnection = new ServiceConnection() {

            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mBoundService = ((GooglePlayBillingService.GooglePlayBillingBinder) service).getService();
                mBoundService.checkIsBillingSupported(new OnGooglePlayBillingSupportResultListener() {

                    @Override
                    public void onGooglePlayBillingSupportResultFound(boolean billingSupported) {
                        if (billingSupported) {
                            Log.i(Constants.TAG, "Google Play billing ready");

                            for (OnBillingReadyListener listener : mOnReadyListeners) {
                                listener.onBillingReady();
                            }

                        } else {
                            Log.i(Constants.TAG, "Google Play billing is not supported");

                            for (OnBillingReadyListener listener : mOnReadyListeners) {
                                listener.onBillingNotSupported();
                            }

                        }
                    }

                });

            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                mBoundService = null;
            }
        };

        mContext.bindService(new Intent(context, GooglePlayBillingService.class), mConnection,
                Context.BIND_AUTO_CREATE);
    }

    @Override
    public void requestPurchase(String itemId) {
        Log.d(Constants.TAG, "Requesting purchase of " + itemId);

        Bundle response;

        try {
            response = mBoundService.makeGooglePlayPurchaseRequest(itemId);
        } catch (RemoteException e) {
            Log.e(Constants.TAG, "RemoteException: " + e.getLocalizedMessage());
            return;
        }

        if (response.getInt(
                GooglePlayBillingConstants.GOOGLE_PLAY_BUNDLE_KEY_RESPONSE_CODE) != GooglePlayResponseCode.RESULT_OK
                        .ordinal()) {
            return;
        }

        PendingIntent pendingIntent = response
                .getParcelable(GooglePlayBillingConstants.GOOGLE_PLAY_BUNDLE_KEY_PURCHASE_INTENT);

        try {
            mContext.startIntentSender(pendingIntent.getIntentSender(), new Intent(), 0, 0, 0);
        } catch (SendIntentException e) {
            Log.e(Constants.TAG, "SendIntentException: " + e.getLocalizedMessage());
        }

    }

    @Override
    public void restorePurchases() {
        try {
            mBoundService.makeGooglePlayRestoreTransactionsRequest();
        } catch (RemoteException e) {
            Log.e(Constants.TAG, "RemoteException: " + e.getLocalizedMessage());
        }
    }

    @Override
    public void registerOnBillingReadyListener(OnBillingReadyListener onBillingReadyListener) {
        mOnReadyListeners.add(onBillingReadyListener);

        if (mBoundService != null) {
            onBillingReadyListener.onBillingReady();
        }
    }

    @Override
    public int clearOnBillingReadyListeners() {
        int result = mOnReadyListeners.size();
        mOnReadyListeners.clear();

        return result;
    }

    @Override
    public void registerOnPurchaseStateChangedListener(
            OnPurchaseStateChangedListener onPurchaseStateChangedListener) {
        mOnPurchaseStateChangedListeners.add(onPurchaseStateChangedListener);
    }

    @Override
    public int clearOnPurchaseStateChangedListeners() {
        int result = mOnPurchaseStateChangedListeners.size();
        mOnPurchaseStateChangedListeners.clear();

        return result;
    }

    public void requestPurchaseInfo(String[] notifyIds) {
        try {
            mBoundService.makeGooglePlayPurchaseInformationRequest(notifyIds);
        } catch (RemoteException e) {
            Log.e(Constants.TAG, "RemoteException: " + e.getLocalizedMessage());
            return;
        }
    }

    public void sendNotificationConformation(String[] notificationIds) {
        Log.v(Constants.TAG, "Confirming " + notificationIds.length + " notification(s)");

        try {
            mBoundService.makeGooglePlayConfirmNotificationsRequest(notificationIds);
        } catch (RemoteException e) {
            Log.e(Constants.TAG, "RemoteException: " + e.getLocalizedMessage());
        }
    }

    public void unbind() {
        if (mConnection != null) {
            mContext.unbindService(mConnection);
            mConnection = null;
        }
    }

    public void handleJsonResponse(String response, String signature) {
        Log.v(Constants.TAG, "Handling JSON response: " + response);

        if (!Security.isCorrectSignature(response, signature)) {
            Log.e(Constants.TAG, "Bad Google Play signature! Possible security breach!");
            return;
        }

        JSONObject responseJson;

        try {
            responseJson = new JSONObject(response);

            long nonce = responseJson.getLong(GooglePlayBillingConstants.GOOGLE_PLAY_JSON_KEY_NONCE);

            if (!Security.isNonceKnown(nonce)) {
                Log.e(Constants.TAG, "Bad Google Play nonce! Possible security breach!");
                return;
            }

            Log.v(Constants.TAG, "Signature and nonce OK");

            JSONArray orders = responseJson.getJSONArray(GooglePlayBillingConstants.GOOGLE_PLAY_JSON_KEY_ORDERS);

            if (orders.length() == 0) {
                Log.v(Constants.TAG, "No orders present in response");
                return;
            }

            List<String> notificationIds = new ArrayList<String>(orders.length());

            for (int i = 0; i < orders.length(); i++) {
                JSONObject order = orders.getJSONObject(i);

                String packageName = order.getString(GooglePlayBillingConstants.GOOGLE_PLAY_JSON_KEY_PACKAGE_NAME);
                if (!packageName.equals(mContext.getPackageName())) {
                    Log.e(Constants.TAG, "Bad Google Play package name! Possible security breach!");
                    return;
                }

                Log.v(Constants.TAG, "Package name OK");

                if (order.has(GooglePlayBillingConstants.GOOGLE_PLAY_JSON_KEY_NOTIFICATION_ID)) {
                    notificationIds
                            .add(order.getString(GooglePlayBillingConstants.GOOGLE_PLAY_JSON_KEY_NOTIFICATION_ID));

                }

                String productId = order.getString(GooglePlayBillingConstants.GOOGLE_PLAY_JSON_KEY_PRODUCT_ID);

                Date purchaseDate = new Date(
                        order.getLong(GooglePlayBillingConstants.GOOGLE_PLAY_JSON_KEY_PURCHASE_TIME));
                GooglePlayPurchaseState purchaseState = GooglePlayPurchaseState
                        .fromInt(order.getInt(GooglePlayBillingConstants.GOOGLE_PLAY_JSON_KEY_PURCHASE_STATE));

                if (purchaseState == GooglePlayPurchaseState.PURCHASED) {

                    Log.i(Constants.TAG, "Found record of purchase of " + productId + " from "
                            + DateFormat.getLongDateFormat(mContext).format(purchaseDate));

                    Security.setFullVersionUnlocked(true, mContext);

                    for (OnPurchaseStateChangedListener listener : mOnPurchaseStateChangedListeners) {
                        listener.onPurchaseSuccessful(productId);
                    }

                } else if (purchaseState == GooglePlayPurchaseState.CANCELLED) {
                    Log.i(Constants.TAG, "User cancelled purchase");

                    for (OnPurchaseStateChangedListener listener : mOnPurchaseStateChangedListeners) {
                        listener.onPurchaseCancelled(productId);
                    }

                } else {
                    Log.e(Constants.TAG, "Google Play purchase refunded");

                    Security.setFullVersionUnlocked(false, mContext);

                    for (OnPurchaseStateChangedListener listener : mOnPurchaseStateChangedListeners) {
                        listener.onPurchaseReturned(productId);
                    }
                }
            }

            if (notificationIds.size() > 0) {
                sendNotificationConformation(notificationIds.toArray(new String[notificationIds.size()]));
            }

        } catch (JSONException e) {
            Log.e(Constants.TAG, "JSONException: " + e.getLocalizedMessage());
        }
    }

}