diff --git a/astrid/src/com/todoroo/astrid/billing/AstridPurchaseObserver.java b/astrid/src/com/todoroo/astrid/billing/AstridPurchaseObserver.java new file mode 100644 index 000000000..61ecad7ae --- /dev/null +++ b/astrid/src/com/todoroo/astrid/billing/AstridPurchaseObserver.java @@ -0,0 +1,151 @@ +package com.todoroo.astrid.billing; + +import android.app.Activity; +import android.os.Handler; +import android.util.Log; +import android.widget.Toast; + +import com.timsu.astrid.R; +import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.service.DependencyInjectionService; +import com.todoroo.andlib.utility.Preferences; +import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; +import com.todoroo.astrid.actfm.sync.ActFmSyncService; +import com.todoroo.astrid.billing.BillingConstants.PurchaseState; +import com.todoroo.astrid.billing.BillingConstants.ResponseCode; +import com.todoroo.astrid.billing.BillingService.RequestPurchase; +import com.todoroo.astrid.billing.BillingService.RestoreTransactions; +import com.todoroo.astrid.utility.Constants; + +@SuppressWarnings("nls") +public abstract class AstridPurchaseObserver extends PurchaseObserver { + + @Autowired + private ActFmSyncService actFmSyncService; + + @Autowired + private ActFmPreferenceService actFmPreferenceService; + + /** + * A {@link PurchaseObserver} is used to get callbacks when Android Market sends + * messages to this application so that we can update the UI. + */ + public AstridPurchaseObserver(Activity activity, Handler handler) { + super(activity, handler); + DependencyInjectionService.getInstance().inject(this); + } + + @Override + public void onBillingSupported(boolean supported, String type) { + if (Constants.DEBUG) { + Log.i(TAG, "supported: " + supported); + } + if (type != null && type.equals(BillingConstants.ITEM_TYPE_SUBSCRIPTION)) { + if (supported) { + billingSupportedCallback(); + } else { + billingNotSupportedCallback(); + } + } else { + subscriptionsNotSupportedCallback(); + } + } + + protected abstract void billingSupportedCallback(); + + protected abstract void billingNotSupportedCallback(); + + protected abstract void subscriptionsNotSupportedCallback(); + + @Override + public void onPurchaseStateChange(PurchaseState purchaseState, final String itemId, + int quantity, long purchaseTime, String developerPayload, final String purchaseToken) { + if (Constants.DEBUG) { + Log.i(TAG, "onPurchaseStateChange() itemId: " + itemId + " " + purchaseState); + } + + Preferences.setString(BillingConstants.PREF_PRODUCT_ID, itemId); + Preferences.setString(BillingConstants.PREF_PURCHASE_TOKEN, purchaseToken); + + if (purchaseState == PurchaseState.PURCHASED) { + new Thread() { + @Override + public void run() { + Preferences.setBoolean(ActFmPreferenceService.PREF_LOCAL_PREMIUM, true); + if (actFmPreferenceService.isLoggedIn()) { + actFmSyncService.updateUserSubscriptionStatus(new Runnable() { + @Override + public void run() { + Preferences.setBoolean(ActFmPreferenceService.PREF_PREMIUM, true); + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(mActivity, R.string.premium_success, Toast.LENGTH_LONG).show(); + } + }); + } + }, new Runnable() { + @Override + public void run() { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(mActivity, R.string.premium_success_with_server_error, Toast.LENGTH_LONG).show(); + } + }); + } + }); + } + } + }.start(); + } else if (purchaseState == PurchaseState.REFUNDED || purchaseState == PurchaseState.EXPIRED) { + new Thread() { + @Override + public void run() { + Preferences.setBoolean(ActFmPreferenceService.PREF_LOCAL_PREMIUM, false); + if (actFmPreferenceService.isLoggedIn()) + actFmSyncService.updateUserSubscriptionStatus(null, null); + } + }.start(); + } + } + + @Override + public void onRequestPurchaseResponse(RequestPurchase request, + ResponseCode responseCode) { + if (Constants.DEBUG) { + Log.d(TAG, request.mProductId + ": " + responseCode); + } + if (responseCode == ResponseCode.RESULT_OK) { + if (Constants.DEBUG) { + Log.i(TAG, "purchase was successfully sent to server"); + } + } else if (responseCode == ResponseCode.RESULT_USER_CANCELED) { + if (Constants.DEBUG) { + Log.i(TAG, "user canceled purchase"); + } + } else { + if (Constants.DEBUG) { + Log.i(TAG, "purchase failed"); + } + } + } + + @Override + public void onRestoreTransactionsResponse(RestoreTransactions request, + ResponseCode responseCode) { + if (responseCode == ResponseCode.RESULT_OK) { + if (Constants.DEBUG) { + Log.d(TAG, "completed RestoreTransactions request"); + } + // Update the shared preferences so that we don't perform + // a RestoreTransactions again. + Preferences.setBoolean(BillingConstants.PREF_TRANSACTIONS_INITIALIZED, true); + } else { + if (Constants.DEBUG) { + Log.d(TAG, "RestoreTransactions error: " + responseCode); + } + } + } + +} diff --git a/astrid/src/com/todoroo/astrid/billing/BillingActivity.java b/astrid/src/com/todoroo/astrid/billing/BillingActivity.java index 2deb72b08..41444bdd9 100644 --- a/astrid/src/com/todoroo/astrid/billing/BillingActivity.java +++ b/astrid/src/com/todoroo/astrid/billing/BillingActivity.java @@ -14,7 +14,6 @@ import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; -import android.widget.Toast; import com.timsu.astrid.R; import com.todoroo.andlib.service.Autowired; @@ -23,11 +22,6 @@ import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.andlib.utility.Preferences; import com.todoroo.astrid.actfm.ActFmLoginActivity; import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; -import com.todoroo.astrid.actfm.sync.ActFmSyncService; -import com.todoroo.astrid.billing.BillingConstants.PurchaseState; -import com.todoroo.astrid.billing.BillingConstants.ResponseCode; -import com.todoroo.astrid.billing.BillingService.RequestPurchase; -import com.todoroo.astrid.billing.BillingService.RestoreTransactions; import com.todoroo.astrid.utility.Constants; public class BillingActivity extends Activity { @@ -36,15 +30,12 @@ public class BillingActivity extends Activity { private static final int DIALOG_BILLING_NOT_SUPPORTED_ID = 2; private static final int DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID = 3; - private static final String TRANSACTIONS_INITIALIZED = "premium_transactions_initialized"; //$NON-NLS-1$ - private Handler handler; private BillingService billingService; private AstridPurchaseObserver purchaseObserver; private Button buyMonth; private Button buyYear; - @Autowired private ActFmSyncService actFmSyncService; @Autowired private ActFmPreferenceService actFmPreferenceService; @@ -59,7 +50,24 @@ public class BillingActivity extends Activity { handler = new Handler(); billingService = new BillingService(); billingService.setContext(this); - purchaseObserver = new AstridPurchaseObserver(handler); + purchaseObserver = new AstridPurchaseObserver(this, handler) { + @Override + protected void billingSupportedCallback() { + restoreTransactions(); + buyMonth.setEnabled(true); + buyYear.setEnabled(true); + } + + @Override + protected void billingNotSupportedCallback() { + showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID); + } + + @Override + protected void subscriptionsNotSupportedCallback() { + showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID); + } + }; ResponseHandler.register(purchaseObserver); } @@ -182,126 +190,9 @@ public class BillingActivity extends Activity { } private void restoreTransactions() { - boolean initialized = Preferences.getBoolean(TRANSACTIONS_INITIALIZED, false); + boolean initialized = Preferences.getBoolean(BillingConstants.PREF_TRANSACTIONS_INITIALIZED, false); if (!initialized) { billingService.restoreTransactions(); } } - - /** - * A {@link PurchaseObserver} is used to get callbacks when Android Market sends - * messages to this application so that we can update the UI. - */ - @SuppressWarnings("nls") - private class AstridPurchaseObserver extends PurchaseObserver { - public AstridPurchaseObserver(Handler handler) { - super(BillingActivity.this, handler); - } - - @Override - public void onBillingSupported(boolean supported, String type) { - if (Constants.DEBUG) { - Log.i(TAG, "supported: " + supported); - } - if (type != null && type.equals(BillingConstants.ITEM_TYPE_SUBSCRIPTION)) { - if (supported) { - restoreTransactions(); - buyMonth.setEnabled(true); - buyYear.setEnabled(true); - } else { - showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID); - } - } else { - showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID); - } - } - - @Override - public void onPurchaseStateChange(PurchaseState purchaseState, final String itemId, - int quantity, long purchaseTime, String developerPayload, final String purchaseToken) { - if (Constants.DEBUG) { - Log.i(TAG, "onPurchaseStateChange() itemId: " + itemId + " " + purchaseState); - } - - Preferences.setString(BillingConstants.PREF_PRODUCT_ID, itemId); - Preferences.setString(BillingConstants.PREF_PURCHASE_TOKEN, purchaseToken); - - if (purchaseState == PurchaseState.PURCHASED) { - new Thread() { - @Override - public void run() { - Preferences.setBoolean(ActFmPreferenceService.PREF_LOCAL_PREMIUM, true); - actFmSyncService.updateUserSubscriptionStatus(new Runnable() { - @Override - public void run() { - Preferences.setBoolean(ActFmPreferenceService.PREF_PREMIUM, true); - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(BillingActivity.this, R.string.premium_success, Toast.LENGTH_LONG).show(); - } - }); - } - }, new Runnable() { - @Override - public void run() { - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(BillingActivity.this, R.string.premium_success_with_server_error, Toast.LENGTH_LONG).show(); - } - }); - } - }); - } - }.start(); - } else if (purchaseState == PurchaseState.REFUNDED || purchaseState == PurchaseState.EXPIRED) { - new Thread() { - @Override - public void run() { - Preferences.setBoolean(ActFmPreferenceService.PREF_LOCAL_PREMIUM, false); - actFmSyncService.updateUserSubscriptionStatus(null, null); - } - }.start(); - } - } - - @Override - public void onRequestPurchaseResponse(RequestPurchase request, - ResponseCode responseCode) { - if (Constants.DEBUG) { - Log.d(TAG, request.mProductId + ": " + responseCode); - } - if (responseCode == ResponseCode.RESULT_OK) { - if (Constants.DEBUG) { - Log.i(TAG, "purchase was successfully sent to server"); - } - } else if (responseCode == ResponseCode.RESULT_USER_CANCELED) { - if (Constants.DEBUG) { - Log.i(TAG, "user canceled purchase"); - } - } else { - if (Constants.DEBUG) { - Log.i(TAG, "purchase failed"); - } - } - } - - @Override - public void onRestoreTransactionsResponse(RestoreTransactions request, - ResponseCode responseCode) { - if (responseCode == ResponseCode.RESULT_OK) { - if (Constants.DEBUG) { - Log.d(TAG, "completed RestoreTransactions request"); - } - // Update the shared preferences so that we don't perform - // a RestoreTransactions again. - Preferences.setBoolean(TRANSACTIONS_INITIALIZED, true); - } else { - if (Constants.DEBUG) { - Log.d(TAG, "RestoreTransactions error: " + responseCode); - } - } - } - } } diff --git a/astrid/src/com/todoroo/astrid/billing/BillingConstants.java b/astrid/src/com/todoroo/astrid/billing/BillingConstants.java index 668d9562f..b9a416951 100644 --- a/astrid/src/com/todoroo/astrid/billing/BillingConstants.java +++ b/astrid/src/com/todoroo/astrid/billing/BillingConstants.java @@ -55,6 +55,7 @@ public class BillingConstants { public static final String PREF_PRODUCT_ID = ActFmPreferenceService.IDENTIFIER + "_inapp_product_id"; public static final String PREF_PURCHASE_TOKEN = ActFmPreferenceService.IDENTIFIER + "_inapp_purchase_token"; public static final String PREF_NEEDS_SERVER_UPDATE = ActFmPreferenceService.IDENTIFIER + "_inapp_needs_server_update"; + public static final String PREF_TRANSACTIONS_INITIALIZED = "premium_transactions_initialized"; //$NON-NLS-1$ public static final char PUB_KEY_OBFUSCATION_CHAR = '!'; public static final char PUB_KEY_REPLACE_CHAR = 'B'; diff --git a/astrid/src/com/todoroo/astrid/billing/PurchaseObserver.java b/astrid/src/com/todoroo/astrid/billing/PurchaseObserver.java index ac79e100e..39767995e 100644 --- a/astrid/src/com/todoroo/astrid/billing/PurchaseObserver.java +++ b/astrid/src/com/todoroo/astrid/billing/PurchaseObserver.java @@ -28,7 +28,7 @@ import com.todoroo.astrid.billing.BillingService.RestoreTransactions; */ public abstract class PurchaseObserver { protected static final String TAG = "purchase-observer"; //$NON-NLS-1$ - private final Activity mActivity; + protected final Activity mActivity; private final Handler mHandler; private Method mStartIntentSender; private final Object[] mStartIntentSenderArgs = new Object[5]; diff --git a/astrid/src/com/todoroo/astrid/service/StartupService.java b/astrid/src/com/todoroo/astrid/service/StartupService.java index 63244802b..b9643417a 100644 --- a/astrid/src/com/todoroo/astrid/service/StartupService.java +++ b/astrid/src/com/todoroo/astrid/service/StartupService.java @@ -23,6 +23,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.database.sqlite.SQLiteException; import android.media.AudioManager; +import android.os.Handler; import android.util.Log; import android.widget.Toast; @@ -41,6 +42,11 @@ import com.todoroo.astrid.activity.BeastModePreferences; import com.todoroo.astrid.backup.BackupConstants; import com.todoroo.astrid.backup.BackupService; import com.todoroo.astrid.backup.TasksXmlImporter; +import com.todoroo.astrid.billing.AstridPurchaseObserver; +import com.todoroo.astrid.billing.BillingConstants; +import com.todoroo.astrid.billing.BillingService; +import com.todoroo.astrid.billing.PurchaseObserver; +import com.todoroo.astrid.billing.ResponseHandler; import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.gtasks.GtasksPreferenceService; import com.todoroo.astrid.gtasks.sync.GtasksSyncService; @@ -108,7 +114,7 @@ public class StartupService { } /** Called when this application is started up */ - public synchronized void onStartupApplication(final Context context) { + public synchronized void onStartupApplication(final Activity context) { if(hasStartedUp || context == null) return; @@ -192,9 +198,30 @@ public class StartupService { abTestInvoker.reportAcquisition(); + final Handler purchaseHandler = new Handler(); + // perform startup activities in a background thread new Thread(new Runnable() { public void run() { + final BillingService billingService = new BillingService(); + billingService.setContext(context); + PurchaseObserver purchaseObserver = new AstridPurchaseObserver(context, purchaseHandler) { + + @Override + protected void billingSupportedCallback() { + billingService.restoreTransactions(); + } + + @Override + protected void subscriptionsNotSupportedCallback() {/**/} + + @Override + protected void billingNotSupportedCallback() {/**/} + }; + + ResponseHandler.register(purchaseObserver); + billingService.checkBillingSupported(BillingConstants.ITEM_TYPE_SUBSCRIPTION); + // start widget updating alarm AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, WidgetUpdateService.class);