// Copyright 2010 Google Inc. All Rights Reserved.
package com.example.dungeons;
import com.example.dungeons.BillingService.RequestPurchase;
import com.example.dungeons.BillingService.RestoreTransactions;
import com.example.dungeons.Consts.PurchaseState;
import com.example.dungeons.Consts.ResponseCode;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Handler;
import android.util.Log;
import java.lang.reflect.Method;
/**
* An interface for observing changes related to purchases. The main application
* extends this class and registers an instance of that derived class with
* {@link ResponseHandler}. The main application implements the callbacks
* {@link #onBillingSupported(boolean)} and
* {@link #onPurchaseStateChange(PurchaseState, String, int, long)}. These methods
* are used to update the UI.
*/
public abstract class PurchaseObserver {
private static final String TAG = "PurchaseObserver";
private final Activity mActivity;
private final Handler mHandler;
private Method mStartIntentSender;
private Object[] mStartIntentSenderArgs = new Object[5];
private static final Class[] START_INTENT_SENDER_SIG = new Class[] {
IntentSender.class, Intent.class, int.class, int.class, int.class
};
public PurchaseObserver(Activity activity, Handler handler) {
mActivity = activity;
mHandler = handler;
initCompatibilityLayer();
}
/**
* This is the callback that is invoked when Android Market responds to the
* {@link BillingService#checkBillingSupported()} request.
* @param supported true if in-app billing is supported.
*/
public abstract void onBillingSupported(boolean supported, String type);
/**
* This is the callback that is invoked when an item is purchased,
* refunded, or canceled. It is the callback invoked in response to
* calling {@link BillingService#requestPurchase(String)}. It may also
* be invoked asynchronously when a purchase is made on another device
* (if the purchase was for a Market-managed item), or if the purchase
* was refunded, or the charge was canceled. This handles the UI
* update. The database update is handled in
* {@link ResponseHandler#purchaseResponse(Context, PurchaseState,
* String, String, long)}.
* @param purchaseState the purchase state of the item
* @param itemId a string identifying the item (the "SKU")
* @param quantity the current quantity of this item after the purchase
* @param purchaseTime the time the product was purchased, in
* milliseconds since the epoch (Jan 1, 1970)
*/
public abstract void onPurchaseStateChange(PurchaseState purchaseState,
String itemId, int quantity, long purchaseTime, String developerPayload);
/**
* This is called when we receive a response code from Market for a
* RequestPurchase request that we made. This is NOT used for any
* purchase state changes. All purchase state changes are received in
* {@link #onPurchaseStateChange(PurchaseState, String, int, long)}.
* This is used for reporting various errors, or if the user backed out
* and didn't purchase the item. The possible response codes are:
* RESULT_OK means that the order was sent successfully to the server.
* The onPurchaseStateChange() will be invoked later (with a
* purchase state of PURCHASED or CANCELED) when the order is
* charged or canceled. This response code can also happen if an
* order for a Market-managed item was already sent to the server.
* RESULT_USER_CANCELED means that the user didn't buy the item.
* RESULT_SERVICE_UNAVAILABLE means that we couldn't connect to the
* Android Market server (for example if the data connection is down).
* RESULT_BILLING_UNAVAILABLE means that in-app billing is not
* supported yet.
* RESULT_ITEM_UNAVAILABLE means that the item this app offered for
* sale does not exist (or is not published) in the server-side
* catalog.
* RESULT_ERROR is used for any other errors (such as a server error).
*/
public abstract void onRequestPurchaseResponse(RequestPurchase request,
ResponseCode responseCode);
/**
* This is called when we receive a response code from Android Market for a
* RestoreTransactions request that we made. A response code of
* RESULT_OK means that the request was successfully sent to the server.
*/
public abstract void onRestoreTransactionsResponse(RestoreTransactions request,
ResponseCode responseCode);
private void initCompatibilityLayer() {
try {
mStartIntentSender = mActivity.getClass().getMethod("startIntentSender",
START_INTENT_SENDER_SIG);
} catch (SecurityException e) {
mStartIntentSender = null;
} catch (NoSuchMethodException e) {
mStartIntentSender = null;
}
}
void startBuyPageActivity(PendingIntent pendingIntent, Intent intent) {
if (mStartIntentSender != null) {
// This is on Android 2.0 and beyond. The in-app buy page activity
// must be on the activity stack of the application.
try {
// This implements the method call:
// mActivity.startIntentSender(pendingIntent.getIntentSender(),
// intent, 0, 0, 0);
mStartIntentSenderArgs[0] = pendingIntent.getIntentSender();
mStartIntentSenderArgs[1] = intent;
mStartIntentSenderArgs[2] = Integer.valueOf(0);
mStartIntentSenderArgs[3] = Integer.valueOf(0);
mStartIntentSenderArgs[4] = Integer.valueOf(0);
mStartIntentSender.invoke(mActivity, mStartIntentSenderArgs);
} catch (Exception e) {
Log.e(TAG, "error starting activity", e);
}
} else {
// This is on Android version 1.6. The in-app buy page activity must be on its
// own separate activity stack instead of on the activity stack of
// the application.
try {
pendingIntent.send(mActivity, 0 /* code */, intent);
} catch (CanceledException e) {
Log.e(TAG, "error starting activity", e);
}
}
}
/**
* Updates the UI after the database has been updated. This method runs
* in a background thread so it has to post a Runnable to run on the UI
* thread.
* @param purchaseState the purchase state of the item
* @param itemId a string identifying the item
* @param quantity the quantity of items in this purchase
*/
void postPurchaseStateChange(final PurchaseState purchaseState, final String itemId,
final int quantity, final long purchaseTime, final String developerPayload) {
mHandler.post(new Runnable() {
@Override
public void run() {
onPurchaseStateChange(
purchaseState, itemId, quantity, purchaseTime, developerPayload);
}
});
}
}