112 | * In-app product support does not imply subscription support. To check if 113 | * subscriptions are supported, use 114 | * {@link BillingController#checkSubscriptionSupported(Context)}. 115 | *
116 | * 117 | * @param context 118 | * @return the current in-app product billing support status (unknown, 119 | * supported or unsupported). If it is unsupported, subscriptions 120 | * are also unsupported. 121 | * @see IBillingObserver#onBillingChecked(boolean) 122 | * @see BillingController#checkSubscriptionSupported(Context) 123 | */ 124 | public static BillingStatus checkBillingSupported(Context context) { 125 | if (billingStatus == BillingStatus.UNKNOWN) { 126 | BillingService.checkBillingSupported(context); 127 | } else { 128 | boolean supported = billingStatus == BillingStatus.SUPPORTED; 129 | onBillingChecked(supported); 130 | } 131 | return billingStatus; 132 | } 133 | 134 | /** 135 | *136 | * Returns the subscription billing support status, and checks it 137 | * asynchronously if it is currently unknown. Observers will receive a 138 | * {@link IBillingObserver#onSubscriptionChecked(boolean)} notification in 139 | * either case. 140 | *
141 | *142 | * No support for subscriptions does not imply that in-app products are also 143 | * unsupported. To check if in-app products are supported, use 144 | * {@link BillingController#checkBillingSupported(Context)}. 145 | *
146 | * 147 | * @param context 148 | * @return the current subscription billing status (unknown, supported or 149 | * unsupported). If it is supported, in-app products are also 150 | * supported. 151 | * @see IBillingObserver#onSubscriptionChecked(boolean) 152 | * @see BillingController#checkBillingSupported(Context) 153 | */ 154 | public static BillingStatus checkSubscriptionSupported(Context context) { 155 | if (subscriptionStatus == BillingStatus.UNKNOWN) { 156 | BillingService.checkSubscriptionSupported(context); 157 | } else { 158 | boolean supported = subscriptionStatus == BillingStatus.SUPPORTED; 159 | onSubscriptionChecked(supported); 160 | } 161 | return subscriptionStatus; 162 | } 163 | 164 | /** 165 | * Requests to confirm all pending notifications for the specified item. 166 | * 167 | * @param context 168 | * @param itemId 169 | * id of the item whose purchase must be confirmed. 170 | * @return true if pending notifications for this item were found, false 171 | * otherwise. 172 | */ 173 | public static boolean confirmNotifications(Context context, String itemId) { 174 | final Set543 | * For subscriptions, use {@link #requestSubscription(Context, String)} 544 | * instead. 545 | *
546 | * 547 | * @param context 548 | * @param itemId 549 | * id of the item to be purchased. 550 | * @see #requestPurchase(Context, String, boolean, String) 551 | */ 552 | public static void requestPurchase(Context context, String itemId) { 553 | requestPurchase(context, itemId, false, null); 554 | } 555 | 556 | /** 557 | *558 | * Requests the purchase of the specified item with optional automatic 559 | * confirmation. 560 | *
561 | *562 | * For subscriptions, use 563 | * {@link #requestSubscription(Context, String, boolean, String)} instead. 564 | *
565 | * 566 | * @param context 567 | * @param itemId 568 | * id of the item to be purchased. 569 | * @param confirm 570 | * if true, the transaction will be confirmed automatically. If 571 | * false, the transaction will have to be confirmed with a call 572 | * to {@link #confirmNotifications(Context, String)}. 573 | * @param developerPayload 574 | * a developer-specified string that contains supplemental 575 | * information about the order. 576 | * @see IBillingObserver#onPurchaseIntent(String, PendingIntent) 577 | */ 578 | public static void requestPurchase(Context context, String itemId, boolean confirm, String developerPayload) { 579 | if (confirm) { 580 | automaticConfirmations.add(itemId); 581 | } 582 | BillingService.requestPurchase(context, itemId, developerPayload); 583 | } 584 | 585 | /** 586 | * Requests the purchase of the specified subscription item. The transaction 587 | * will not be confirmed automatically. 588 | * 589 | * @param context 590 | * @param itemId 591 | * id of the item to be purchased. 592 | * @see #requestSubscription(Context, String, boolean, String) 593 | */ 594 | public static void requestSubscription(Context context, String itemId) { 595 | requestSubscription(context, itemId, false, null); 596 | } 597 | 598 | /** 599 | * Requests the purchase of the specified subscription item with optional 600 | * automatic confirmation. 601 | * 602 | * @param context 603 | * @param itemId 604 | * id of the item to be purchased. 605 | * @param confirm 606 | * if true, the transaction will be confirmed automatically. If 607 | * false, the transaction will have to be confirmed with a call 608 | * to {@link #confirmNotifications(Context, String)}. 609 | * @param developerPayload 610 | * a developer-specified string that contains supplemental 611 | * information about the order. 612 | * @see IBillingObserver#onPurchaseIntent(String, PendingIntent) 613 | */ 614 | public static void requestSubscription(Context context, String itemId, boolean confirm, String developerPayload) { 615 | if (confirm) { 616 | automaticConfirmations.add(itemId); 617 | } 618 | BillingService.requestSubscription(context, itemId, developerPayload); 619 | } 620 | 621 | /** 622 | * Requests to restore all transactions. 623 | * 624 | * @param context 625 | */ 626 | public static void restoreTransactions(Context context) { 627 | final long nonce = Security.generateNonce(); 628 | BillingService.restoreTransations(context, nonce); 629 | } 630 | 631 | /** 632 | * Sets the configuration instance of the controller. 633 | * 634 | * @param config 635 | * configuration instance. 636 | */ 637 | public static void setConfiguration(IConfiguration config) { 638 | configuration = config; 639 | } 640 | 641 | /** 642 | * Sets debug mode. 643 | * 644 | * @param value 645 | */ 646 | public static final void setDebug(boolean value) { 647 | debug = value; 648 | } 649 | 650 | /** 651 | * Sets a custom signature validator. If no custom signature validator is 652 | * provided, 653 | * {@link net.robotmedia.billing.signature.DefaultSignatureValidator} will 654 | * be used. 655 | * 656 | * @param validator 657 | * signature validator instance. 658 | */ 659 | public static void setSignatureValidator(ISignatureValidator validator) { 660 | BillingController.validator = validator; 661 | } 662 | 663 | /** 664 | * Starts the specified purchase intent with the specified activity. 665 | * 666 | * @param activity 667 | * @param purchaseIntent 668 | * purchase intent. 669 | * @param intent 670 | */ 671 | public static void startPurchaseIntent(Activity activity, PendingIntent purchaseIntent, Intent intent) { 672 | if (Compatibility.isStartIntentSenderSupported()) { 673 | // This is on Android 2.0 and beyond. The in-app buy page activity 674 | // must be on the activity stack of the application. 675 | Compatibility.startIntentSender(activity, purchaseIntent.getIntentSender(), intent); 676 | } else { 677 | // This is on Android version 1.6. The in-app buy page activity must 678 | // be on its own separate activity stack instead of on the activity 679 | // stack of the application. 680 | try { 681 | purchaseIntent.send(activity, 0 /* code */, intent); 682 | } catch (CanceledException e) { 683 | Log.e(LOG_TAG, "Error starting purchase intent", e); 684 | } 685 | } 686 | } 687 | 688 | static void storeTransaction(Context context, Transaction t) { 689 | final Transaction t2 = t.clone(); 690 | obfuscate(context, t2); 691 | TransactionManager.addTransaction(context, t2); 692 | } 693 | 694 | static void unobfuscate(Context context, List30 | * Returns the in-app product billing support status, and checks it 31 | * asynchronously if it is currently unknown. 32 | * {@link AbstractBillingActivity#onBillingChecked(boolean)} will be called 33 | * eventually with the result. 34 | *
35 | *36 | * In-app product support does not imply subscription support. To check if 37 | * subscriptions are supported, use 38 | * {@link AbstractBillingActivity#checkSubscriptionSupported()}. 39 | *
40 | * 41 | * @return the current in-app product billing support status (unknown, 42 | * supported or unsupported). If it is unsupported, subscriptions 43 | * are also unsupported. 44 | * @see AbstractBillingActivity#onBillingChecked(boolean) 45 | * @see AbstractBillingActivity#checkSubscriptionSupported() 46 | */ 47 | public BillingStatus checkBillingSupported() { 48 | return BillingController.checkBillingSupported(this); 49 | } 50 | 51 | /** 52 | *53 | * Returns the subscription billing support status, and checks it 54 | * asynchronously if it is currently unknown. 55 | * {@link AbstractBillingActivity#onSubscriptionChecked(boolean)} will be 56 | * called eventually with the result. 57 | *
58 | *59 | * No support for subscriptions does not imply that in-app products are also 60 | * unsupported. To check if subscriptions are supported, use 61 | * {@link AbstractBillingActivity#checkSubscriptionSupported()}. 62 | *
63 | * 64 | * @return the current in-app product billing support status (unknown, 65 | * supported or unsupported). If it is unsupported, subscriptions 66 | * are also unsupported. 67 | * @see AbstractBillingActivity#onBillingChecked(boolean) 68 | * @see AbstractBillingActivity#checkSubscriptionSupported() 69 | */ 70 | public BillingStatus checkSubscriptionSupported() { 71 | return BillingController.checkSubscriptionSupported(this); 72 | } 73 | 74 | public abstract void onBillingChecked(boolean supported); 75 | 76 | public abstract void onSubscriptionChecked(boolean supported); 77 | 78 | @Override 79 | protected void onCreate(android.os.Bundle savedInstanceState) { 80 | super.onCreate(savedInstanceState); 81 | 82 | mBillingObserver = new AbstractBillingObserver(this) { 83 | 84 | public void onBillingChecked(boolean supported) { 85 | AbstractBillingActivity.this.onBillingChecked(supported); 86 | } 87 | 88 | public void onSubscriptionChecked(boolean supported) { 89 | AbstractBillingActivity.this.onSubscriptionChecked(supported); 90 | } 91 | 92 | public void onPurchaseStateChanged(String itemId, PurchaseState state) { 93 | AbstractBillingActivity.this.onPurchaseStateChanged(itemId, state); 94 | } 95 | 96 | public void onRequestPurchaseResponse(String itemId, ResponseCode response) { 97 | AbstractBillingActivity.this.onRequestPurchaseResponse(itemId, response); 98 | } 99 | }; 100 | BillingController.registerObserver(mBillingObserver); 101 | BillingController.setConfiguration(this); // This activity will provide 102 | // the public key and salt 103 | this.checkBillingSupported(); 104 | if (!mBillingObserver.isTransactionsRestored()) { 105 | BillingController.restoreTransactions(this); 106 | } 107 | } 108 | 109 | @Override 110 | protected void onDestroy() { 111 | super.onDestroy(); 112 | BillingController.unregisterObserver(mBillingObserver); // Avoid 113 | // receiving 114 | // notifications after 115 | // destroy 116 | BillingController.setConfiguration(null); 117 | } 118 | 119 | public abstract void onPurchaseStateChanged(String itemId, PurchaseState state);; 120 | 121 | public abstract void onRequestPurchaseResponse(String itemId, ResponseCode response); 122 | 123 | /** 124 | * Requests the purchase of the specified item. The transaction will not be 125 | * confirmed automatically; such confirmation could be handled in 126 | * {@link AbstractBillingActivity#onPurchaseExecuted(String)}. If automatic 127 | * confirmation is preferred use 128 | * {@link BillingController#requestPurchase(android.content.Context, String, boolean)} 129 | * instead. 130 | * 131 | * @param itemId 132 | * id of the item to be purchased. 133 | */ 134 | public void requestPurchase(String itemId) { 135 | BillingController.requestPurchase(this, itemId); 136 | } 137 | 138 | /** 139 | * Requests the purchase of the specified subscription item. The transaction 140 | * will not be confirmed automatically; such confirmation could be handled 141 | * in {@link AbstractBillingActivity#onPurchaseExecuted(String)}. If 142 | * automatic confirmation is preferred use 143 | * {@link BillingController#requestPurchase(android.content.Context, String, boolean)} 144 | * instead. 145 | * 146 | * @param itemId 147 | * id of the item to be purchased. 148 | */ 149 | public void requestSubscription(String itemId) { 150 | BillingController.requestSubscription(this, itemId); 151 | } 152 | 153 | /** 154 | * Requests to restore all transactions. 155 | */ 156 | public void restoreTransactions() { 157 | BillingController.restoreTransactions(this); 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /AndroidBillingLibrary/src/net/robotmedia/billing/helper/AbstractBillingFragment.java: -------------------------------------------------------------------------------- 1 | package net.robotmedia.billing.helper; 2 | 3 | import net.robotmedia.billing.BillingController; 4 | import net.robotmedia.billing.BillingController.BillingStatus; 5 | import net.robotmedia.billing.BillingRequest.ResponseCode; 6 | import net.robotmedia.billing.model.Transaction.PurchaseState; 7 | import android.annotation.TargetApi; 8 | import android.app.Fragment; 9 | 10 | @TargetApi(11) 11 | public abstract class AbstractBillingFragment extends Fragment implements BillingController.IConfiguration { 12 | 13 | protected AbstractBillingObserver mBillingObserver; 14 | 15 | /** 16 | *17 | * Returns the in-app product billing support status, and checks it 18 | * asynchronously if it is currently unknown. 19 | * {@link AbstractBillingActivity#onBillingChecked(boolean)} will be called 20 | * eventually with the result. 21 | *
22 | *23 | * In-app product support does not imply subscription support. To check if 24 | * subscriptions are supported, use 25 | * {@link AbstractBillingActivity#checkSubscriptionSupported()}. 26 | *
27 | * 28 | * @return the current in-app product billing support status (unknown, 29 | * supported or unsupported). If it is unsupported, subscriptions 30 | * are also unsupported. 31 | * @see AbstractBillingActivity#onBillingChecked(boolean) 32 | * @see AbstractBillingActivity#checkSubscriptionSupported() 33 | */ 34 | public BillingStatus checkBillingSupported() { 35 | return BillingController.checkBillingSupported(getActivity()); 36 | } 37 | 38 | /** 39 | *40 | * Returns the subscription billing support status, and checks it 41 | * asynchronously if it is currently unknown. 42 | * {@link AbstractBillingActivity#onSubscriptionChecked(boolean)} will be 43 | * called eventually with the result. 44 | *
45 | *46 | * No support for subscriptions does not imply that in-app products are also 47 | * unsupported. To check if subscriptions are supported, use 48 | * {@link AbstractBillingActivity#checkSubscriptionSupported()}. 49 | *
50 | * 51 | * @return the current in-app product billing support status (unknown, 52 | * supported or unsupported). If it is unsupported, subscriptions 53 | * are also unsupported. 54 | * @see AbstractBillingActivity#onBillingChecked(boolean) 55 | * @see AbstractBillingActivity#checkSubscriptionSupported() 56 | */ 57 | public BillingStatus checkSubscriptionSupported() { 58 | return BillingController.checkSubscriptionSupported(getActivity()); 59 | } 60 | 61 | public abstract void onBillingChecked(boolean supported); 62 | 63 | public abstract void onSubscriptionChecked(boolean supported); 64 | 65 | @Override 66 | public void onCreate(android.os.Bundle savedInstanceState) { 67 | super.onCreate(savedInstanceState); 68 | 69 | mBillingObserver = new AbstractBillingObserver(getActivity()) { 70 | 71 | public void onBillingChecked(boolean supported) { 72 | AbstractBillingFragment.this.onBillingChecked(supported); 73 | } 74 | 75 | public void onSubscriptionChecked(boolean supported) { 76 | AbstractBillingFragment.this.onSubscriptionChecked(supported); 77 | } 78 | 79 | public void onPurchaseStateChanged(String itemId, PurchaseState state) { 80 | AbstractBillingFragment.this.onPurchaseStateChanged(itemId, state); 81 | } 82 | 83 | public void onRequestPurchaseResponse(String itemId, ResponseCode response) { 84 | AbstractBillingFragment.this.onRequestPurchaseResponse(itemId, response); 85 | } 86 | }; 87 | BillingController.registerObserver(mBillingObserver); 88 | BillingController.setConfiguration(this); // This fragment will provide 89 | // the public key and salt 90 | this.checkBillingSupported(); 91 | if (!mBillingObserver.isTransactionsRestored()) { 92 | BillingController.restoreTransactions(getActivity()); 93 | } 94 | } 95 | 96 | @Override 97 | public void onDestroy() { 98 | super.onDestroy(); 99 | BillingController.unregisterObserver(mBillingObserver); // Avoid 100 | // receiving 101 | // notifications 102 | // after destroy 103 | BillingController.setConfiguration(null); 104 | } 105 | 106 | public abstract void onPurchaseStateChanged(String itemId, PurchaseState state);; 107 | 108 | public abstract void onRequestPurchaseResponse(String itemId, ResponseCode response); 109 | 110 | /** 111 | * Requests the purchase of the specified item. The transaction will not be 112 | * confirmed automatically; such confirmation could be handled in 113 | * {@link AbstractBillingActivity#onPurchaseExecuted(String)}. If automatic 114 | * confirmation is preferred use 115 | * {@link BillingController#requestPurchase(android.content.Context, String, boolean)} 116 | * instead. 117 | * 118 | * @param itemId 119 | * id of the item to be purchased. 120 | */ 121 | public void requestPurchase(String itemId) { 122 | BillingController.requestPurchase(getActivity(), itemId); 123 | } 124 | 125 | /** 126 | * Requests the purchase of the specified subscription item. The transaction 127 | * will not be confirmed automatically; such confirmation could be handled 128 | * in {@link AbstractBillingActivity#onPurchaseExecuted(String)}. If 129 | * automatic confirmation is preferred use 130 | * {@link BillingController#requestPurchase(android.content.Context, String, boolean)} 131 | * instead. 132 | * 133 | * @param itemId 134 | * id of the item to be purchased. 135 | */ 136 | public void requestSubscription(String itemId) { 137 | BillingController.requestSubscription(getActivity(), itemId); 138 | } 139 | 140 | /** 141 | * Requests to restore all transactions. 142 | */ 143 | public void restoreTransactions() { 144 | BillingController.restoreTransactions(getActivity()); 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /AndroidBillingLibrary/src/net/robotmedia/billing/helper/AbstractBillingObserver.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2011 Robot Media SL (http://www.robotmedia.net) 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package net.robotmedia.billing.helper; 17 | 18 | import android.app.Activity; 19 | import android.app.PendingIntent; 20 | import android.content.SharedPreferences; 21 | import android.content.SharedPreferences.Editor; 22 | import android.preference.PreferenceManager; 23 | import net.robotmedia.billing.BillingController; 24 | import net.robotmedia.billing.IBillingObserver; 25 | 26 | /** 27 | * Abstract subclass of IBillingObserver that provides default implementations 28 | * for {@link IBillingObserver#onPurchaseIntent(String, PendingIntent)} and 29 | * {@link IBillingObserver#onTransactionsRestored()}. 30 | * 31 | */ 32 | public abstract class AbstractBillingObserver implements IBillingObserver { 33 | 34 | protected static final String KEY_TRANSACTIONS_RESTORED = "net.robotmedia.billing.transactionsRestored"; 35 | 36 | protected Activity activity; 37 | 38 | public AbstractBillingObserver(Activity activity) { 39 | this.activity = activity; 40 | } 41 | 42 | public boolean isTransactionsRestored() { 43 | final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); 44 | return preferences.getBoolean(KEY_TRANSACTIONS_RESTORED, false); 45 | } 46 | 47 | /** 48 | * Called after requesting the purchase of the specified item. The default 49 | * implementation simply starts the pending intent. 50 | * 51 | * @param itemId 52 | * id of the item whose purchase was requested. 53 | * @param purchaseIntent 54 | * a purchase pending intent for the specified item. 55 | */ 56 | public void onPurchaseIntent(String itemId, PendingIntent purchaseIntent) { 57 | BillingController.startPurchaseIntent(activity, purchaseIntent, null); 58 | } 59 | 60 | public void onTransactionsRestored() { 61 | final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); 62 | final Editor editor = preferences.edit(); 63 | editor.putBoolean(KEY_TRANSACTIONS_RESTORED, true); 64 | editor.commit(); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /AndroidBillingLibrary/src/net/robotmedia/billing/model/BillingDB.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2011 Robot Media SL (http://www.robotmedia.net) 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package net.robotmedia.billing.model; 17 | 18 | import net.robotmedia.billing.model.Transaction.PurchaseState; 19 | import android.content.ContentValues; 20 | import android.content.Context; 21 | import android.database.Cursor; 22 | import android.database.sqlite.SQLiteDatabase; 23 | import android.database.sqlite.SQLiteOpenHelper; 24 | 25 | public class BillingDB { 26 | static final String DATABASE_NAME = "billing.db"; 27 | static final int DATABASE_VERSION = 1; 28 | static final String TABLE_TRANSACTIONS = "purchases"; 29 | 30 | public static final String COLUMN__ID = "_id"; 31 | public static final String COLUMN_STATE = "state"; 32 | public static final String COLUMN_PRODUCT_ID = "productId"; 33 | public static final String COLUMN_PURCHASE_TIME = "purchaseTime"; 34 | public static final String COLUMN_DEVELOPER_PAYLOAD = "developerPayload"; 35 | 36 | private static final String[] TABLE_TRANSACTIONS_COLUMNS = { 37 | COLUMN__ID, COLUMN_PRODUCT_ID, COLUMN_STATE, 38 | COLUMN_PURCHASE_TIME, COLUMN_DEVELOPER_PAYLOAD 39 | }; 40 | 41 | SQLiteDatabase mDb; 42 | private DatabaseHelper mDatabaseHelper; 43 | 44 | public BillingDB(Context context) { 45 | mDatabaseHelper = new DatabaseHelper(context); 46 | mDb = mDatabaseHelper.getWritableDatabase(); 47 | } 48 | 49 | public void close() { 50 | mDatabaseHelper.close(); 51 | } 52 | 53 | public void insert(Transaction transaction) { 54 | ContentValues values = new ContentValues(); 55 | values.put(COLUMN__ID, transaction.orderId); 56 | values.put(COLUMN_PRODUCT_ID, transaction.productId); 57 | values.put(COLUMN_STATE, transaction.purchaseState.ordinal()); 58 | values.put(COLUMN_PURCHASE_TIME, transaction.purchaseTime); 59 | values.put(COLUMN_DEVELOPER_PAYLOAD, transaction.developerPayload); 60 | mDb.replace(TABLE_TRANSACTIONS, null /* nullColumnHack */, values); 61 | } 62 | 63 | public Cursor queryTransactions() { 64 | return mDb.query(TABLE_TRANSACTIONS, TABLE_TRANSACTIONS_COLUMNS, null, 65 | null, null, null, null); 66 | } 67 | 68 | public Cursor queryTransactions(String productId) { 69 | return mDb.query(TABLE_TRANSACTIONS, TABLE_TRANSACTIONS_COLUMNS, COLUMN_PRODUCT_ID + " = ?", 70 | new String[] {productId}, null, null, null); 71 | } 72 | 73 | public Cursor queryTransactions(String productId, PurchaseState state) { 74 | return mDb.query(TABLE_TRANSACTIONS, TABLE_TRANSACTIONS_COLUMNS, COLUMN_PRODUCT_ID + " = ? AND " + COLUMN_STATE + " = ?", 75 | new String[] {productId, String.valueOf(state.ordinal())}, null, null, null); 76 | } 77 | 78 | protected static final Transaction createTransaction(Cursor cursor) { 79 | final Transaction purchase = new Transaction(); 80 | purchase.orderId = cursor.getString(0); 81 | purchase.productId = cursor.getString(1); 82 | purchase.purchaseState = PurchaseState.valueOf(cursor.getInt(2)); 83 | purchase.purchaseTime = cursor.getLong(3); 84 | purchase.developerPayload = cursor.getString(4); 85 | return purchase; 86 | } 87 | 88 | private class DatabaseHelper extends SQLiteOpenHelper { 89 | public DatabaseHelper(Context context) { 90 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 91 | } 92 | 93 | @Override 94 | public void onCreate(SQLiteDatabase db) { 95 | createTransactionsTable(db); 96 | } 97 | 98 | private void createTransactionsTable(SQLiteDatabase db) { 99 | db.execSQL("CREATE TABLE " + TABLE_TRANSACTIONS + "(" + 100 | COLUMN__ID + " TEXT PRIMARY KEY, " + 101 | COLUMN_PRODUCT_ID + " INTEGER, " + 102 | COLUMN_STATE + " TEXT, " + 103 | COLUMN_PURCHASE_TIME + " TEXT, " + 104 | COLUMN_DEVELOPER_PAYLOAD + " INTEGER)"); 105 | } 106 | 107 | @Override 108 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /AndroidBillingLibrary/src/net/robotmedia/billing/model/Transaction.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2011 Robot Media SL (http://www.robotmedia.net) 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package net.robotmedia.billing.model; 17 | 18 | import org.json.JSONException; 19 | import org.json.JSONObject; 20 | 21 | public class Transaction { 22 | 23 | public enum PurchaseState { 24 | // Responses to requestPurchase or restoreTransactions. 25 | PURCHASED, // 0: User was charged for the order. 26 | CANCELLED, // 1: The charge failed on the server. 27 | REFUNDED, // 2: User received a refund for the order. 28 | EXPIRED; // 3: Sent at the end of a billing cycle to indicate that the 29 | // subscription expired without renewal because of 30 | // non-payment or user-cancellation. Your app does not need 31 | // to grant continued access to the subscription content. 32 | 33 | // Converts from an ordinal value to the PurchaseState 34 | public static PurchaseState valueOf(int index) { 35 | PurchaseState[] values = PurchaseState.values(); 36 | if (index < 0 || index >= values.length) { 37 | return CANCELLED; 38 | } 39 | return values[index]; 40 | } 41 | } 42 | static final String DEVELOPER_PAYLOAD = "developerPayload"; 43 | static final String NOTIFICATION_ID = "notificationId"; 44 | static final String ORDER_ID = "orderId"; 45 | static final String PACKAGE_NAME = "packageName"; 46 | static final String PRODUCT_ID = "productId"; 47 | static final String PURCHASE_STATE = "purchaseState"; 48 | 49 | static final String PURCHASE_TIME = "purchaseTime"; 50 | 51 | public static Transaction parse(JSONObject json) throws JSONException { 52 | final Transaction transaction = new Transaction(); 53 | final int response = json.getInt(PURCHASE_STATE); 54 | transaction.purchaseState = PurchaseState.valueOf(response); 55 | transaction.productId = json.getString(PRODUCT_ID); 56 | transaction.packageName = json.getString(PACKAGE_NAME); 57 | transaction.purchaseTime = json.getLong(PURCHASE_TIME); 58 | transaction.orderId = json.optString(ORDER_ID, null); 59 | transaction.notificationId = json.optString(NOTIFICATION_ID, null); 60 | transaction.developerPayload = json.optString(DEVELOPER_PAYLOAD, null); 61 | return transaction; 62 | } 63 | 64 | public String developerPayload; 65 | public String notificationId; 66 | public String orderId; 67 | public String packageName; 68 | public String productId; 69 | public PurchaseState purchaseState; 70 | public long purchaseTime; 71 | 72 | public Transaction() {} 73 | 74 | public Transaction(String orderId, String productId, String packageName, PurchaseState purchaseState, 75 | String notificationId, long purchaseTime, String developerPayload) { 76 | this.orderId = orderId; 77 | this.productId = productId; 78 | this.packageName = packageName; 79 | this.purchaseState = purchaseState; 80 | this.notificationId = notificationId; 81 | this.purchaseTime = purchaseTime; 82 | this.developerPayload = developerPayload; 83 | } 84 | 85 | public Transaction clone() { 86 | return new Transaction(orderId, productId, packageName, purchaseState, notificationId, purchaseTime, developerPayload); 87 | } 88 | 89 | @Override 90 | public boolean equals(Object obj) { 91 | if (this == obj) 92 | return true; 93 | if (obj == null) 94 | return false; 95 | if (getClass() != obj.getClass()) 96 | return false; 97 | Transaction other = (Transaction) obj; 98 | if (developerPayload == null) { 99 | if (other.developerPayload != null) 100 | return false; 101 | } else if (!developerPayload.equals(other.developerPayload)) 102 | return false; 103 | if (notificationId == null) { 104 | if (other.notificationId != null) 105 | return false; 106 | } else if (!notificationId.equals(other.notificationId)) 107 | return false; 108 | if (orderId == null) { 109 | if (other.orderId != null) 110 | return false; 111 | } else if (!orderId.equals(other.orderId)) 112 | return false; 113 | if (packageName == null) { 114 | if (other.packageName != null) 115 | return false; 116 | } else if (!packageName.equals(other.packageName)) 117 | return false; 118 | if (productId == null) { 119 | if (other.productId != null) 120 | return false; 121 | } else if (!productId.equals(other.productId)) 122 | return false; 123 | if (purchaseState != other.purchaseState) 124 | return false; 125 | if (purchaseTime != other.purchaseTime) 126 | return false; 127 | return true; 128 | } 129 | 130 | @Override 131 | public String toString() { 132 | return String.valueOf(orderId); 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /AndroidBillingLibrary/src/net/robotmedia/billing/model/TransactionManager.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2011 Robot Media SL (http://www.robotmedia.net) 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package net.robotmedia.billing.model; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import net.robotmedia.billing.model.Transaction.PurchaseState; 22 | import android.content.Context; 23 | import android.database.Cursor; 24 | 25 | public class TransactionManager { 26 | 27 | public synchronized static void addTransaction(Context context, Transaction transaction) { 28 | BillingDB db = new BillingDB(context); 29 | db.insert(transaction); 30 | db.close(); 31 | } 32 | 33 | public synchronized static boolean isPurchased(Context context, String itemId) { 34 | return countPurchases(context, itemId) > 0; 35 | } 36 | 37 | public synchronized static int countPurchases(Context context, String itemId) { 38 | BillingDB db = new BillingDB(context); 39 | final Cursor c = db.queryTransactions(itemId, PurchaseState.PURCHASED); 40 | int count = 0; 41 | if (c != null) { 42 | count = c.getCount(); 43 | c.close(); 44 | } 45 | db.close(); 46 | return count; 47 | } 48 | 49 | public synchronized static List21 | * I am placing this code in the Public Domain. Do with it as you will. 22 | * This software comes with no guarantees or warranties but with 23 | * plenty of well-wishing instead! 24 | * Please visit 25 | * http://iharder.net/xmlizable 26 | * periodically to check for updates or to contribute improvements. 27 | *
28 | * 29 | * @author Robert Harder 30 | * @author rharder@usa.net 31 | * @version 1.3 32 | */ 33 | 34 | /** 35 | * Base64 converter class. This code is not a complete MIME encoder; 36 | * it simply converts binary data to base64 data and back. 37 | * 38 | *Note {@link CharBase64} is a GWT-compatible implementation of this
39 | * class.
40 | */
41 | public class Base64 {
42 | /** Specify encoding (value is {@code true}). */
43 | public final static boolean ENCODE = true;
44 |
45 | /** Specify decoding (value is {@code false}). */
46 | public final static boolean DECODE = false;
47 |
48 | /** The equals sign (=) as a byte. */
49 | private final static byte EQUALS_SIGN = (byte) '=';
50 |
51 | /** The new line character (\n) as a byte. */
52 | private final static byte NEW_LINE = (byte) '\n';
53 |
54 | /**
55 | * The 64 valid Base64 values.
56 | */
57 | private final static byte[] ALPHABET =
58 | {(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
59 | (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
60 | (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
61 | (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
62 | (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
63 | (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
64 | (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
65 | (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
66 | (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
67 | (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
68 | (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
69 | (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
70 | (byte) '9', (byte) '+', (byte) '/'};
71 |
72 | /**
73 | * The 64 valid web safe Base64 values.
74 | */
75 | private final static byte[] WEBSAFE_ALPHABET =
76 | {(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
77 | (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
78 | (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
79 | (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
80 | (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
81 | (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
82 | (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
83 | (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
84 | (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
85 | (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
86 | (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
87 | (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
88 | (byte) '9', (byte) '-', (byte) '_'};
89 |
90 | /**
91 | * Translates a Base64 value to either its 6-bit reconstruction value
92 | * or a negative number indicating some other meaning.
93 | **/
94 | private final static byte[] DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
95 | -5, -5, // Whitespace: Tab and Linefeed
96 | -9, -9, // Decimal 11 - 12
97 | -5, // Whitespace: Carriage Return
98 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
99 | -9, -9, -9, -9, -9, // Decimal 27 - 31
100 | -5, // Whitespace: Space
101 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
102 | 62, // Plus sign at decimal 43
103 | -9, -9, -9, // Decimal 44 - 46
104 | 63, // Slash at decimal 47
105 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
106 | -9, -9, -9, // Decimal 58 - 60
107 | -1, // Equals sign at decimal 61
108 | -9, -9, -9, // Decimal 62 - 64
109 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
110 | 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
111 | -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
112 | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
113 | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
114 | -9, -9, -9, -9, -9 // Decimal 123 - 127
115 | /* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
116 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
117 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
118 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
119 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
120 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
121 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
122 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
123 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
124 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
125 | };
126 |
127 | /** The web safe decodabet */
128 | private final static byte[] WEBSAFE_DECODABET =
129 | {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
130 | -5, -5, // Whitespace: Tab and Linefeed
131 | -9, -9, // Decimal 11 - 12
132 | -5, // Whitespace: Carriage Return
133 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
134 | -9, -9, -9, -9, -9, // Decimal 27 - 31
135 | -5, // Whitespace: Space
136 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 44
137 | 62, // Dash '-' sign at decimal 45
138 | -9, -9, // Decimal 46-47
139 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
140 | -9, -9, -9, // Decimal 58 - 60
141 | -1, // Equals sign at decimal 61
142 | -9, -9, -9, // Decimal 62 - 64
143 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
144 | 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
145 | -9, -9, -9, -9, // Decimal 91-94
146 | 63, // Underscore '_' at decimal 95
147 | -9, // Decimal 96
148 | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
149 | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
150 | -9, -9, -9, -9, -9 // Decimal 123 - 127
151 | /* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
152 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
153 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
154 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
155 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
156 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
157 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
158 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
159 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
160 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
161 | };
162 |
163 | // Indicates white space in encoding
164 | private final static byte WHITE_SPACE_ENC = -5;
165 | // Indicates equals sign in encoding
166 | private final static byte EQUALS_SIGN_ENC = -1;
167 |
168 | /** Defeats instantiation. */
169 | private Base64() {
170 | }
171 |
172 | /* ******** E N C O D I N G M E T H O D S ******** */
173 |
174 | /**
175 | * Encodes up to three bytes of the array source
176 | * and writes the resulting four Base64 bytes to destination.
177 | * The source and destination arrays can be manipulated
178 | * anywhere along their length by specifying
179 | * srcOffset and destOffset.
180 | * This method does not check to make sure your arrays
181 | * are large enough to accommodate srcOffset + 3 for
182 | * the source array or destOffset + 4 for
183 | * the destination array.
184 | * The actual number of significant bytes in your array is
185 | * given by numSigBytes.
186 | *
187 | * @param source the array to convert
188 | * @param srcOffset the index where conversion begins
189 | * @param numSigBytes the number of significant bytes in your array
190 | * @param destination the array to hold the conversion
191 | * @param destOffset the index where output will be put
192 | * @param alphabet is the encoding alphabet
193 | * @return the destination array
194 | * @since 1.3
195 | */
196 | private static byte[] encode3to4(byte[] source, int srcOffset,
197 | int numSigBytes, byte[] destination, int destOffset, byte[] alphabet) {
198 | // 1 2 3
199 | // 01234567890123456789012345678901 Bit position
200 | // --------000000001111111122222222 Array position from threeBytes
201 | // --------| || || || | Six bit groups to index alphabet
202 | // >>18 >>12 >> 6 >> 0 Right shift necessary
203 | // 0x3f 0x3f 0x3f Additional AND
204 |
205 | // Create buffer with zero-padding if there are only one or two
206 | // significant bytes passed in the array.
207 | // We have to shift left 24 in order to flush out the 1's that appear
208 | // when Java treats a value as negative that is cast from a byte to an int.
209 | int inBuff =
210 | (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
211 | | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
212 | | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
213 |
214 | switch (numSigBytes) {
215 | case 3:
216 | destination[destOffset] = alphabet[(inBuff >>> 18)];
217 | destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
218 | destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
219 | destination[destOffset + 3] = alphabet[(inBuff) & 0x3f];
220 | return destination;
221 | case 2:
222 | destination[destOffset] = alphabet[(inBuff >>> 18)];
223 | destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
224 | destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
225 | destination[destOffset + 3] = EQUALS_SIGN;
226 | return destination;
227 | case 1:
228 | destination[destOffset] = alphabet[(inBuff >>> 18)];
229 | destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
230 | destination[destOffset + 2] = EQUALS_SIGN;
231 | destination[destOffset + 3] = EQUALS_SIGN;
232 | return destination;
233 | default:
234 | return destination;
235 | } // end switch
236 | } // end encode3to4
237 |
238 | /**
239 | * Encodes a byte array into Base64 notation.
240 | * Equivalent to calling
241 | * {@code encodeBytes(source, 0, source.length)}
242 | *
243 | * @param source The data to convert
244 | * @since 1.4
245 | */
246 | public static String encode(byte[] source) {
247 | return encode(source, 0, source.length, ALPHABET, true);
248 | }
249 |
250 | /**
251 | * Encodes a byte array into web safe Base64 notation.
252 | *
253 | * @param source The data to convert
254 | * @param doPadding is {@code true} to pad result with '=' chars
255 | * if it does not fall on 3 byte boundaries
256 | */
257 | public static String encodeWebSafe(byte[] source, boolean doPadding) {
258 | return encode(source, 0, source.length, WEBSAFE_ALPHABET, doPadding);
259 | }
260 |
261 | /**
262 | * Encodes a byte array into Base64 notation.
263 | *
264 | * @param source the data to convert
265 | * @param off offset in array where conversion should begin
266 | * @param len length of data to convert
267 | * @param alphabet the encoding alphabet
268 | * @param doPadding is {@code true} to pad result with '=' chars
269 | * if it does not fall on 3 byte boundaries
270 | * @since 1.4
271 | */
272 | public static String encode(byte[] source, int off, int len, byte[] alphabet,
273 | boolean doPadding) {
274 | byte[] outBuff = encode(source, off, len, alphabet, Integer.MAX_VALUE);
275 | int outLen = outBuff.length;
276 |
277 | // If doPadding is false, set length to truncate '='
278 | // padding characters
279 | while (doPadding == false && outLen > 0) {
280 | if (outBuff[outLen - 1] != '=') {
281 | break;
282 | }
283 | outLen -= 1;
284 | }
285 |
286 | return new String(outBuff, 0, outLen);
287 | }
288 |
289 | /**
290 | * Encodes a byte array into Base64 notation.
291 | *
292 | * @param source the data to convert
293 | * @param off offset in array where conversion should begin
294 | * @param len length of data to convert
295 | * @param alphabet is the encoding alphabet
296 | * @param maxLineLength maximum length of one line.
297 | * @return the BASE64-encoded byte array
298 | */
299 | public static byte[] encode(byte[] source, int off, int len, byte[] alphabet,
300 | int maxLineLength) {
301 | int lenDiv3 = (len + 2) / 3; // ceil(len / 3)
302 | int len43 = lenDiv3 * 4;
303 | byte[] outBuff = new byte[len43 // Main 4:3
304 | + (len43 / maxLineLength)]; // New lines
305 |
306 | int d = 0;
307 | int e = 0;
308 | int len2 = len - 2;
309 | int lineLength = 0;
310 | for (; d < len2; d += 3, e += 4) {
311 |
312 | // The following block of code is the same as
313 | // encode3to4( source, d + off, 3, outBuff, e, alphabet );
314 | // but inlined for faster encoding (~20% improvement)
315 | int inBuff =
316 | ((source[d + off] << 24) >>> 8)
317 | | ((source[d + 1 + off] << 24) >>> 16)
318 | | ((source[d + 2 + off] << 24) >>> 24);
319 | outBuff[e] = alphabet[(inBuff >>> 18)];
320 | outBuff[e + 1] = alphabet[(inBuff >>> 12) & 0x3f];
321 | outBuff[e + 2] = alphabet[(inBuff >>> 6) & 0x3f];
322 | outBuff[e + 3] = alphabet[(inBuff) & 0x3f];
323 |
324 | lineLength += 4;
325 | if (lineLength == maxLineLength) {
326 | outBuff[e + 4] = NEW_LINE;
327 | e++;
328 | lineLength = 0;
329 | } // end if: end of line
330 | } // end for: each piece of array
331 |
332 | if (d < len) {
333 | encode3to4(source, d + off, len - d, outBuff, e, alphabet);
334 |
335 | lineLength += 4;
336 | if (lineLength == maxLineLength) {
337 | // Add a last newline
338 | outBuff[e + 4] = NEW_LINE;
339 | e++;
340 | }
341 | e += 4;
342 | }
343 |
344 | assert (e == outBuff.length);
345 | return outBuff;
346 | }
347 |
348 |
349 | /* ******** D E C O D I N G M E T H O D S ******** */
350 |
351 |
352 | /**
353 | * Decodes four bytes from array source
354 | * and writes the resulting bytes (up to three of them)
355 | * to destination.
356 | * The source and destination arrays can be manipulated
357 | * anywhere along their length by specifying
358 | * srcOffset and destOffset.
359 | * This method does not check to make sure your arrays
360 | * are large enough to accommodate srcOffset + 4 for
361 | * the source array or destOffset + 3 for
362 | * the destination array.
363 | * This method returns the actual number of bytes that
364 | * were converted from the Base64 encoding.
365 | *
366 | *
367 | * @param source the array to convert
368 | * @param srcOffset the index where conversion begins
369 | * @param destination the array to hold the conversion
370 | * @param destOffset the index where output will be put
371 | * @param decodabet the decodabet for decoding Base64 content
372 | * @return the number of decoded bytes converted
373 | * @since 1.3
374 | */
375 | private static int decode4to3(byte[] source, int srcOffset,
376 | byte[] destination, int destOffset, byte[] decodabet) {
377 | // Example: Dk==
378 | if (source[srcOffset + 2] == EQUALS_SIGN) {
379 | int outBuff =
380 | ((decodabet[source[srcOffset]] << 24) >>> 6)
381 | | ((decodabet[source[srcOffset + 1]] << 24) >>> 12);
382 |
383 | destination[destOffset] = (byte) (outBuff >>> 16);
384 | return 1;
385 | } else if (source[srcOffset + 3] == EQUALS_SIGN) {
386 | // Example: DkL=
387 | int outBuff =
388 | ((decodabet[source[srcOffset]] << 24) >>> 6)
389 | | ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
390 | | ((decodabet[source[srcOffset + 2]] << 24) >>> 18);
391 |
392 | destination[destOffset] = (byte) (outBuff >>> 16);
393 | destination[destOffset + 1] = (byte) (outBuff >>> 8);
394 | return 2;
395 | } else {
396 | // Example: DkLE
397 | int outBuff =
398 | ((decodabet[source[srcOffset]] << 24) >>> 6)
399 | | ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
400 | | ((decodabet[source[srcOffset + 2]] << 24) >>> 18)
401 | | ((decodabet[source[srcOffset + 3]] << 24) >>> 24);
402 |
403 | destination[destOffset] = (byte) (outBuff >> 16);
404 | destination[destOffset + 1] = (byte) (outBuff >> 8);
405 | destination[destOffset + 2] = (byte) (outBuff);
406 | return 3;
407 | }
408 | } // end decodeToBytes
409 |
410 |
411 | /**
412 | * Decodes data from Base64 notation.
413 | *
414 | * @param s the string to decode (decoded in default encoding)
415 | * @return the decoded data
416 | * @since 1.4
417 | */
418 | public static byte[] decode(String s) throws Base64DecoderException {
419 | byte[] bytes = s.getBytes();
420 | return decode(bytes, 0, bytes.length);
421 | }
422 |
423 | /**
424 | * Decodes data from web safe Base64 notation.
425 | * Web safe encoding uses '-' instead of '+', '_' instead of '/'
426 | *
427 | * @param s the string to decode (decoded in default encoding)
428 | * @return the decoded data
429 | */
430 | public static byte[] decodeWebSafe(String s) throws Base64DecoderException {
431 | byte[] bytes = s.getBytes();
432 | return decodeWebSafe(bytes, 0, bytes.length);
433 | }
434 |
435 | /**
436 | * Decodes Base64 content in byte array format and returns
437 | * the decoded byte array.
438 | *
439 | * @param source The Base64 encoded data
440 | * @return decoded data
441 | * @since 1.3
442 | * @throws Base64DecoderException
443 | */
444 | public static byte[] decode(byte[] source) throws Base64DecoderException {
445 | return decode(source, 0, source.length);
446 | }
447 |
448 | /**
449 | * Decodes web safe Base64 content in byte array format and returns
450 | * the decoded data.
451 | * Web safe encoding uses '-' instead of '+', '_' instead of '/'
452 | *
453 | * @param source the string to decode (decoded in default encoding)
454 | * @return the decoded data
455 | */
456 | public static byte[] decodeWebSafe(byte[] source)
457 | throws Base64DecoderException {
458 | return decodeWebSafe(source, 0, source.length);
459 | }
460 |
461 | /**
462 | * Decodes Base64 content in byte array format and returns
463 | * the decoded byte array.
464 | *
465 | * @param source the Base64 encoded data
466 | * @param off the offset of where to begin decoding
467 | * @param len the length of characters to decode
468 | * @return decoded data
469 | * @since 1.3
470 | * @throws Base64DecoderException
471 | */
472 | public static byte[] decode(byte[] source, int off, int len)
473 | throws Base64DecoderException {
474 | return decode(source, off, len, DECODABET);
475 | }
476 |
477 | /**
478 | * Decodes web safe Base64 content in byte array format and returns
479 | * the decoded byte array.
480 | * Web safe encoding uses '-' instead of '+', '_' instead of '/'
481 | *
482 | * @param source the Base64 encoded data
483 | * @param off the offset of where to begin decoding
484 | * @param len the length of characters to decode
485 | * @return decoded data
486 | */
487 | public static byte[] decodeWebSafe(byte[] source, int off, int len)
488 | throws Base64DecoderException {
489 | return decode(source, off, len, WEBSAFE_DECODABET);
490 | }
491 |
492 | /**
493 | * Decodes Base64 content using the supplied decodabet and returns
494 | * the decoded byte array.
495 | *
496 | * @param source the Base64 encoded data
497 | * @param off the offset of where to begin decoding
498 | * @param len the length of characters to decode
499 | * @param decodabet the decodabet for decoding Base64 content
500 | * @return decoded data
501 | */
502 | public static byte[] decode(byte[] source, int off, int len, byte[] decodabet)
503 | throws Base64DecoderException {
504 | int len34 = len * 3 / 4;
505 | byte[] outBuff = new byte[2 + len34]; // Upper limit on size of output
506 | int outBuffPosn = 0;
507 |
508 | byte[] b4 = new byte[4];
509 | int b4Posn = 0;
510 | int i = 0;
511 | byte sbiCrop = 0;
512 | byte sbiDecode = 0;
513 | for (i = 0; i < len; i++) {
514 | sbiCrop = (byte) (source[i + off] & 0x7f); // Only the low seven bits
515 | sbiDecode = decodabet[sbiCrop];
516 |
517 | if (sbiDecode >= WHITE_SPACE_ENC) { // White space Equals sign or better
518 | if (sbiDecode >= EQUALS_SIGN_ENC) {
519 | // An equals sign (for padding) must not occur at position 0 or 1
520 | // and must be the last byte[s] in the encoded value
521 | if (sbiCrop == EQUALS_SIGN) {
522 | int bytesLeft = len - i;
523 | byte lastByte = (byte) (source[len - 1 + off] & 0x7f);
524 | if (b4Posn == 0 || b4Posn == 1) {
525 | throw new Base64DecoderException(
526 | "invalid padding byte '=' at byte offset " + i);
527 | } else if ((b4Posn == 3 && bytesLeft > 2)
528 | || (b4Posn == 4 && bytesLeft > 1)) {
529 | throw new Base64DecoderException(
530 | "padding byte '=' falsely signals end of encoded value "
531 | + "at offset " + i);
532 | } else if (lastByte != EQUALS_SIGN && lastByte != NEW_LINE) {
533 | throw new Base64DecoderException(
534 | "encoded value has invalid trailing byte");
535 | }
536 | break;
537 | }
538 |
539 | b4[b4Posn++] = sbiCrop;
540 | if (b4Posn == 4) {
541 | outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
542 | b4Posn = 0;
543 | }
544 | }
545 | } else {
546 | throw new Base64DecoderException("Bad Base64 input character at " + i
547 | + ": " + source[i + off] + "(decimal)");
548 | }
549 | }
550 |
551 | // Because web safe encoding allows non padding base64 encodes, we
552 | // need to pad the rest of the b4 buffer with equal signs when
553 | // b4Posn != 0. There can be at most 2 equal signs at the end of
554 | // four characters, so the b4 buffer must have two or three
555 | // characters. This also catches the case where the input is
556 | // padded with EQUALS_SIGN
557 | if (b4Posn != 0) {
558 | if (b4Posn == 1) {
559 | throw new Base64DecoderException("single trailing character at offset "
560 | + (len - 1));
561 | }
562 | b4[b4Posn++] = EQUALS_SIGN;
563 | outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
564 | }
565 |
566 | byte[] out = new byte[outBuffPosn];
567 | System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
568 | return out;
569 | }
570 | }
571 |
--------------------------------------------------------------------------------
/AndroidBillingLibrary/src/net/robotmedia/billing/utils/Base64DecoderException.java:
--------------------------------------------------------------------------------
1 | // Copyright 2002, Google, Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package net.robotmedia.billing.utils;
16 |
17 | /**
18 | * Exception thrown when encountering an invalid Base64 input character.
19 | *
20 | * @author nelson
21 | */
22 | public class Base64DecoderException extends Exception {
23 | public Base64DecoderException() {
24 | super();
25 | }
26 |
27 | public Base64DecoderException(String s) {
28 | super(s);
29 | }
30 |
31 | private static final long serialVersionUID = 1L;
32 | }
33 |
--------------------------------------------------------------------------------
/AndroidBillingLibrary/src/net/robotmedia/billing/utils/Compatibility.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2011 Robot Media SL (http://www.robotmedia.net)
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package net.robotmedia.billing.utils;
17 |
18 | import java.lang.reflect.Field;
19 | import java.lang.reflect.Method;
20 |
21 | import android.app.Activity;
22 | import android.app.Service;
23 | import android.content.Intent;
24 | import android.content.IntentSender;
25 | import android.util.Log;
26 |
27 | public class Compatibility {
28 | private static Method startIntentSender;
29 | public static int START_NOT_STICKY;
30 | @SuppressWarnings("rawtypes")
31 | private static final Class[] START_INTENT_SENDER_SIG = new Class[] {
32 | IntentSender.class, Intent.class, int.class, int.class, int.class
33 | };
34 |
35 | static {
36 | initCompatibility();
37 | };
38 |
39 | private static void initCompatibility() {
40 | try {
41 | final Field field = Service.class.getField("START_NOT_STICKY");
42 | START_NOT_STICKY = field.getInt(null);
43 | } catch (Exception e) {
44 | START_NOT_STICKY = 2;
45 | }
46 | try {
47 | startIntentSender = Activity.class.getMethod("startIntentSender",
48 | START_INTENT_SENDER_SIG);
49 | } catch (SecurityException e) {
50 | startIntentSender = null;
51 | } catch (NoSuchMethodException e) {
52 | startIntentSender = null;
53 | }
54 | }
55 |
56 | public static void startIntentSender(Activity activity, IntentSender intentSender, Intent intent) {
57 | if (startIntentSender != null) {
58 | final Object[] args = new Object[5];
59 | args[0] = intentSender;
60 | args[1] = intent;
61 | args[2] = Integer.valueOf(0);
62 | args[3] = Integer.valueOf(0);
63 | args[4] = Integer.valueOf(0);
64 | try {
65 | startIntentSender.invoke(activity, args);
66 | } catch (Exception e) {
67 | Log.e(Compatibility.class.getSimpleName(), "startIntentSender", e);
68 | }
69 | }
70 | }
71 |
72 | public static boolean isStartIntentSenderSupported() {
73 | return startIntentSender != null;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/AndroidBillingLibrary/src/net/robotmedia/billing/utils/Installation.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2011 Robot Media SL (http://www.robotmedia.net)
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package net.robotmedia.billing.utils;
17 |
18 | import java.io.File;
19 | import java.io.FileOutputStream;
20 | import java.io.IOException;
21 | import java.io.RandomAccessFile;
22 | import java.util.UUID;
23 |
24 | import android.content.Context;
25 |
26 | public class Installation {
27 | private static final String INSTALLATION = "INSTALLATION";
28 | private static String sID = null;
29 |
30 | public synchronized static String id(Context context) {
31 | if (sID == null) {
32 | File installation = new File(context.getFilesDir(), INSTALLATION);
33 | try {
34 | if (!installation.exists()) {
35 | writeInstallationFile(installation);
36 | }
37 | sID = readInstallationFile(installation);
38 | } catch (Exception e) {
39 | throw new RuntimeException(e);
40 | }
41 | }
42 | return sID;
43 | }
44 |
45 | private static String readInstallationFile(File installation) throws IOException {
46 | RandomAccessFile f = new RandomAccessFile(installation, "r");
47 | byte[] bytes = new byte[(int) f.length()];
48 | f.readFully(bytes);
49 | f.close();
50 | return new String(bytes);
51 | }
52 |
53 | private static void writeInstallationFile(File installation) throws IOException {
54 | FileOutputStream out = new FileOutputStream(installation);
55 | String id = UUID.randomUUID().toString();
56 | out.write(id.getBytes());
57 | out.close();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/AndroidBillingLibrary/src/net/robotmedia/billing/utils/Security.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2011 Robot Media SL (http://www.robotmedia.net)
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package net.robotmedia.billing.utils;
17 |
18 | import java.security.SecureRandom;
19 | import java.util.HashSet;
20 |
21 | import net.robotmedia.billing.utils.AESObfuscator.ValidationException;
22 |
23 | import android.content.Context;
24 | import android.provider.Settings;
25 | import android.util.Log;
26 |
27 | public class Security {
28 |
29 | private static HashSet