From 9b6ea1f2de2501878abd5e270277a5b78d170c9c Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Sun, 23 Feb 2014 13:21:10 -0600 Subject: [PATCH] Remove billing * Remove billing service (#53) * BILLING permission prevented distribution to various countries --- astrid/src/main/AndroidManifest.xml | 2 - .../billing/IMarketBillingService.aidl | 24 - .../astrid/activity/TaskListActivity.java | 34 -- .../astrid/billing/BillingConstants.java | 88 --- .../astrid/billing/BillingReceiver.java | 61 -- .../astrid/billing/BillingService.java | 541 ------------------ .../astrid/billing/PurchaseObserver.java | 74 --- .../astrid/billing/ResponseHandler.java | 53 -- .../com/todoroo/astrid/billing/Security.java | 45 -- .../org/tasks/billing/PurchaseHandler.java | 68 --- .../src/main/res/menu/task_list_activity.xml | 5 - astrid/src/main/res/values/strings.xml | 1 - .../tasks/billing/PurchaseHandlerTest.java | 119 ---- 13 files changed, 1115 deletions(-) delete mode 100644 astrid/src/main/aidl/com/android/vending/billing/IMarketBillingService.aidl delete mode 100644 astrid/src/main/java/com/todoroo/astrid/billing/BillingConstants.java delete mode 100644 astrid/src/main/java/com/todoroo/astrid/billing/BillingReceiver.java delete mode 100644 astrid/src/main/java/com/todoroo/astrid/billing/BillingService.java delete mode 100644 astrid/src/main/java/com/todoroo/astrid/billing/PurchaseObserver.java delete mode 100644 astrid/src/main/java/com/todoroo/astrid/billing/ResponseHandler.java delete mode 100644 astrid/src/main/java/com/todoroo/astrid/billing/Security.java delete mode 100644 astrid/src/main/java/org/tasks/billing/PurchaseHandler.java delete mode 100644 astrid/src/test/java/org/tasks/billing/PurchaseHandlerTest.java diff --git a/astrid/src/main/AndroidManifest.xml b/astrid/src/main/AndroidManifest.xml index 35c15456d..a819cbe8f 100644 --- a/astrid/src/main/AndroidManifest.xml +++ b/astrid/src/main/AndroidManifest.xml @@ -39,8 +39,6 @@ - - diff --git a/astrid/src/main/aidl/com/android/vending/billing/IMarketBillingService.aidl b/astrid/src/main/aidl/com/android/vending/billing/IMarketBillingService.aidl deleted file mode 100644 index 6884b41f6..000000000 --- a/astrid/src/main/aidl/com/android/vending/billing/IMarketBillingService.aidl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * 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.android.vending.billing; - -import android.os.Bundle; - -interface IMarketBillingService { - /** Given the arguments in bundle form, returns a bundle for results. */ - Bundle sendBillingRequest(in Bundle bundle); -} diff --git a/astrid/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java b/astrid/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java index ccb9ebdcf..e845aef19 100644 --- a/astrid/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java +++ b/astrid/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java @@ -30,8 +30,6 @@ import com.todoroo.astrid.actfm.TagViewFragment; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.FilterListItem; -import com.todoroo.astrid.billing.BillingConstants; -import com.todoroo.astrid.billing.BillingService; import com.todoroo.astrid.core.CoreFilterExposer; import com.todoroo.astrid.core.CustomFilterActivity; import com.todoroo.astrid.core.PluginServices; @@ -75,7 +73,6 @@ public class TaskListActivity extends AstridActivity implements OnPageChangeList private int filterMode; private FilterModeSpec filterModeSpec; - private BillingService billingService; /** * @see android.app.Activity#onCreate(Bundle) @@ -140,9 +137,6 @@ public class TaskListActivity extends AstridActivity implements OnPageChangeList if (getIntent().hasExtra(TOKEN_SOURCE)) { trackActivitySource(); } - - billingService = new BillingService(); - billingService.setActivity(this); } @Override @@ -319,7 +313,6 @@ public class TaskListActivity extends AstridActivity implements OnPageChangeList @Override public boolean onPrepareOptionsMenu(Menu menu) { menuDrawer.closeMenu(); - menu.findItem(R.id.menu_donate).setVisible(billingService.showDonateOption()); return super.onPrepareOptionsMenu(menu); } @@ -532,9 +525,6 @@ public class TaskListActivity extends AstridActivity implements OnPageChangeList rename.putExtra(TagViewFragment.EXTRA_TAG_UUID, renameTag.uuid); startActivityForResult(rename, FilterListFragment.REQUEST_CUSTOM_INTENT); return true; - case R.id.menu_donate: - billingService.requestPurchase(BillingConstants.TASKS_DONATION_ITEM_ID, BillingConstants.ITEM_TYPE_INAPP, null); - return true; default: return super.onOptionsItemSelected(item); } @@ -569,28 +559,4 @@ public class TaskListActivity extends AstridActivity implements OnPageChangeList } return super.onKeyDown(keyCode, event); } - - @Override - protected void onStart() { - super.onStart(); - billingService.onStart(); - } - - @Override - protected void onStop() { - super.onStop(); - billingService.onStop(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - billingService.unbind(); - } - - @Override - protected void onResume() { - super.onResume(); - billingService.checkBillingSupported(); - } } diff --git a/astrid/src/main/java/com/todoroo/astrid/billing/BillingConstants.java b/astrid/src/main/java/com/todoroo/astrid/billing/BillingConstants.java deleted file mode 100644 index d34515db6..000000000 --- a/astrid/src/main/java/com/todoroo/astrid/billing/BillingConstants.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.todoroo.astrid.billing; - -@SuppressWarnings("nls") -public class BillingConstants { - - /** 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"; - - // Intent actions that we send from the BillingReceiver to the - // BillingService. Defined by this application. - public static final String ACTION_CONFIRM_NOTIFICATION = "com.timsu.astrid.subscriptions.CONFIRM_NOTIFICATION"; - public static final String ACTION_GET_PURCHASE_INFORMATION = "com.timsu.astrid.subscriptions.GET_PURCHASE_INFORMATION"; - - // Intent actions that we receive in the BillingReceiver from Market. - // These are defined by Market and cannot be changed. - public static final String ACTION_NOTIFY = "com.android.vending.billing.IN_APP_NOTIFY"; - public static final String ACTION_RESPONSE_CODE = "com.android.vending.billing.RESPONSE_CODE"; - public static final String ACTION_PURCHASE_STATE_CHANGED = "com.android.vending.billing.PURCHASE_STATE_CHANGED"; - - // These are the names of the extras that are passed in an intent from - // Market to this application and cannot be changed. - public static final String NOTIFICATION_ID = "notification_id"; - public static final String INAPP_SIGNED_DATA = "inapp_signed_data"; - public static final String INAPP_SIGNATURE = "inapp_signature"; - public static final String INAPP_REQUEST_ID = "request_id"; - public static final String INAPP_RESPONSE_CODE = "response_code"; - - // These are the names of the fields in the request bundle. - public static final String BILLING_REQUEST_METHOD = "BILLING_REQUEST"; - public static final String BILLING_REQUEST_API_VERSION = "API_VERSION"; - public static final String BILLING_REQUEST_PACKAGE_NAME = "PACKAGE_NAME"; - public static final String BILLING_REQUEST_ITEM_ID = "ITEM_ID"; - public static final String BILLING_REQUEST_ITEM_TYPE = "ITEM_TYPE"; - public static final String BILLING_REQUEST_DEVELOPER_PAYLOAD = "DEVELOPER_PAYLOAD"; - public static final String BILLING_REQUEST_NOTIFY_IDS = "NOTIFY_IDS"; - public static final String BILLING_REQUEST_NONCE = "NONCE"; - - public static final String BILLING_RESPONSE_RESPONSE_CODE = "RESPONSE_CODE"; - public static final String BILLING_RESPONSE_PURCHASE_INTENT = "PURCHASE_INTENT"; - public static final String BILLING_RESPONSE_REQUEST_ID = "REQUEST_ID"; - public static final long BILLING_RESPONSE_INVALID_REQUEST_ID = -1; - - // These are the types supported in the IAB v2 - public static final String ITEM_TYPE_INAPP = "inapp"; - public static final String ITEM_TYPE_SUBSCRIPTION = "subs"; - - public static final String TASKS_DONATION_ITEM_ID = "tasks_donation_4_6"; - - public static final boolean DEBUG = true; - - // The response codes for a request, defined by Android Market. - public enum ResponseCode { - RESULT_OK, - RESULT_USER_CANCELED, - RESULT_SERVICE_UNAVAILABLE, - RESULT_BILLING_UNAVAILABLE, - RESULT_ITEM_UNAVAILABLE, - RESULT_DEVELOPER_ERROR, - RESULT_ERROR; - - // Converts from an ordinal value to the ResponseCode - public static ResponseCode valueOf(int index) { - ResponseCode[] values = ResponseCode.values(); - if (index < 0 || index >= values.length) { - return RESULT_ERROR; - } - return values[index]; - } - } - - // The possible states of an in-app purchase, as defined by Android Market. - public enum PurchaseState { - // Responses to requestPurchase or restoreTransactions. - PURCHASED, // User was charged for the order. - CANCELED, // The charge failed on the server. (NOT THE SAME AS CANCELING A SUBSCRIPTION) - REFUNDED, // User received a refund for the order. - EXPIRED; // Subscription expired due to non-payment or cancellation - - // Converts from an ordinal value to the PurchaseState - public static PurchaseState valueOf(int index) { - PurchaseState[] values = PurchaseState.values(); - if (index < 0 || index >= values.length) { - return CANCELED; - } - return values[index]; - } - } -} diff --git a/astrid/src/main/java/com/todoroo/astrid/billing/BillingReceiver.java b/astrid/src/main/java/com/todoroo/astrid/billing/BillingReceiver.java deleted file mode 100644 index 7c502759d..000000000 --- a/astrid/src/main/java/com/todoroo/astrid/billing/BillingReceiver.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.todoroo.astrid.billing; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.util.Log; - -import com.todoroo.astrid.billing.BillingConstants.ResponseCode; - -public class BillingReceiver extends BroadcastReceiver { - private static final String TAG = "BillingReceiver"; //$NON-NLS-1$ - - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - switch (action) { - case BillingConstants.ACTION_PURCHASE_STATE_CHANGED: - String signedData = intent.getStringExtra(BillingConstants.INAPP_SIGNED_DATA); - String signature = intent.getStringExtra(BillingConstants.INAPP_SIGNATURE); - purchaseStateChanged(context, signedData, signature); - break; - case BillingConstants.ACTION_NOTIFY: - String notifyId = intent.getStringExtra(BillingConstants.NOTIFICATION_ID); - Log.i(TAG, "notifyId: " + notifyId); //$NON-NLS-1$ - notify(context, notifyId); - break; - case BillingConstants.ACTION_RESPONSE_CODE: - long requestId = intent.getLongExtra(BillingConstants.INAPP_REQUEST_ID, -1); - int responseCodeIndex = intent.getIntExtra(BillingConstants.INAPP_RESPONSE_CODE, - ResponseCode.RESULT_ERROR.ordinal()); - checkResponseCode(context, requestId, responseCodeIndex); - break; - default: - Log.w(TAG, "unexpected action: " + action); //$NON-NLS-1$ - break; - } - } - - private void purchaseStateChanged(Context context, String signedData, String signature) { - Intent intent = new Intent(BillingConstants.ACTION_PURCHASE_STATE_CHANGED); - intent.setClass(context, BillingService.class); - intent.putExtra(BillingConstants.INAPP_SIGNED_DATA, signedData); - intent.putExtra(BillingConstants.INAPP_SIGNATURE, signature); - context.startService(intent); - } - - private void notify(Context context, String notifyId) { - Intent intent = new Intent(BillingConstants.ACTION_GET_PURCHASE_INFORMATION); - intent.setClass(context, BillingService.class); - intent.putExtra(BillingConstants.NOTIFICATION_ID, notifyId); - context.startService(intent); - } - - private void checkResponseCode(Context context, long requestId, int responseCodeIndex) { - Intent intent = new Intent(BillingConstants.ACTION_RESPONSE_CODE); - intent.setClass(context, BillingService.class); - intent.putExtra(BillingConstants.INAPP_REQUEST_ID, requestId); - intent.putExtra(BillingConstants.INAPP_RESPONSE_CODE, responseCodeIndex); - context.startService(intent); - } -} diff --git a/astrid/src/main/java/com/todoroo/astrid/billing/BillingService.java b/astrid/src/main/java/com/todoroo/astrid/billing/BillingService.java deleted file mode 100644 index 65c0b6aff..000000000 --- a/astrid/src/main/java/com/todoroo/astrid/billing/BillingService.java +++ /dev/null @@ -1,541 +0,0 @@ -package com.todoroo.astrid.billing; - -import android.app.Activity; -import android.app.PendingIntent; -import android.app.Service; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import com.android.vending.billing.IMarketBillingService; -import com.todoroo.astrid.billing.BillingConstants.ResponseCode; - -import org.tasks.billing.PurchaseHandler; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; - -import static com.todoroo.astrid.billing.Security.Purchase; - -@SuppressWarnings("nls") -public class BillingService extends Service implements ServiceConnection { - private static final String TAG = "BillingService"; - - private static IMarketBillingService marketBillingService; - - private static LinkedList pendingRequests = new LinkedList<>(); - - private static HashMap sentRequests = new HashMap<>(); - - private PurchaseObserver purchaseObserver; - private PurchaseHandler purchaseHandler; - - public boolean showDonateOption() { - return purchaseHandler.isBillingSupported() && !purchaseHandler.userDonated(); - } - - abstract class BillingRequest { - private final int mStartId; - protected long mRequestId; - - public BillingRequest(int startId) { - mStartId = startId; - } - - public int getStartId() { - return mStartId; - } - - public boolean runRequest() { - if (runIfConnected()) { - return true; - } - - if (bindToMarketBillingService()) { - pendingRequests.add(this); - return true; - } - return false; - } - - /** - * 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 (BillingConstants.DEBUG) { - Log.d(TAG, getClass().getSimpleName()); - } - if (marketBillingService != null) { - try { - mRequestId = run(); - if (BillingConstants.DEBUG) { - Log.d(TAG, "request id: " + mRequestId); - } - if (mRequestId >= 0) { - sentRequests.put(mRequestId, this); - } - return true; - } catch (RemoteException e) { - onRemoteException(e); - } - } - return false; - } - - /** - * Called when a remote exception occurs while trying to execute the - * {@link #run()} 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"); - marketBillingService = null; - } - - /** - * The derived class must implement this method. - * - * @throws android.os.RemoteException - */ - abstract protected long run() throws RemoteException; - - /** - * This is called when Android Market sends a response code for this - * request. - * - * @param responseCode the response code - */ - protected void responseCodeReceived(ResponseCode responseCode) { - // - } - - protected Bundle makeRequestBundle(String method) { - Bundle request = new Bundle(); - request.putString(BillingConstants.BILLING_REQUEST_METHOD, method); - request.putInt(BillingConstants.BILLING_REQUEST_API_VERSION, 2); - request.putString(BillingConstants.BILLING_REQUEST_PACKAGE_NAME, getPackageName()); - return request; - } - - protected void logResponseCode(String method, Bundle response) { - ResponseCode responseCode = ResponseCode.valueOf( - response.getInt(BillingConstants.BILLING_RESPONSE_RESPONSE_CODE)); - if (BillingConstants.DEBUG) { - Log.e(TAG, method + " received " + responseCode.toString()); - } - } - } - - class CheckBillingSupported extends BillingRequest { - public String mProductType = null; - - /** - * Constructor - *

- * Note: Support for subscriptions implies support for one-time purchases. However, the - * opposite is not true. - *

- * Developers may want to perform two checks if both one-time and subscription products are - * available. - * - * @param itemType Either BillingConstants.ITEM_TYPE_INAPP or BillingConstants.ITEM_TYPE_SUBSCRIPTION, indicating - * the type of item support is being checked for. - */ - public CheckBillingSupported(String itemType) { - super(-1); - mProductType = itemType; - } - - @Override - protected long run() throws RemoteException { - Bundle request = makeRequestBundle("CHECK_BILLING_SUPPORTED"); - if (mProductType != null) { - request.putString(BillingConstants.BILLING_REQUEST_ITEM_TYPE, mProductType); - } - Bundle response = marketBillingService.sendBillingRequest(request); - int responseCode = response.getInt(BillingConstants.BILLING_RESPONSE_RESPONSE_CODE); - if (BillingConstants.DEBUG) { - Log.i(TAG, "CheckBillingSupported response code: " + - ResponseCode.valueOf(responseCode)); - } - boolean billingSupported = (responseCode == ResponseCode.RESULT_OK.ordinal()); - Log.d(TAG, "check billing support type: " + mProductType + ", response code: " + responseCode); - ResponseHandler.checkBillingSupportedResponse(billingSupported, mProductType); - return BillingConstants.BILLING_RESPONSE_INVALID_REQUEST_ID; - } - } - - class RequestPurchase extends BillingRequest { - public final String mProductId; - public final String mDeveloperPayload; - public final String mProductType; - - /** - * Constructor - * - * @param itemId The ID of the item to be purchased. Will be assumed to be a one-time - * purchase. - * @param itemType Either BillingConstants.ITEM_TYPE_INAPP or BillingConstants.ITEM_TYPE_SUBSCRIPTION, - * indicating the type of item type support is being checked for. - * @param developerPayload Optional data. - */ - public RequestPurchase(String itemId, String itemType, String developerPayload) { - // This object is never created as a side effect of starting this - // service so we pass -1 as the startId to indicate that we should - // not stop this service after executing this request. - super(-1); - mProductId = itemId; - mDeveloperPayload = developerPayload; - mProductType = itemType; - } - - @Override - protected long run() throws RemoteException { - Bundle request = makeRequestBundle("REQUEST_PURCHASE"); - request.putString(BillingConstants.BILLING_REQUEST_ITEM_ID, mProductId); - request.putString(BillingConstants.BILLING_REQUEST_ITEM_TYPE, mProductType); - // Note that the developer payload is optional. - if (mDeveloperPayload != null) { - request.putString(BillingConstants.BILLING_REQUEST_DEVELOPER_PAYLOAD, mDeveloperPayload); - } - Bundle response = marketBillingService.sendBillingRequest(request); - PendingIntent pendingIntent - = response.getParcelable(BillingConstants.BILLING_RESPONSE_PURCHASE_INTENT); - if (pendingIntent == null) { - Log.e(TAG, "Error with requestPurchase"); - return BillingConstants.BILLING_RESPONSE_INVALID_REQUEST_ID; - } - - Intent intent = new Intent(); - ResponseHandler.buyPageIntentResponse(pendingIntent, intent); - return response.getLong(BillingConstants.BILLING_RESPONSE_REQUEST_ID, - BillingConstants.BILLING_RESPONSE_INVALID_REQUEST_ID); - } - - @Override - protected void responseCodeReceived(ResponseCode responseCode) { - Log.d(TAG, "received response code " + responseCode + " for request " + this); - } - } - - class ConfirmNotifications extends BillingRequest { - final String[] mNotifyIds; - - public ConfirmNotifications(int startId, String[] notifyIds) { - super(startId); - mNotifyIds = notifyIds; - } - - @Override - protected long run() throws RemoteException { - Bundle request = makeRequestBundle("CONFIRM_NOTIFICATIONS"); - request.putStringArray(BillingConstants.BILLING_REQUEST_NOTIFY_IDS, mNotifyIds); - Bundle response = marketBillingService.sendBillingRequest(request); - logResponseCode("confirmNotifications", response); - return response.getLong(BillingConstants.BILLING_RESPONSE_REQUEST_ID, - BillingConstants.BILLING_RESPONSE_INVALID_REQUEST_ID); - } - } - - class GetPurchaseInformation extends BillingRequest { - long mNonce; - final String[] mNotifyIds; - - public GetPurchaseInformation(int startId, String[] notifyIds) { - super(startId); - mNotifyIds = notifyIds; - } - - @Override - protected long run() throws RemoteException { - mNonce = Security.generateNonce(); - - Bundle request = makeRequestBundle("GET_PURCHASE_INFORMATION"); - request.putLong(BillingConstants.BILLING_REQUEST_NONCE, mNonce); - request.putStringArray(BillingConstants.BILLING_REQUEST_NOTIFY_IDS, mNotifyIds); - Bundle response = marketBillingService.sendBillingRequest(request); - logResponseCode("getPurchaseInformation", response); - return response.getLong(BillingConstants.BILLING_RESPONSE_REQUEST_ID, - BillingConstants.BILLING_RESPONSE_INVALID_REQUEST_ID); - } - } - - class RestoreTransactions extends BillingRequest { - long mNonce; - - public RestoreTransactions() { - // This object is never created as a side effect of starting this - // service so we pass -1 as the startId to indicate that we should - // not stop this service after executing this request. - super(-1); - } - - @Override - protected long run() throws RemoteException { - mNonce = Security.generateNonce(); - - Bundle request = makeRequestBundle("RESTORE_TRANSACTIONS"); - request.putLong(BillingConstants.BILLING_REQUEST_NONCE, mNonce); - Bundle response = marketBillingService.sendBillingRequest(request); - logResponseCode("restoreTransactions", response); - return response.getLong(BillingConstants.BILLING_RESPONSE_REQUEST_ID, - BillingConstants.BILLING_RESPONSE_INVALID_REQUEST_ID); - } - - @Override - protected void responseCodeReceived(ResponseCode responseCode) { - Log.d(TAG, "received response code " + responseCode + " for request " + this); - ResponseHandler.responseCodeReceived(responseCode); - } - } - - public void setActivity(Activity activity) { - attachBaseContext(activity); - purchaseHandler = new PurchaseHandler(this); - purchaseObserver = new PurchaseObserver(activity, purchaseHandler); - } - - @Override - public IBinder onBind(Intent intent) { - return null; // binding not supported for this service - } - - @Override - public void onStart(Intent intent, int startId) { - handleCommand(intent, startId); - } - - /** - * The {@link BillingReceiver} sends messages to this service using intents. - * Each intent has an action and some extra arguments specific to that action. - * - * @param intent the intent containing one of the supported actions - * @param startId an identifier for the invocation instance of this service - */ - private void handleCommand(Intent intent, int startId) { - if (intent == null) { - return; - } - String action = intent.getAction(); - Log.d(TAG, "handleCommand(" + action + ")"); - switch (action) { - case BillingConstants.ACTION_CONFIRM_NOTIFICATION: - String[] notifyIds = intent.getStringArrayExtra(BillingConstants.NOTIFICATION_ID); - confirmNotifications(startId, notifyIds); - break; - case BillingConstants.ACTION_GET_PURCHASE_INFORMATION: - String notifyId = intent.getStringExtra(BillingConstants.NOTIFICATION_ID); - getPurchaseInformation(startId, new String[]{notifyId}); - break; - case BillingConstants.ACTION_PURCHASE_STATE_CHANGED: - String signedData = intent.getStringExtra(BillingConstants.INAPP_SIGNED_DATA); - purchaseStateChanged(startId, signedData); - break; - case BillingConstants.ACTION_RESPONSE_CODE: - long requestId = intent.getLongExtra(BillingConstants.INAPP_REQUEST_ID, -1); - int responseCodeIndex = intent.getIntExtra(BillingConstants.INAPP_RESPONSE_CODE, - ResponseCode.RESULT_ERROR.ordinal()); - ResponseCode responseCode = ResponseCode.valueOf(responseCodeIndex); - checkResponseCode(requestId, responseCode); - break; - } - } - - private boolean bindToMarketBillingService() { - Log.d(TAG, "bindToMarketBillingService()"); - try { - boolean bindResult = bindService( - new Intent(BillingConstants.MARKET_BILLING_SERVICE_ACTION), - this, - Context.BIND_AUTO_CREATE); - - if (bindResult) { - return true; - } else { - Log.e(TAG, "Could not bind to service."); - } - } catch (SecurityException e) { - Log.e(TAG, "Security exception: " + e); - } - return false; - } - - public boolean checkBillingSupported() { - return purchaseHandler.userDonated() || new CheckBillingSupported(BillingConstants.ITEM_TYPE_INAPP).runRequest(); - } - - /** - * Requests that the given item be offered to the user for purchase. When - * the purchase succeeds (or is canceled) the {@link BillingReceiver} - * receives an intent with the action {@link BillingConstants#ACTION_NOTIFY}. - * Returns false if there was an error trying to connect to Android Market. - * - * @param productId an identifier for the item being offered for purchase - * @param itemType Either BillingConstants.ITEM_TYPE_INAPP or BillingConstants.ITEM_TYPE_SUBSCRIPTION, indicating - * the type of item type support is being checked for. - * @param developerPayload a payload that is associated with a given - * purchase, if null, no payload is sent - * @return false if there was an error connecting to Android Market - */ - public boolean requestPurchase(String productId, String itemType, String developerPayload) { - return new RequestPurchase(productId, itemType, developerPayload).runRequest(); - } - - /** - * Requests transaction information for all managed items. Call this only when the - * application is first installed or after a database wipe. Do NOT call this - * every time the application starts up. - * - * @return false if there was an error connecting to Android Market - */ - public boolean restoreTransactions() { - return new RestoreTransactions().runRequest(); - } - - /** - * Confirms receipt of a purchase state change. Each {@code notifyId} is - * an opaque identifier that came from the server. This method sends those - * identifiers back to the MarketBillingService, which ACKs them to the - * server. Returns false if there was an error trying to connect to the - * MarketBillingService. - * - * @param startId an identifier for the invocation instance of this service - * @param notifyIds a list of opaque identifiers associated with purchase - * state changes. - * @return false if there was an error connecting to Market - */ - private boolean confirmNotifications(int startId, String[] notifyIds) { - return new ConfirmNotifications(startId, notifyIds).runRequest(); - } - - /** - * Gets the purchase information. This message includes a list of - * notification IDs sent to us by Android Market, which we include in - * our request. The server responds with the purchase information, - * encoded as a JSON string, and sends that to the {@link BillingReceiver} - * in an intent with the action {@link BillingConstants#ACTION_PURCHASE_STATE_CHANGED}. - * Returns false if there was an error trying to connect to the MarketBillingService. - * - * @param startId an identifier for the invocation instance of this service - * @param notifyIds a list of opaque identifiers associated with purchase - * state changes - * @return false if there was an error connecting to Android Market - */ - private boolean getPurchaseInformation(int startId, String[] notifyIds) { - return new GetPurchaseInformation(startId, notifyIds).runRequest(); - } - - /** - * Verifies that the data was signed with the given signature, and calls - * ResponseHandler.purchaseResponse(android.content.Context, PurchaseState, String, String, long) - * for each verified purchase. - * - * @param startId an identifier for the invocation instance of this service - * @param signedData the signed JSON string (signed, not encrypted) - */ - private void purchaseStateChanged(int startId, String signedData) { - ArrayList purchases; - purchases = Security.parse(signedData); - ArrayList notifyList = new ArrayList<>(); - for (Purchase vp : purchases) { - if (vp.notificationId != null) { - notifyList.add(vp.notificationId); - } - Log.d(TAG, "purchase state changed productId: " + vp.productId + ", state: " + vp.purchaseState); - ResponseHandler.purchaseResponse(vp.purchaseState, vp.productId); - } - if (!notifyList.isEmpty()) { - String[] notifyIds = notifyList.toArray(new String[notifyList.size()]); - confirmNotifications(startId, notifyIds); - } - } - - private void checkResponseCode(long requestId, ResponseCode responseCode) { - BillingRequest request = sentRequests.get(requestId); - if (request != null) { - Log.d(TAG, request.getClass().getSimpleName() + ": " + responseCode); - request.responseCodeReceived(responseCode); - } - sentRequests.remove(requestId); - } - - /** - * 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() { - int maxStartId = -1; - BillingRequest request; - while ((request = pendingRequests.peek()) != null) { - if (request.runIfConnected()) { - // Remove the request - pendingRequests.remove(); - - // Remember the largest startId, which is the most recent - // request to start this service. - if (maxStartId < request.getStartId()) { - maxStartId = request.getStartId(); - } - } else { - // The service crashed, so restart it. Note that this leaves - // the current request on the queue. - bindToMarketBillingService(); - return; - } - } - - // If we get here then all the requests ran successfully. If maxStartId - // is not -1, then one of the requests started the service, so we can - // stop it now. - if (maxStartId >= 0) { - if (BillingConstants.DEBUG) { - Log.i(TAG, "stopping service, startId: " + maxStartId); - } - stopSelf(maxStartId); - } - } - - /** - * This is called when we are connected to the MarketBillingService. - * This runs in the main UI thread. - */ - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - Log.d(TAG, "Billing service connected"); - marketBillingService = IMarketBillingService.Stub.asInterface(service); - runPendingRequests(); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - Log.w(TAG, "Billing service disconnected"); - marketBillingService = null; - } - - public void unbind() { - try { - unbindService(this); - } catch (IllegalArgumentException e) { - // This might happen if the service was disconnected - } - } - - public void onStart() { - ResponseHandler.register(purchaseObserver); - } - - public void onStop() { - ResponseHandler.unregister(); - } -} diff --git a/astrid/src/main/java/com/todoroo/astrid/billing/PurchaseObserver.java b/astrid/src/main/java/com/todoroo/astrid/billing/PurchaseObserver.java deleted file mode 100644 index 910a20159..000000000 --- a/astrid/src/main/java/com/todoroo/astrid/billing/PurchaseObserver.java +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2010 Google Inc. All Rights Reserved. - -package com.todoroo.astrid.billing; - -import android.app.Activity; -import android.app.PendingIntent; -import android.content.Intent; -import android.content.IntentSender; -import android.os.Handler; -import android.util.Log; - -import com.todoroo.astrid.billing.BillingConstants.PurchaseState; -import com.todoroo.astrid.billing.BillingConstants.ResponseCode; - -import org.tasks.billing.PurchaseHandler; - -import java.lang.reflect.Method; - -public class PurchaseObserver { - protected static final String TAG = "purchase-observer"; //$NON-NLS-1$ - protected final Activity mActivity; - private PurchaseHandler purchaseHandler; - private final Handler mHandler = new Handler(); - private Method mStartIntentSender; - private final 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, PurchaseHandler purchaseHandler) { - mActivity = activity; - this.purchaseHandler = purchaseHandler; - initCompatibilityLayer(); - } - - public void onBillingSupported(boolean supported, String type) { - purchaseHandler.onBillingSupported(supported, type); - } - - public void onRestoreTransactionsResponse(ResponseCode responseCode) { - purchaseHandler.onRestoreTransactionsResponse(responseCode); - } - - private void initCompatibilityLayer() { - try { - mStartIntentSender = mActivity.getClass().getMethod("startIntentSender", //$NON-NLS-1$ - START_INTENT_SENDER_SIG); - } catch (SecurityException | NoSuchMethodException e) { - mStartIntentSender = null; - } - } - - void startBuyPageActivity(PendingIntent pendingIntent, Intent intent) { - try { - mStartIntentSenderArgs[0] = pendingIntent.getIntentSender(); - mStartIntentSenderArgs[1] = intent; - mStartIntentSenderArgs[2] = 0; - mStartIntentSenderArgs[3] = 0; - mStartIntentSenderArgs[4] = 0; - mStartIntentSender.invoke(mActivity, mStartIntentSenderArgs); - } catch (Exception e) { - Log.e(TAG, "error starting activity", e); //$NON-NLS-1$ - } - } - - void postPurchaseStateChange(final PurchaseState purchaseState, final String itemId) { - mHandler.post(new Runnable() { - @Override - public void run() { - purchaseHandler.onPurchaseStateChange(purchaseState, itemId); - } - }); - } -} diff --git a/astrid/src/main/java/com/todoroo/astrid/billing/ResponseHandler.java b/astrid/src/main/java/com/todoroo/astrid/billing/ResponseHandler.java deleted file mode 100644 index 92b20a4f5..000000000 --- a/astrid/src/main/java/com/todoroo/astrid/billing/ResponseHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.todoroo.astrid.billing; - -import android.app.PendingIntent; -import android.content.Intent; - -import com.todoroo.astrid.billing.BillingConstants.PurchaseState; -import com.todoroo.astrid.billing.BillingConstants.ResponseCode; - -public class ResponseHandler { - - private static PurchaseObserver sPurchaseObserver; - - public static synchronized void register(PurchaseObserver observer) { - sPurchaseObserver = observer; - } - - public static synchronized void unregister() { - sPurchaseObserver = null; - } - - public static void checkBillingSupportedResponse(boolean supported, String type) { - if (sPurchaseObserver != null) { - sPurchaseObserver.onBillingSupported(supported, type); - } - } - - public static void buyPageIntentResponse(PendingIntent pendingIntent, Intent intent) { - if (sPurchaseObserver != null) { - sPurchaseObserver.startBuyPageActivity(pendingIntent, intent); - } - } - - public static void purchaseResponse(final PurchaseState purchaseState, final String productId) { - new Thread(new Runnable() { - @Override - public void run() { - // This needs to be synchronized because the UI thread can change the - // value of sPurchaseObserver. - synchronized (ResponseHandler.class) { - if (sPurchaseObserver != null) { - sPurchaseObserver.postPurchaseStateChange(purchaseState, productId); - } - } - } - }).start(); - } - - public static void responseCodeReceived(ResponseCode responseCode) { - if (sPurchaseObserver != null) { - sPurchaseObserver.onRestoreTransactionsResponse(responseCode); - } - } -} diff --git a/astrid/src/main/java/com/todoroo/astrid/billing/Security.java b/astrid/src/main/java/com/todoroo/astrid/billing/Security.java deleted file mode 100644 index 4593243bc..000000000 --- a/astrid/src/main/java/com/todoroo/astrid/billing/Security.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.todoroo.astrid.billing; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.todoroo.astrid.billing.BillingConstants.PurchaseState; - -import java.security.SecureRandom; -import java.util.ArrayList; - -public class Security { - private static final SecureRandom RANDOM = new SecureRandom(); - - public static class Purchase { - public PurchaseState purchaseState; - public String notificationId; - public String productId; - - public Purchase(PurchaseState purchaseState, String notificationId, String productId) { - this.purchaseState = purchaseState; - this.notificationId = notificationId; - this.productId = productId; - } - } - - public static long generateNonce() { - return RANDOM.nextLong(); - } - - public static ArrayList parse(String signedData) { - ArrayList purchases = new ArrayList<>(); - JsonElement jsonElement = new JsonParser().parse(signedData); - JsonObject jsonObject = jsonElement.getAsJsonObject(); - JsonArray orders = jsonObject.getAsJsonArray("orders"); - for (JsonElement orderElement : orders) { - JsonObject orderObject = orderElement.getAsJsonObject(); - purchases.add(new Purchase( - PurchaseState.valueOf(orderObject.get("purchaseState").getAsInt()), - orderObject.has("notificationId") ? orderObject.get("notificationId").getAsString() : null, - orderObject.get("productId").getAsString())); - } - return purchases; - } -} diff --git a/astrid/src/main/java/org/tasks/billing/PurchaseHandler.java b/astrid/src/main/java/org/tasks/billing/PurchaseHandler.java deleted file mode 100644 index 01f9d6602..000000000 --- a/astrid/src/main/java/org/tasks/billing/PurchaseHandler.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.tasks.billing; - -import com.todoroo.andlib.utility.Preferences; -import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; -import com.todoroo.astrid.billing.BillingConstants; -import com.todoroo.astrid.billing.BillingConstants.PurchaseState; -import com.todoroo.astrid.billing.BillingConstants.ResponseCode; -import com.todoroo.astrid.billing.BillingService; - -import static com.todoroo.andlib.utility.Preferences.getBoolean; -import static com.todoroo.andlib.utility.Preferences.getInt; -import static com.todoroo.andlib.utility.Preferences.getStringValue; - -public class PurchaseHandler { - - private static final String PREF_PRODUCT_ID = ActFmPreferenceService.IDENTIFIER + "_inapp_product_id"; - private static final String PREF_PURCHASE_STATE = ActFmPreferenceService.IDENTIFIER + "_inapp_purchase_state"; - private static final String PREF_TRANSACTIONS_INITIALIZED = "premium_transactions_initialized"; //$NON-NLS-1$ - - private boolean billingSupported; - private boolean userDonated; - private BillingService billingService; - - public PurchaseHandler(BillingService billingService) { - this.billingService = billingService; - updateDonationStatus(); - } - - public boolean isBillingSupported() { - return billingSupported; - } - - public boolean userDonated() { - return userDonated; - } - - private void updateDonationStatus() { - userDonated = BillingConstants.TASKS_DONATION_ITEM_ID.equals(getStringValue(PREF_PRODUCT_ID)) && - getInt(PREF_PURCHASE_STATE, -1) == PurchaseState.PURCHASED.ordinal(); - } - - public void onBillingSupported(boolean supported, String type) { - if (BillingConstants.ITEM_TYPE_INAPP.equals(type)) { - billingSupported = supported; - if (supported && !restoredTransactions()) { - billingService.restoreTransactions(); - } - } - } - - public void onPurchaseStateChange(PurchaseState purchaseState, final String itemId) { - if (BillingConstants.TASKS_DONATION_ITEM_ID.equals(itemId)) { - Preferences.setString(PREF_PRODUCT_ID, itemId); - Preferences.setInt(PREF_PURCHASE_STATE, purchaseState.ordinal()); - updateDonationStatus(); - } - } - - public void onRestoreTransactionsResponse(ResponseCode responseCode) { - if (responseCode == ResponseCode.RESULT_OK) { - Preferences.setBoolean(PREF_TRANSACTIONS_INITIALIZED, true); - } - } - - boolean restoredTransactions() { - return getBoolean(PREF_TRANSACTIONS_INITIALIZED, false); - } -} diff --git a/astrid/src/main/res/menu/task_list_activity.xml b/astrid/src/main/res/menu/task_list_activity.xml index ed0662fa3..4578ca028 100644 --- a/astrid/src/main/res/menu/task_list_activity.xml +++ b/astrid/src/main/res/menu/task_list_activity.xml @@ -39,11 +39,6 @@ android:id="@+id/menu_new_filter" android:title="@string/FLA_new_filter" tasks:showAsAction="never" /> - Dark theme Dark widget theme Delete task - Donate \ No newline at end of file diff --git a/astrid/src/test/java/org/tasks/billing/PurchaseHandlerTest.java b/astrid/src/test/java/org/tasks/billing/PurchaseHandlerTest.java deleted file mode 100644 index b11367518..000000000 --- a/astrid/src/test/java/org/tasks/billing/PurchaseHandlerTest.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.tasks.billing; - -import com.todoroo.astrid.billing.BillingConstants; -import com.todoroo.astrid.billing.BillingService; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.tasks.TestUtilities.resetPreferences; - -@Ignore("Throws mockito exception on Travis for some reason") -@RunWith(RobolectricTestRunner.class) -public class PurchaseHandlerTest { - - BillingService billingService; - PurchaseHandler purchaseHandler; - - @Before - public void before() { - resetPreferences(); - billingService = mock(BillingService.class); - purchaseHandler = new PurchaseHandler(billingService); - } - - @After - public void after() { - verifyNoMoreInteractions(billingService); - } - - @Test - public void userHasNotDonatedByDefault() { - assertFalse(purchaseHandler.userDonated()); - } - - @Test - public void billingNotSupportedByDefault() { - assertFalse(purchaseHandler.isBillingSupported()); - } - - @Test - public void haveNotRestoredTransactionsByDefault() { - assertFalse(purchaseHandler.restoredTransactions()); - } - - @Test - public void restoreTransactions() { - purchaseHandler.onBillingSupported(true, BillingConstants.ITEM_TYPE_INAPP); - - verify(billingService).restoreTransactions(); - } - - @Test - public void dontRestoreWhenBillingNotSupported() { - purchaseHandler.onBillingSupported(false, BillingConstants.ITEM_TYPE_INAPP); - } - - @Test - public void dontRestoreWhenAlreadyRestored() { - purchaseHandler.onRestoreTransactionsResponse(BillingConstants.ResponseCode.RESULT_OK); - purchaseHandler.onBillingSupported(true, BillingConstants.ITEM_TYPE_INAPP); - } - - @Test - public void ignoreSubscriptions() { - purchaseHandler.onBillingSupported(true, BillingConstants.ITEM_TYPE_SUBSCRIPTION); - } - - @Test - public void userDonated() { - purchaseHandler.onPurchaseStateChange(BillingConstants.PurchaseState.PURCHASED, BillingConstants.TASKS_DONATION_ITEM_ID); - - assertTrue(purchaseHandler.userDonated()); - } - - @Test - public void ignoreFailedTransaction() { - purchaseHandler.onPurchaseStateChange(BillingConstants.PurchaseState.CANCELED, BillingConstants.TASKS_DONATION_ITEM_ID); - - assertFalse(purchaseHandler.userDonated()); - } - - @Test - public void ignoreOldItems() { - purchaseHandler.onPurchaseStateChange(BillingConstants.PurchaseState.PURCHASED, "some old purchase"); - - assertFalse(purchaseHandler.userDonated()); - } - - @Test - public void oldItemsDontReplaceLatest() { - purchaseHandler.onPurchaseStateChange(BillingConstants.PurchaseState.PURCHASED, BillingConstants.TASKS_DONATION_ITEM_ID); - purchaseHandler.onPurchaseStateChange(BillingConstants.PurchaseState.PURCHASED, "some old purchase"); - - assertTrue(purchaseHandler.userDonated()); - } - - @Test - public void restoredTransactions() { - purchaseHandler.onRestoreTransactionsResponse(BillingConstants.ResponseCode.RESULT_OK); - - assertTrue(purchaseHandler.restoredTransactions()); - } - - @Test - public void restoreTransactionsFailed() { - purchaseHandler.onRestoreTransactionsResponse(BillingConstants.ResponseCode.RESULT_DEVELOPER_ERROR); - - assertFalse(purchaseHandler.restoredTransactions()); - } -}