Example #1
0
 /** @see java.lang.Runnable#run() */
 @Override
 public final void run() {
   try {
     doRun();
   } catch (RuntimeException e) {
     AbstractApplication.get().getExceptionHandler().logHandledException(e);
   }
 }
 /**
  * @see
  *     com.jdroid.android.fragment.AbstractFragment#onFinishFailedUseCase(com.jdroid.java.exception.AbstractException)
  */
 @Override
 public void onFinishFailedUseCase(AbstractException abstractException) {
   if (getPaginatedUseCase().isPaginating()) {
     AbstractApplication.get().getExceptionHandler().logHandledException(abstractException);
     dismissPaginationLoading();
   } else {
     super.onFinishFailedUseCase(abstractException);
   }
 }
Example #3
0
  /**
   * @see com.jdroid.android.fragment.AbstractFragment#onViewCreated(android.view.View,
   *     android.os.Bundle)
   */
  @Override
  public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    View header = inflate(R.layout.about_header_fragment);
    TextView appName = (TextView) header.findViewById(R.id.appName);
    appName.setText(AbstractApplication.get().getAppName());

    TextView version = (TextView) header.findViewById(R.id.version);
    version.setText(getString(R.string.version, AndroidUtils.getVersionName()));

    TextView copyright = (TextView) header.findViewById(R.id.copyright);
    copyright.setText(getCopyRightLegend());

    if (getAppContext().displayDebugSettings()) {
      View debugSettings = header.findViewById(R.id.icon);
      debugSettings.setOnClickListener(
          new OnClickListener() {

            @Override
            public void onClick(View v) {
              AbstractApplication.get().getDebugContext().launchActivityDebugSettingsActivity();
            }
          });
    }

    if (getListView().getHeaderViewsCount() == 0) {
      if (getListAdapter() != null) {
        setListAdapter(null);
      }
      getListView().addHeaderView(header);
    }
    if (getListAdapter() == null) {
      setListAdapter(new AboutItemsAdapter(getActivity(), aboutItems));
    }
  }
Example #4
0
/** @author Maxi Rosson */
public class BillingManagerImpl implements BillingManager, ServiceConnection {

  private static final String TAG = BillingManagerImpl.class.getSimpleName();

  // This is the action we use to bind to the MarketBillingService.
  public static final String MARKET_BILLING_SERVICE_ACTION =
      "com.android.vending.billing.MarketBillingService.BIND";

  // The service connection to the remote MarketBillingService.
  private IMarketBillingService service;

  // The list of requests that are pending while we are waiting for the connection to the
  // MarketBillingService to be
  // established.
  private LinkedList<BillingRequest> pendingRequests = new LinkedList<BillingRequest>();

  // The list of requests that we have sent to the Google Play app but for which we have not yet
  // received a response
  // code. The Map is indexed by the request Id that each request receives when it executes.
  private Map<Long, BillingRequest> sentRequests = Maps.newHashMap();

  private Context context = AbstractApplication.get();

  /**
   * @see
   *     com.jdroid.android.billing.BillingManager#register(com.jdroid.android.billing.BillingListener)
   */
  @Override
  public synchronized void register(BillingListener billingListener) {
    BillingResponseHandler.register(billingListener);
  }

  /**
   * @see
   *     com.jdroid.android.billing.BillingManager#unregister(com.jdroid.android.billing.BillingListener)
   */
  @Override
  public synchronized void unregister(BillingListener billingListener) {
    BillingResponseHandler.unregister(billingListener);
  }

  /**
   * @see
   *     com.jdroid.android.billing.BillingManager#register(com.jdroid.android.billing.PurchaseObserver)
   */
  @Override
  public synchronized void register(PurchaseObserver purchaseObserver) {
    BillingResponseHandler.register(purchaseObserver);
  }

  /**
   * @see
   *     com.jdroid.android.billing.BillingManager#unregister(com.jdroid.android.billing.PurchaseObserver)
   */
  @Override
  public synchronized void unregister(PurchaseObserver purchaseObserver) {
    BillingResponseHandler.unregister(purchaseObserver);
  }

  /**
   * The base class for all requests that use the MarketBillingService. Each derived class overrides
   * the run() method to call the appropriate service interface. If we are already connected to the
   * MarketBillingService, then we call the run() method directly. Otherwise, we bind to the service
   * and save the request on a queue to be run later when the service is connected.
   */
  abstract class BillingRequest {

    public static final String BILLING_RESPONSE_REQUEST_ID = "REQUEST_ID";
    public static final String BILLING_RESPONSE_RESPONSE_CODE = "RESPONSE_CODE";
    public static final long BILLING_RESPONSE_INVALID_REQUEST_ID = -1;

    // These are the names of the fields in the request bundle.
    private static final String BILLING_REQUEST_METHOD = "BILLING_REQUEST";
    private static final String BILLING_REQUEST_API_VERSION = "API_VERSION";
    private static final String BILLING_REQUEST_PACKAGE_NAME = "PACKAGE_NAME";
    public static final String BILLING_REQUEST_NOTIFY_IDS = "NOTIFY_IDS";
    public static final String BILLING_REQUEST_NONCE = "NONCE";

    private long requestId;

    /**
     * Execute the request, starting the connection if necessary.
     *
     * @return true if the request was executed or queued; false if there was an error starting the
     *     connection
     */
    public boolean execute() {
      if (runIfConnected()) {
        return true;
      }

      if (bindToMarketBillingService()) {
        // Add a pending request to run when the service is connected.
        pendingRequests.add(this);
        return true;
      }
      return false;
    }

    /**
     * The derived class must implement this method.
     *
     * @throws RemoteException
     */
    protected abstract long doExecute() throws RemoteException;

    /**
     * Try running the request directly if the service is already connected.
     *
     * @return true if the request ran successfully; false if the service is not connected or there
     *     was an error when trying to use it
     */
    public boolean runIfConnected() {
      if (service != null) {
        try {
          requestId = doExecute();
          Log.d(TAG, "BillingRequest id: " + requestId);
          if (requestId >= 0) {
            sentRequests.put(requestId, this);
          }
          return true;
        } catch (RemoteException e) {
          onRemoteException(e);
        }
      }
      return false;
    }

    /**
     * Called when a remote exception occurs while trying to execute the {@link #doExecute()}
     * method. The derived class can override this to execute exception-handling code.
     *
     * @param e the exception
     */
    protected void onRemoteException(RemoteException e) {
      Log.w(TAG, "Remote billing service crashed");
      service = null;
    }

    /**
     * This is called when the Google Play app sends a response code for this request.
     *
     * @param responseCode the response code
     */
    protected void responseCodeReceived(BillingResponseCode responseCode) {
      // Do Nothing by default
    }

    protected Bundle makeRequestBundle(String method) {
      Bundle request = new Bundle();
      request.putString(BILLING_REQUEST_METHOD, method);
      request.putInt(BILLING_REQUEST_API_VERSION, 1);
      request.putString(BILLING_REQUEST_PACKAGE_NAME, AndroidUtils.getPackageName());
      return request;
    }

    protected void logResponseCode(String request, Bundle response) {
      BillingResponseCode responseCode = BillingResponseCode.valueOf(response);
      Log.i(TAG, request + " response code: " + responseCode);
    }
  }

  /** This request verifies that the Google Play app supports in-app billing. */
  private class CheckBillingSupported extends BillingRequest {

    private static final String CHECK_BILLING_SUPPORTED = "CHECK_BILLING_SUPPORTED";

    /** @see com.splatt.android.common.billing.BillingService.BillingRequest#doExecute() */
    @Override
    protected long doExecute() throws RemoteException {

      Log.d(TAG, "Checking if billing is supported");

      Bundle requestBundle = makeRequestBundle(CHECK_BILLING_SUPPORTED);
      Bundle responseBundle = service.sendBillingRequest(requestBundle);
      BillingResponseCode responseCode = BillingResponseCode.valueOf(responseBundle);
      Log.i(TAG, "CheckBillingSupported response code: " + responseCode);
      BillingResponseHandler.checkBillingSupportedResponse(responseCode);
      return BILLING_RESPONSE_INVALID_REQUEST_ID;
    }
  }

  /**
   * This request sends a purchase message to the Google Play app and is the foundation of in-app
   * billing. You send this request when a user indicates that he or she wants to purchase an item
   * in your application. The Google Play app then handles the financial transaction by displaying
   * the checkout user interface.
   */
  public class RequestPurchase extends BillingRequest {

    private static final String REQUEST_PURCHASE = "REQUEST_PURCHASE";
    private static final String BILLING_REQUEST_ITEM_ID = "ITEM_ID";
    private static final String BILLING_REQUEST_DEVELOPER_PAYLOAD = "DEVELOPER_PAYLOAD";
    private static final String BILLING_RESPONSE_PURCHASE_INTENT = "PURCHASE_INTENT";

    private Purchasable purchasable;

    private String developerPayload;

    public RequestPurchase(Purchasable purchasable) {
      this(purchasable, null);
    }

    public RequestPurchase(Purchasable purchasable, String developerPayload) {
      this.purchasable = purchasable;
      this.developerPayload = developerPayload;
    }

    @Override
    protected long doExecute() throws RemoteException {

      Log.d(TAG, "Requesting purchase for product id: " + purchasable.getProductId());

      Bundle request = makeRequestBundle(REQUEST_PURCHASE);
      request.putString(BILLING_REQUEST_ITEM_ID, purchasable.getProductId());
      // Note that the developer payload is optional.
      if (developerPayload != null) {
        request.putString(BILLING_REQUEST_DEVELOPER_PAYLOAD, developerPayload);
      }
      Bundle response = service.sendBillingRequest(request);
      PendingIntent pendingIntent = response.getParcelable(BILLING_RESPONSE_PURCHASE_INTENT);
      if (pendingIntent == null) {
        Log.e(TAG, "Error with requestPurchase for product id: " + purchasable.getProductId());
        return BILLING_RESPONSE_INVALID_REQUEST_ID;
      }

      BillingResponseHandler.startMarketCheckoutActivity(pendingIntent, new Intent());
      return response.getLong(BILLING_RESPONSE_REQUEST_ID, BILLING_RESPONSE_INVALID_REQUEST_ID);
    }

    @Override
    protected void responseCodeReceived(BillingResponseCode responseCode) {
      BillingResponseHandler.responseCodeReceived(this, responseCode);
    }
  }

  /**
   * This request acknowledges that your application received the details of a purchase state
   * change. The Google Play app sends purchase state change notifications to your application until
   * you confirm that you received them.
   */
  class ConfirmNotifications extends BillingRequest {

    private static final String CONFIRM_NOTIFICATIONS = "CONFIRM_NOTIFICATIONS";

    private final String[] notifyIds;

    public ConfirmNotifications(String[] notifyIds) {
      this.notifyIds = notifyIds;
    }

    @Override
    protected long doExecute() throws RemoteException {

      Log.d(TAG, "Confirming notifications for notification ids: " + notifyIds);

      Bundle request = makeRequestBundle(CONFIRM_NOTIFICATIONS);
      request.putStringArray(BILLING_REQUEST_NOTIFY_IDS, notifyIds);
      Bundle response = service.sendBillingRequest(request);
      logResponseCode("ConfirmNotifications", response);
      return response.getLong(BILLING_RESPONSE_REQUEST_ID, BILLING_RESPONSE_INVALID_REQUEST_ID);
    }
  }

  /**
   * This request retrieves the details of a purchase state change. A purchase changes state when a
   * requested purchase is billed successfully or when a user cancels a transaction during checkout.
   * It can also occur when a previous purchase is refunded. The Google Play app notifies your
   * application when a purchase changes state, so you only need to send this request when there is
   * transaction information to retrieve.
   */
  class GetPurchaseInformation extends BillingRequest {

    private static final String GET_PURCHASE_INFORMATION = "GET_PURCHASE_INFORMATION";

    private long nonce;

    private final String[] notifyIds;

    public GetPurchaseInformation(String[] notifyIds) {
      this.notifyIds = notifyIds;
    }

    @Override
    protected long doExecute() throws RemoteException {

      Log.d(TAG, "Getting purchase information for notification ids: " + notifyIds);

      nonce = Security.generateNonce();

      Bundle request = makeRequestBundle(GET_PURCHASE_INFORMATION);
      request.putLong(BILLING_REQUEST_NONCE, nonce);
      request.putStringArray(BILLING_REQUEST_NOTIFY_IDS, notifyIds);
      Bundle response = service.sendBillingRequest(request);
      logResponseCode("GetPurchaseInformation", response);
      return response.getLong(BILLING_RESPONSE_REQUEST_ID, BILLING_RESPONSE_INVALID_REQUEST_ID);
    }

    @Override
    protected void onRemoteException(RemoteException e) {
      super.onRemoteException(e);
      Security.removeNonce(nonce);
    }
  }

  /**
   * This request retrieves a user's transaction status for managed purchases. You should send this
   * request only when you need to retrieve a user's transaction status, which is usually only when
   * your application is reinstalled or installed for the first time on a device.
   */
  public class RestoreTransactions extends BillingRequest {

    private static final String RESTORE_TRANSACTIONS = "RESTORE_TRANSACTIONS";

    private long nonce;

    @Override
    protected long doExecute() throws RemoteException {

      Log.d(TAG, "Executing restore transactions");

      nonce = Security.generateNonce();

      Bundle request = makeRequestBundle(RESTORE_TRANSACTIONS);
      request.putLong(BILLING_REQUEST_NONCE, nonce);
      Bundle response = service.sendBillingRequest(request);
      logResponseCode("RestoreTransactions", response);
      return response.getLong(BILLING_RESPONSE_REQUEST_ID, BILLING_RESPONSE_INVALID_REQUEST_ID);
    }

    @Override
    protected void onRemoteException(RemoteException e) {
      super.onRemoteException(e);
      Security.removeNonce(nonce);
    }

    @Override
    protected void responseCodeReceived(BillingResponseCode responseCode) {
      BillingResponseHandler.responseCodeReceived(this, responseCode);
    }
  }

  /**
   * @see com.jdroid.android.billing.BillingManager#checkResponseCode(long,
   *     com.jdroid.android.billing.BillingResponseCode)
   */
  @Override
  public void checkResponseCode(long requestId, BillingResponseCode responseCode) {
    BillingRequest request = sentRequests.get(requestId);
    if (request != null) {
      Log.d(TAG, request.getClass().getSimpleName() + " response code: " + responseCode);
      request.responseCodeReceived(responseCode);
    }
    sentRequests.remove(requestId);
  }

  /**
   * Binds to the MarketBillingService and returns true if the bind succeeded.
   *
   * @return true if the bind succeeded; false otherwise
   */
  private boolean bindToMarketBillingService() {
    try {
      Log.i(TAG, "Binding to Market billing service");
      boolean bindResult = false;
      if (context != null) {
        bindResult =
            context.bindService(
                new Intent(MARKET_BILLING_SERVICE_ACTION), this, Context.BIND_AUTO_CREATE);
      }
      if (bindResult) {
        Log.i(TAG, "Market billing service bind successful.");
        return true;
      } else {
        Log.e(TAG, "Could not bind to Market billing service.");
      }
    } catch (SecurityException e) {
      Log.e(TAG, "Security exception: " + e);
    }
    return false;
  }

  /** @see com.jdroid.android.billing.BillingManager#checkBillingSupported() */
  @Override
  public boolean checkBillingSupported() {
    return new CheckBillingSupported().execute();
  }

  /**
   * @see
   *     com.jdroid.android.billing.BillingManager#requestPurchase(com.jdroid.android.billing.Purchasable,
   *     java.lang.String)
   */
  @Override
  public boolean requestPurchase(Purchasable purchasable, String developerPayload) {
    return new RequestPurchase(purchasable, developerPayload).execute();
  }

  /** @see com.jdroid.android.billing.BillingManager#restoreTransactions() */
  @Override
  public boolean restoreTransactions() {
    return new RestoreTransactions().execute();
  }

  /** @see com.jdroid.android.billing.BillingManager#confirmNotifications(java.lang.String[]) */
  @Override
  public boolean confirmNotifications(String... notifyIds) {
    return new ConfirmNotifications(notifyIds).execute();
  }

  /** @see com.jdroid.android.billing.BillingManager#getPurchaseInformation(java.lang.String[]) */
  @Override
  public boolean getPurchaseInformation(String[] notifyIds) {
    return new GetPurchaseInformation(notifyIds).execute();
  }

  /**
   * @see com.jdroid.android.billing.BillingManager#purchaseStateChanged(java.lang.String,
   *     java.lang.String)
   */
  @Override
  public void purchaseStateChanged(String signedData, String signature) {
    List<PurchaseOrder> orders = Security.verifyPurchase(signedData, signature);
    if ((orders != null) && !orders.isEmpty()) {
      BillingResponseHandler.notifyPurchaseStateChanged(orders);
    }
  }

  /**
   * This is called when we are connected to the MarketBillingService. This runs in the main UI
   * thread.
   *
   * @see android.content.ServiceConnection#onServiceConnected(android.content.ComponentName,
   *     android.os.IBinder)
   */
  @Override
  public void onServiceConnected(ComponentName name, IBinder binder) {
    Log.d(TAG, "MarketBillingService connected");
    service = IMarketBillingService.Stub.asInterface(binder);
    runPendingRequests();
  }

  /**
   * Runs any pending requests that are waiting for a connection to the service to be established.
   * This runs in the main UI thread.
   */
  private void runPendingRequests() {
    BillingRequest request;
    while ((request = pendingRequests.peek()) != null) {
      if (request.runIfConnected()) {
        // Remove the request
        pendingRequests.remove();
      } else {
        // The service crashed, so restart it. Note that this leaves the current request on the
        // queue.
        bindToMarketBillingService();
        return;
      }
    }
  }

  /**
   * This is called when we are disconnected from the MarketBillingService.
   *
   * @see android.content.ServiceConnection#onServiceDisconnected(android.content.ComponentName)
   */
  @Override
  public void onServiceDisconnected(ComponentName name) {
    Log.w(TAG, "Billing service disconnected");
    service = null;
  }

  /** @see com.jdroid.android.billing.BillingManager#bind(android.content.Context) */
  @Override
  public void bind(Context context) {
    this.context = context;
  }

  /** @see com.jdroid.android.billing.BillingManager#unbind() */
  @Override
  public void unbind() {
    try {
      context.unbindService(this);
      context = null;
    } catch (IllegalArgumentException e) {
      // This might happen if the service was disconnected
    }
  }
}
Example #5
0
  /** @see com.jdroid.android.fragment.AbstractListFragment#onCreate(android.os.Bundle) */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    final String website = getWebsite();
    if (website != null) {
      aboutItems.add(
          new AboutItem(R.drawable.ic_website, R.string.website) {
            @Override
            public void onSelected(Activity activity) {
              IntentUtils.startUrl(activity, website);
            }
          });
    }

    final String contactUsEmailAddress = getContactUsEmail();
    if (contactUsEmailAddress != null) {
      aboutItems.add(
          new AboutItem(R.drawable.ic_contact_us, R.string.contactUs) {

            @Override
            public void onSelected(Activity activity) {
              Intent intent =
                  ShareUtils.createOpenMail(
                      contactUsEmailAddress, AbstractApplication.get().getAppName());
              if (IntentUtils.isIntentAvailable(intent)) {
                startActivity(intent);
                AbstractApplication.get().getAnalyticsSender().trackContactUs();
              } else {
                // TODO Improve this adding a toast or something
                AbstractApplication.get()
                    .getExceptionHandler()
                    .logWarningException("Error when sending email intent");
              }
            }
          });
    }

    if (AbstractApplication.get().getAboutContext().getSpreadTheLoveFragmentClass() != null) {
      aboutItems.add(
          new AboutItem(R.drawable.ic_spread_the_love, R.string.spreadTheLove) {

            @Override
            public void onSelected(Activity activity) {
              ActivityLauncher.launchActivity(SpreadTheLoveActivity.class);
            }
          });
    }
    aboutItems.add(
        new AboutItem(R.drawable.ic_rate_us, R.string.rateUs) {

          @Override
          public void onSelected(Activity activity) {
            RateAppView.rateMeClicked();
            GooglePlayUtils.launchAppDetails(getActivity());
            AbstractApplication.get().getAnalyticsSender().trackRateUs();
          }
        });
    aboutItems.add(
        new AboutItem(R.drawable.ic_libraries, R.string.libraries) {

          @Override
          public void onSelected(Activity activity) {
            ActivityLauncher.launchActivity(LibrariesActivity.class);
          }
        });
    aboutItems.addAll(getCustomAboutItems());
  }
Example #6
0
 protected String getContactUsEmail() {
   return AbstractApplication.get().getAppContext().getContactUsEmail();
 }
Example #7
0
 protected String getWebsite() {
   return AbstractApplication.get().getAppContext().getWebsite();
 }
Example #8
0
 protected String getCopyRightLegend() {
   return getString(
       R.string.copyright, DateUtils.getYear(), AbstractApplication.get().getAppName());
 }