gt:             if (mPurchaseListener != null) {
context: /* Copyright (c) 2012 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.nego.screenoff.util;
  
  import android.app.Activity;
  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.Handler;
  import android.os.IBinder;
  import android.os.RemoteException;
  import android.text.TextUtils;
  import android.util.Log;
  
  import com.android.vending.billing.IInAppBillingService;
  
  import org.json.JSONException;
  
  import java.util.ArrayList;
  import java.util.List;
  
  
  /**
   * Provides convenience methods for in-app billing. You can create one instance of this
   * class for your application and use it to process in-app billing operations.
   * It provides synchronous (blocking) and asynchronous (non-blocking) methods for
   * many common in-app billing operations, as well as automatic signature
   * verification.
   *
   * After instantiating, you must perform setup in order to start using the object.
   * To perform setup, call the {@link #startSetup} method and provide a listener;
   * that listener will be notified when setup is complete, after which (and not before)
   * you may call other methods.
   *
   * After setup is complete, you will typically want to request an inventory of owned
   * items and subscriptions. See {@link #queryInventory}, {@link #queryInventoryAsync}
   * and related methods.
   *
   * When you are done with this object, don't forget to call {@link #dispose}
   * to ensure proper cleanup. This object holds a binding to the in-app billing
   * service, which will leak unless you dispose of it correctly. If you created
   * the object on an Activity's onCreate method, then the recommended
   * place to dispose of it is the Activity's onDestroy method.
   *
   * A note about threading: When using this object from a background thread, you may
   * call the blocking versions of methods; when using from a UI thread, call
   * only the asynchronous versions and handle the results via callbacks.
   * Also, notice that you can only call one asynchronous operation at a time;
   * attempting to start a second asynchronous operation while the first one
   * has not yet completed will result in an exception being thrown.
   *
   * @author Bruno Oliveira (Google)
   *
   */
  public class IabHelper {
      // Is debug logging enabled?
      boolean mDebugLog = false;
      String mDebugTag = "IabHelper";
  
      // Is setup done?
      boolean mSetupDone = false;
  
      // Has this object been disposed of? (If so, we should ignore callbacks, etc)
      boolean mDisposed = false;
  
      // Are subscriptions supported?
      boolean mSubscriptionsSupported = false;
  
      // Is an asynchronous operation in progress?
      // (only one at a time can be in progress)
      boolean mAsyncInProgress = false;
  
      // (for logging/debugging)
      // if mAsyncInProgress == true, what asynchronous operation is in progress?
      String mAsyncOperation = "";
  
      // Context we were passed during initialization
      Context mContext;
  
      // Connection to the service
      IInAppBillingService mService;
      ServiceConnection mServiceConn;
  
      // The request code used to launch purchase flow
      int mRequestCode;
  
      // The item type of the current purchase flow
      String mPurchasingItemType;
  
      // Public key for verifying signature, in base64 encoding
      String mSignatureBase64 = null;
  
      // Billing response codes
      public static final int BILLING_RESPONSE_RESULT_OK = 0;
      public static final int BILLING_RESPONSE_RESULT_USER_CANCELED = 1;
      public static final int BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE = 3;
      public static final int BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE = 4;
      public static final int BILLING_RESPONSE_RESULT_DEVELOPER_ERROR = 5;
      public static final int BILLING_RESPONSE_RESULT_ERROR = 6;
      public static final int BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED = 7;
      public static final int BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED = 8;
  
      // IAB Helper error codes
      public static final int IABHELPER_ERROR_BASE = -1000;
      public static final int IABHELPER_REMOTE_EXCEPTION = -1001;
      public static final int IABHELPER_BAD_RESPONSE = -1002;
      public static final int IABHELPER_VERIFICATION_FAILED = -1003;
      public static final int IABHELPER_SEND_INTENT_FAILED = -1004;
      public static final int IABHELPER_USER_CANCELLED = -1005;
      public static final int IABHELPER_UNKNOWN_PURCHASE_RESPONSE = -1006;
      public static final int IABHELPER_MISSING_TOKEN = -1007;
      public static final int IABHELPER_UNKNOWN_ERROR = -1008;
      public static final int IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE = -1009;
      public static final int IABHELPER_INVALID_CONSUMPTION = -1010;
  
      // Keys for the responses from InAppBillingService
      public static final String RESPONSE_CODE = "RESPONSE_CODE";
      public static final String RESPONSE_GET_SKU_DETAILS_LIST = "DETAILS_LIST";
      public static final String RESPONSE_BUY_INTENT = "BUY_INTENT";
      public static final String RESPONSE_INAPP_PURCHASE_DATA = "INAPP_PURCHASE_DATA";
      public static final String RESPONSE_INAPP_SIGNATURE = "INAPP_DATA_SIGNATURE";
      public static final String RESPONSE_INAPP_ITEM_LIST = "INAPP_PURCHASE_ITEM_LIST";
      public static final String RESPONSE_INAPP_PURCHASE_DATA_LIST = "INAPP_PURCHASE_DATA_LIST";
      public static final String RESPONSE_INAPP_SIGNATURE_LIST = "INAPP_DATA_SIGNATURE_LIST";
      public static final String INAPP_CONTINUATION_TOKEN = "INAPP_CONTINUATION_TOKEN";
  
      // Item types
      public static final String ITEM_TYPE_INAPP = "inapp";
      public static final String ITEM_TYPE_SUBS = "subs";
  
      // some fields on the getSkuDetails response bundle
      public static final String GET_SKU_DETAILS_ITEM_LIST = "ITEM_ID_LIST";
      public static final String GET_SKU_DETAILS_ITEM_TYPE_LIST = "ITEM_TYPE_LIST";
  
      /**
       * Creates an instance. After creation, it will not yet be ready to use. You must perform
       * setup by calling {@link #startSetup} and wait for setup to complete. This constructor does not
       * block and is safe to call from a UI thread.
       *
       * @param ctx Your application or Activity context. Needed to bind to the in-app billing service.
       * @param base64PublicKey Your application's public key, encoded in base64.
       *     This is used for verification of purchase signatures. You can find your app's base64-encoded
       *     public key in your application's page on Google Play Developer Console. Note that this
       *     is NOT your "developer public key".
       */
      public IabHelper(Context ctx, String base64PublicKey) {
          mContext = ctx.getApplicationContext();
          mSignatureBase64 = base64PublicKey;
          logDebug("IAB helper created.");
      }
  
      /**
       * Enables or disable debug logging through LogCat.
       */
      public void enableDebugLogging(boolean enable, String tag) {
          checkNotDisposed();
          mDebugLog = enable;
          mDebugTag = tag;
      }
  
      public void enableDebugLogging(boolean enable) {
          checkNotDisposed();
          mDebugLog = enable;
      }
  
      /**
       * Callback for setup process. This listener's {@link #onIabSetupFinished} method is called
       * when the setup process is complete.
       */
      public interface OnIabSetupFinishedListener {
          /**
           * Called to notify that setup is complete.
           *
           * @param result The result of the setup process.
           */
          public void onIabSetupFinished(IabResult result);
      }
  
      /**
       * Starts the setup process. This will start up the setup process asynchronously.
       * You will be notified through the listener when the setup process is complete.
       * This method is safe to call from a UI thread.
       *
       * @param listener The listener to notify when the setup process is complete.
       */
      public void startSetup(final OnIabSetupFinishedListener listener) {
          // If already set up, can't do it again.
          checkNotDisposed();
          if (mSetupDone) throw new IllegalStateException("IAB helper is already set up.");
  
          // Connection to IAB service
          logDebug("Starting in-app billing setup.");
          mServiceConn = new ServiceConnection() {
              @Override
              public void onServiceDisconnected(ComponentName name) {
                  logDebug("Billing service disconnected.");
                  mService = null;
              }
  
              @Override
              public void onServiceConnected(ComponentName name, IBinder service) {
                  if (mDisposed) return;
                  logDebug("Billing service connected.");
                  mService = IInAppBillingService.Stub.asInterface(service);
                  String packageName = mContext.getPackageName();
                  try {
                      logDebug("Checking for in-app billing 3 support.");
  
                      // check for in-app billing v3 support
                      int response = mService.isBillingSupported(3, packageName, ITEM_TYPE_INAPP);
                      if (response != BILLING_RESPONSE_RESULT_OK) {
                          if (listener != null) listener.onIabSetupFinished(new IabResult(response,
                                  "Error checking for billing v3 support."));
  
                          // if in-app purchases aren't supported, neither are subscriptions.
                          mSubscriptionsSupported = false;
                          return;
                      }
                      logDebug("In-app billing version 3 supported for " + packageName);
  
                      // check for v3 subscriptions support
                      response = mService.isBillingSupported(3, packageName, ITEM_TYPE_SUBS);
                      if (response == BILLING_RESPONSE_RESULT_OK) {
                          logDebug("Subscriptions AVAILABLE.");
                          mSubscriptionsSupported = true;
                      }
                      else {
                          logDebug("Subscriptions NOT AVAILABLE. Response: " + response);
                      }
  
                      mSetupDone = true;
                  }
                  catch (RemoteException e) {
                      if (listener != null) {
                          listener.onIabSetupFinished(new IabResult(IABHELPER_REMOTE_EXCEPTION,
                                                      "RemoteException while setting up in-app billing."));
                      }
                      e.printStackTrace();
                      return;
                  }
  
                  if (listener != null) {
                      listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup successful."));
                  }
              }
          };
  
          Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
          serviceIntent.setPackage("com.android.vending");
          if (!mContext.getPackageManager().queryIntentServices(serviceIntent, 0).isEmpty()) {
              // service available to handle that Intent
              mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
          }
          else {
              // no service available to handle that Intent
              if (listener != null) {
                  listener.onIabSetupFinished(
                          new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE,
                          "Billing service unavailable on device."));
              }
          }
      }
  
      /**
       * Dispose of object, releasing resources. It's very important to call this
       * method when you are done with this object. It will release any resources
       * used by it such as service connections. Naturally, once the object is
       * disposed of, it can't be used again.
       */
      public void dispose() {
          logDebug("Disposing.");
          mSetupDone = false;
          if (mServiceConn != null) {
              logDebug("Unbinding from service.");
              if (mContext != null) mContext.unbindService(mServiceConn);
          }
          mDisposed = true;
          mContext = null;
          mServiceConn = null;
          mService = null;
          mPurchaseListener = null;
      }
  
      private void checkNotDisposed() {
          if (mDisposed) throw new IllegalStateException("IabHelper was disposed of, so it cannot be used.");
      }
  
      /** Returns whether subscriptions are supported. */
      public boolean subscriptionsSupported() {
          checkNotDisposed();
          return mSubscriptionsSupported;
      }
  
  
      /**
       * Callback that notifies when a purchase is finished.
       */
      public interface OnIabPurchaseFinishedListener {
          /**
           * Called to notify that an in-app purchase finished. If the purchase was successful,
           * then the sku parameter specifies which item was purchased. If the purchase failed,
           * the sku and extraData parameters may or may not be null, depending on how far the purchase
           * process went.
           *
           * @param result The result of the purchase.
           * @param info The purchase information (null if purchase failed)
           */
          public void onIabPurchaseFinished(IabResult result, Purchase info);
      }
  
      // The listener registered on launchPurchaseFlow, which we have to call back when
      // the purchase finishes
      OnIabPurchaseFinishedListener mPurchaseListener;
  
      public void launchPurchaseFlow(Activity act, String sku, int requestCode, OnIabPurchaseFinishedListener listener) {
          launchPurchaseFlow(act, sku, requestCode, listener, "");
      }
  
      public void launchPurchaseFlow(Activity act, String sku, int requestCode,
              OnIabPurchaseFinishedListener listener, String extraData) {
          launchPurchaseFlow(act, sku, ITEM_TYPE_INAPP, requestCode, listener, extraData);
      }
  
      public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode,
              OnIabPurchaseFinishedListener listener) {
          launchSubscriptionPurchaseFlow(act, sku, requestCode, listener, "");
      }
  
      public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode,
              OnIabPurchaseFinishedListener listener, String extraData) {
          launchPurchaseFlow(act, sku, ITEM_TYPE_SUBS, requestCode, listener, extraData);
      }
  
      /**
       * Initiate the UI flow for an in-app purchase. Call this method to initiate an in-app purchase,
       * which will involve bringing up the Google Play screen. The calling activity will be paused while
       * the user interacts with Google Play, and the result will be delivered via the activity's
       * {@link Activity#onActivityResult} method, at which point you must call
       * this object's {@link #handleActivityResult} method to continue the purchase flow. This method
       * MUST be called from the UI thread of the Activity.
       *
       * @param act The calling activity.
       * @param sku The sku of the item to purchase.
       * @param itemType indicates if it's a product or a subscription (ITEM_TYPE_INAPP or ITEM_TYPE_SUBS)
       * @param requestCode A request code (to differentiate from other responses --
       *     as in {@link Activity#startActivityForResult}).
       * @param listener The listener to notify when the purchase process finishes
       * @param extraData Extra data (developer payload), which will be returned with the purchase data
       *     when the purchase completes. This extra data will be permanently bound to that purchase
       *     and will always be returned when the purchase is queried.
       */
      public void launchPurchaseFlow(Activity act, String sku, String itemType, int requestCode,
                          OnIabPurchaseFinishedListener listener, String extraData) {
          checkNotDisposed();
          checkSetupDone("launchPurchaseFlow");
          flagStartAsync("launchPurchaseFlow");
          IabResult result;
  
          if (itemType.equals(ITEM_TYPE_SUBS) && !mSubscriptionsSupported) {
              IabResult r = new IabResult(IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE,
                      "Subscriptions are not available.");
              flagEndAsync();
              if (listener != null) listener.onIabPurchaseFinished(r, null);
              return;
          }
  
          try {
              logDebug("Constructing buy intent for " + sku + ", item type: " + itemType);
              Bundle buyIntentBundle = mService.getBuyIntent(3, mContext.getPackageName(), sku, itemType, extraData);
              int response = getResponseCodeFromBundle(buyIntentBundle);
              if (response != BILLING_RESPONSE_RESULT_OK) {
                  logError("Unable to buy item, Error response: " + getResponseDesc(response));
                  flagEndAsync();
                  result = new IabResult(response, "Unable to buy item");
                  if (listener != null) listener.onIabPurchaseFinished(result, null);
                  return;
              }
  
              PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT);
              logDebug("Launching buy intent for " + sku + ". Request code: " + requestCode);
              mRequestCode = requestCode;
              mPurchaseListener = listener;
              mPurchasingItemType = itemType;
              act.startIntentSenderForResult(pendingIntent.getIntentSender(),
                                             requestCode, new Intent(),
                                             Integer.valueOf(0), Integer.valueOf(0),
                                             Integer.valueOf(0));
          }
          catch (SendIntentException e) {
              logError("SendIntentException while launching purchase flow for sku " + sku);
              e.printStackTrace();
              flagEndAsync();
  
              result = new IabResult(IABHELPER_SEND_INTENT_FAILED, "Failed to send intent.");
              if (listener != null) listener.onIabPurchaseFinished(result, null);
          }
          catch (RemoteException e) {
              logError("RemoteException while launching purchase flow for sku " + sku);
              e.printStackTrace();
              flagEndAsync();
  
              result = new IabResult(IABHELPER_REMOTE_EXCEPTION, "Remote exception while starting purchase flow");
              if (listener != null) listener.onIabPurchaseFinished(result, null);
          }
      }
  
      /**
       * Handles an activity result that's part of the purchase flow in in-app billing. If you
       * are calling {@link #launchPurchaseFlow}, then you must call this method from your
       * Activity's {@link Activity@onActivityResult} method. This method
       * MUST be called from the UI thread of the Activity.
       *
       * @param requestCode The requestCode as you received it.
       * @param resultCode The resultCode as you received it.
       * @param data The data (Intent) as you received it.
       * @return Returns true if the result was related to a purchase flow and was handled;
       *     false if the result was not related to a purchase, in which case you should
       *     handle it normally.
       */
      public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
          IabResult result;
          if (requestCode != mRequestCode) return false;
  
          checkNotDisposed();
          checkSetupDone("handleActivityResult");
  
          // end of async purchase operation that started on launchPurchaseFlow
          flagEndAsync();
  
          if (data == null) {
              logError("Null data in IAB activity result.");
              result = new IabResult(IABHELPER_BAD_RESPONSE, "Null data in IAB result");
              if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
              return true;
          }
  
          int responseCode = getResponseCodeFromIntent(data);
          String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);
          String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);
  
          if (resultCode == Activity.RESULT_OK && responseCode == BILLING_RESPONSE_RESULT_OK) {
              logDebug("Successful resultcode from purchase activity.");
              logDebug("Purchase data: " + purchaseData);
              logDebug("Data signature: " + dataSignature);
              logDebug("Extras: " + data.getExtras());
              logDebug("Expected item type: " + mPurchasingItemType);
  
              if (purchaseData == null || dataSignature == null) {
                  logError("BUG: either purchaseData or dataSignature is null.");
                  logDebug("Extras: " + data.getExtras().toString());
                  result = new IabResult(IABHELPER_UNKNOWN_ERROR, "IAB returned null purchaseData or dataSignature");
                  if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
                  return true;
              }
  
              Purchase purchase = null;
              try {
                  purchase = new Purchase(mPurchasingItemType, purchaseData, dataSignature);
                  String sku = purchase.getSku();
  
                  // Verify signature
                  if (!Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) {
                      logError("Purchase signature verification FAILED for sku " + sku);
                      result = new IabResult(IABHELPER_VERIFICATION_FAILED, "Signature verification failed for sku " + sku);
                      if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, purchase);
                      return true;
                  }
                  logDebug("Purchase signature successfully verified.");
              }
              catch (JSONException e) {
                  logError("Failed to parse purchase data.");
                  e.printStackTrace();
                  result = new IabResult(IABHELPER_BAD_RESPONSE, "Failed to parse purchase data.");
                  if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
                  return true;
              }
  
  