skuBatch) throws RemoteException, RequestException {
77 | Check.isTrue(skuBatch.size() <= MAX_SIZE_PER_REQUEST, "SKU list is too big");
78 | final Bundle skusBundle = new Bundle();
79 | skusBundle.putStringArrayList("ITEM_ID_LIST", skuBatch);
80 | final Bundle bundle = service.getSkuDetails(Billing.V3, packageName, mProduct, skusBundle);
81 | if (!handleError(bundle)) {
82 | return Skus.fromBundle(bundle, mProduct);
83 | }
84 | return null;
85 | }
86 |
87 | @Nullable
88 | @Override
89 | protected String getCacheKey() {
90 | if (mSkus.size() == 1) {
91 | return mProduct + "_" + mSkus.get(0);
92 | } else {
93 | final StringBuilder sb = new StringBuilder(5 * mSkus.size());
94 | sb.append("[");
95 | for (int i = 0; i < mSkus.size(); i++) {
96 | if (i > 0) {
97 | sb.append(",");
98 | }
99 | sb.append(mSkus.get(i));
100 | }
101 | sb.append("]");
102 | return mProduct + "_" + sb.toString();
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/IntentStarter.java:
--------------------------------------------------------------------------------
1 | package org.solovyev.android.checkout;
2 |
3 | import android.content.Intent;
4 | import android.content.IntentSender;
5 | import android.os.Bundle;
6 |
7 | import javax.annotation.Nonnull;
8 |
9 | /**
10 | * Callback invoked by {@link UiCheckout} when a {@link PurchaseFlow} starts. Implementor expects
11 | * to call one of the following methods:
12 | * {@link android.app.Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int,
13 | * int)},
14 | * {@link android.app.Fragment#startIntentSenderForResult(IntentSender, int, Intent, int, int, int,
15 | * Bundle)}
16 | * or its AndroidX counterpart.
17 | *
18 | *
19 | * The reason for this interface is to avoid a dependency between this library and the support
20 | * library.
21 | *
22 | *
23 | * @see Checkout#forUi
24 | */
25 | public interface IntentStarter {
26 | void startForResult(@Nonnull IntentSender intentSender, int requestCode, @Nonnull Intent intent) throws IntentSender.SendIntentException;
27 | }
28 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/Logger.java:
--------------------------------------------------------------------------------
1 | package org.solovyev.android.checkout;
2 |
3 | import javax.annotation.Nonnull;
4 |
5 | /**
6 | * Logger interface that can be used in {@link Billing} via {@link Billing#setLogger(Logger)}.
7 | * Methods of this class can be invoked on any thread, use
8 | * {@link Billing#newMainThreadLogger(Logger)} to perform logging only on the main thread.
9 | */
10 | public interface Logger {
11 |
12 | void v(@Nonnull String tag, @Nonnull String msg);
13 |
14 | void v(@Nonnull String tag, @Nonnull String msg, @Nonnull Throwable e);
15 |
16 | void d(@Nonnull String tag, @Nonnull String msg);
17 |
18 | void d(@Nonnull String tag, @Nonnull String msg, @Nonnull Throwable e);
19 |
20 | void i(@Nonnull String tag, @Nonnull String msg);
21 |
22 | void i(@Nonnull String tag, @Nonnull String msg, @Nonnull Throwable e);
23 |
24 | void w(@Nonnull String tag, @Nonnull String msg);
25 |
26 | void w(@Nonnull String tag, @Nonnull String msg, @Nonnull Throwable e);
27 |
28 | void e(@Nonnull String tag, @Nonnull String msg);
29 |
30 | void e(@Nonnull String tag, @Nonnull String msg, @Nonnull Throwable e);
31 | }
32 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/MainThread.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import android.os.Handler;
26 | import android.os.Looper;
27 |
28 | import javax.annotation.Nonnull;
29 |
30 | /**
31 | * Utility class that executes runnables on the main application thread
32 | */
33 | final class MainThread implements CancellableExecutor {
34 |
35 | @Nonnull
36 | private final Handler mHandler;
37 |
38 | MainThread(@Nonnull Handler handler) {
39 | Check.isTrue(handler.getLooper() == Looper.getMainLooper(), "Should be main application thread handler");
40 | mHandler = handler;
41 | }
42 |
43 | static boolean isMainThread() {
44 | return Looper.getMainLooper() == Looper.myLooper();
45 | }
46 |
47 | /**
48 | * Method executes runnable on the main application thread. If method is called on
49 | * the main application thread then the passed runnable is executed synchronously.
50 | * Otherwise, it is posted to be executed on the next iteration of the main thread looper.
51 | *
52 | * @param runnable runnable to be executed on the main application thread
53 | */
54 | @Override
55 | public void execute(@Nonnull Runnable runnable) {
56 | if (MainThread.isMainThread()) {
57 | runnable.run();
58 | } else {
59 | mHandler.post(runnable);
60 | }
61 | }
62 |
63 | @Override
64 | public void cancel(@Nonnull Runnable runnable) {
65 | mHandler.removeCallbacks(runnable);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/MainThreadLogger.java:
--------------------------------------------------------------------------------
1 | package org.solovyev.android.checkout;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 |
6 | import javax.annotation.Nonnull;
7 | import javax.annotation.concurrent.ThreadSafe;
8 |
9 | @ThreadSafe
10 | class MainThreadLogger implements Logger {
11 |
12 | @Nonnull
13 | private final Logger mLogger;
14 | private final MainThread mMainThread;
15 |
16 | public MainThreadLogger(@Nonnull Logger logger) {
17 | mLogger = logger;
18 | mMainThread = new MainThread(new Handler(Looper.getMainLooper()));
19 | }
20 |
21 | @Override
22 | public void v(@Nonnull final String tag, @Nonnull final String msg) {
23 | mMainThread.execute(new Runnable() {
24 | @Override
25 | public void run() {
26 | mLogger.v(tag, msg);
27 | }
28 | });
29 | }
30 |
31 | @Override
32 | public void v(@Nonnull final String tag, @Nonnull final String msg, @Nonnull final Throwable e) {
33 | mMainThread.execute(new Runnable() {
34 | @Override
35 | public void run() {
36 | mLogger.v(tag, msg, e);
37 | }
38 | });
39 | }
40 |
41 | @Override
42 | public void d(@Nonnull final String tag, @Nonnull final String msg) {
43 | mMainThread.execute(new Runnable() {
44 | @Override
45 | public void run() {
46 | mLogger.d(tag, msg);
47 | }
48 | });
49 | }
50 |
51 | @Override
52 | public void d(@Nonnull final String tag, @Nonnull final String msg, @Nonnull final Throwable e) {
53 | mMainThread.execute(new Runnable() {
54 | @Override
55 | public void run() {
56 | mLogger.d(tag, msg, e);
57 | }
58 | });
59 | }
60 |
61 | @Override
62 | public void i(@Nonnull final String tag, @Nonnull final String msg) {
63 | mMainThread.execute(new Runnable() {
64 | @Override
65 | public void run() {
66 | mLogger.i(tag, msg);
67 | }
68 | });
69 | }
70 |
71 | @Override
72 | public void i(@Nonnull final String tag, @Nonnull final String msg, @Nonnull final Throwable e) {
73 | mMainThread.execute(new Runnable() {
74 | @Override
75 | public void run() {
76 | mLogger.i(tag, msg, e);
77 | }
78 | });
79 | }
80 |
81 | @Override
82 | public void w(@Nonnull final String tag, @Nonnull final String msg) {
83 | mMainThread.execute(new Runnable() {
84 | @Override
85 | public void run() {
86 | mLogger.w(tag, msg);
87 | }
88 | });
89 | }
90 |
91 | @Override
92 | public void w(@Nonnull final String tag, @Nonnull final String msg, @Nonnull final Throwable e) {
93 | mMainThread.execute(new Runnable() {
94 | @Override
95 | public void run() {
96 | mLogger.w(tag, msg, e);
97 | }
98 | });
99 | }
100 |
101 | @Override
102 | public void e(@Nonnull final String tag, @Nonnull final String msg) {
103 | mMainThread.execute(new Runnable() {
104 | @Override
105 | public void run() {
106 | mLogger.e(tag, msg);
107 | }
108 | });
109 | }
110 |
111 | @Override
112 | public void e(@Nonnull final String tag, @Nonnull final String msg, @Nonnull final Throwable e) {
113 | mMainThread.execute(new Runnable() {
114 | @Override
115 | public void run() {
116 | mLogger.e(tag, msg, e);
117 | }
118 | });
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/MainThreadRequestListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import javax.annotation.Nonnull;
26 | import javax.annotation.Nullable;
27 |
28 | /**
29 | * Class dispatches all listener method calls on the main thread and allows to cancel
30 | * them.
31 | *
32 | * @param type of the result
33 | */
34 | final class MainThreadRequestListener extends RequestListenerWrapper {
35 |
36 | @Nonnull
37 | private final CancellableExecutor mMainThread;
38 |
39 | @Nullable
40 | private Runnable mSuccessRunnable;
41 |
42 | @Nullable
43 | private Runnable mErrorRunnable;
44 |
45 | MainThreadRequestListener(@Nonnull CancellableExecutor mainThread, @Nonnull RequestListener listener) {
46 | super(listener);
47 | mMainThread = mainThread;
48 | }
49 |
50 | @Override
51 | public void onSuccess(@Nonnull final R result) {
52 | mSuccessRunnable = new Runnable() {
53 | @Override
54 | public void run() {
55 | mListener.onSuccess(result);
56 | }
57 | };
58 | mMainThread.execute(mSuccessRunnable);
59 | }
60 |
61 | @Override
62 | public void onError(final int response, @Nonnull final Exception e) {
63 | mErrorRunnable = new Runnable() {
64 | @Override
65 | public void run() {
66 | mListener.onError(response, e);
67 | }
68 | };
69 | mMainThread.execute(mErrorRunnable);
70 | }
71 |
72 | public void onCancel() {
73 | if (mSuccessRunnable != null) {
74 | mMainThread.cancel(mSuccessRunnable);
75 | mSuccessRunnable = null;
76 | }
77 |
78 | if (mErrorRunnable != null) {
79 | mMainThread.cancel(mErrorRunnable);
80 | mErrorRunnable = null;
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/MapCache.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import java.util.HashMap;
26 | import java.util.Iterator;
27 | import java.util.Map;
28 | import java.util.Set;
29 |
30 | import javax.annotation.Nonnull;
31 | import javax.annotation.Nullable;
32 |
33 | class MapCache implements Cache {
34 |
35 | @Nonnull
36 | private final Map mMap = new HashMap();
37 |
38 | MapCache() {
39 | }
40 |
41 | @Nullable
42 | @Override
43 | public Entry get(@Nonnull Key key) {
44 | return mMap.get(key);
45 | }
46 |
47 | @Override
48 | public void put(@Nonnull Key key, @Nonnull Entry entry) {
49 | mMap.put(key, entry);
50 | }
51 |
52 | @Override
53 | public void init() {
54 | }
55 |
56 | @Override
57 | public void remove(@Nonnull Key key) {
58 | mMap.remove(key);
59 | }
60 |
61 | @Override
62 | public void removeAll(int type) {
63 | final Set> entries = mMap.entrySet();
64 | final Iterator> iterator = entries.iterator();
65 | while (iterator.hasNext()) {
66 | final Map.Entry entry = iterator.next();
67 | if (entry.getKey().type == type) {
68 | iterator.remove();
69 | }
70 | }
71 | }
72 |
73 | @Override
74 | public void clear() {
75 | mMap.clear();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/PlayStoreBroadcastReceiver.java:
--------------------------------------------------------------------------------
1 | package org.solovyev.android.checkout;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 | import android.os.Build;
8 | import android.text.TextUtils;
9 | import androidx.core.content.ContextCompat;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 | import javax.annotation.Nonnull;
13 | import javax.annotation.concurrent.GuardedBy;
14 |
15 | /**
16 | * Receives "com.android.vending.billing.PURCHASES_UPDATED" from the Play Store and notifies the
17 | * listener.
18 | */
19 | class PlayStoreBroadcastReceiver extends BroadcastReceiver {
20 |
21 | private static final String ACTION = "com.android.vending.billing.PURCHASES_UPDATED";
22 |
23 | @Nonnull
24 | private final Context mContext;
25 | @Nonnull
26 | private final Object mLock;
27 | @GuardedBy("mLock")
28 | @Nonnull
29 | private final List mListeners = new ArrayList<>();
30 |
31 | PlayStoreBroadcastReceiver(@Nonnull Context context, @Nonnull Object lock) {
32 | mContext = context;
33 | mLock = lock;
34 | }
35 |
36 | void addListener(@Nonnull PlayStoreListener listener) {
37 | synchronized (mLock) {
38 | Check.isTrue(!mListeners.contains(listener), "Listener " + listener + " is already in the list");
39 | mListeners.add(listener);
40 | if (mListeners.size() == 1) {
41 | ContextCompat.registerReceiver(mContext, this, new IntentFilter(ACTION), ContextCompat.RECEIVER_EXPORTED);
42 | }
43 | }
44 | }
45 |
46 | void removeListener(@Nonnull PlayStoreListener listener) {
47 | synchronized (mLock) {
48 | Check.isTrue(mListeners.contains(listener), "Listener " + listener + " is not in the list");
49 | mListeners.remove(listener);
50 | if (mListeners.size() == 0) {
51 | mContext.unregisterReceiver(this);
52 | }
53 | }
54 | }
55 |
56 | boolean contains(@Nonnull PlayStoreListener listener) {
57 | synchronized (mLock) {
58 | return mListeners.contains(listener);
59 | }
60 | }
61 |
62 | @Override
63 | public void onReceive(Context context, Intent intent) {
64 | if (intent == null || !TextUtils.equals(intent.getAction(), ACTION)) {
65 | return;
66 | }
67 | final List listeners;
68 | synchronized (mLock) {
69 | listeners = new ArrayList<>(mListeners);
70 | }
71 | for (PlayStoreListener listener : listeners) {
72 | listener.onPurchasesChanged();
73 | }
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/PlayStoreListener.java:
--------------------------------------------------------------------------------
1 | package org.solovyev.android.checkout;
2 |
3 | public interface PlayStoreListener {
4 | /**
5 | * Called when the Play Store notifies us about the purchase changes.
6 | */
7 | void onPurchasesChanged();
8 | }
9 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/ProductTypes.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import java.util.Arrays;
26 | import java.util.List;
27 |
28 | import javax.annotation.Nonnull;
29 |
30 | /**
31 | * Types of the products available in Billing API
32 | */
33 | public final class ProductTypes {
34 |
35 | /**
36 | * Simple product. Might be purchased many times or only once depending on configuration. See
37 | * Managed In-app
38 | * Products docs
39 | */
40 | public static final String IN_APP = "inapp";
41 | /**
42 | * Subscription product. See Subscriptions
43 | * docs
44 | */
45 | public static final String SUBSCRIPTION = "subs";
46 |
47 | public static final List ALL = Arrays.asList(IN_APP, SUBSCRIPTION);
48 |
49 | private ProductTypes() {
50 | throw new AssertionError();
51 | }
52 |
53 | static void checkSupported(@Nonnull String product) {
54 | Check.isTrue(ALL.contains(product), "Unsupported product: " + product);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/PurchaseComparator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import java.util.Comparator;
26 |
27 | import javax.annotation.Nonnull;
28 |
29 | final class PurchaseComparator implements Comparator {
30 |
31 | @Nonnull
32 | private static final Comparator EARLIEST_FIRST = new PurchaseComparator(true);
33 |
34 | @Nonnull
35 | private static final Comparator LATEST_FIRST = new PurchaseComparator(false);
36 | private final int mAsc;
37 |
38 | private PurchaseComparator(boolean asc) {
39 | mAsc = asc ? 1 : -1;
40 | }
41 |
42 | @Nonnull
43 | static Comparator earliestFirst() {
44 | return EARLIEST_FIRST;
45 | }
46 |
47 | @Nonnull
48 | static Comparator latestFirst() {
49 | return LATEST_FIRST;
50 | }
51 |
52 | public static int compare(long l, long r) {
53 | return l < r ? -1 : (l == r ? 0 : 1);
54 | }
55 |
56 | @Override
57 | public int compare(@Nonnull Purchase l, @Nonnull Purchase r) {
58 | return mAsc * compare(l.time, r.time);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/PurchaseRequest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import com.android.vending.billing.InAppBillingService;
26 |
27 | import android.app.PendingIntent;
28 | import android.os.Bundle;
29 | import android.os.RemoteException;
30 |
31 | import javax.annotation.Nonnull;
32 | import javax.annotation.Nullable;
33 |
34 | final class PurchaseRequest extends Request {
35 |
36 | @Nonnull
37 | private final String mProduct;
38 | @Nonnull
39 | private final String mSku;
40 | @Nullable
41 | private final String mPayload;
42 | @Nullable
43 | private final Bundle mExtraParams;
44 |
45 | PurchaseRequest(@Nonnull String product, @Nonnull String sku, @Nullable String payload) {
46 | this(product, sku, payload, null);
47 | }
48 |
49 | PurchaseRequest(@Nonnull String product, @Nonnull String sku, @Nullable String payload, @Nullable Bundle extraParams) {
50 | super(RequestType.PURCHASE, extraParams != null ? Billing.V6 : Billing.V3);
51 | mProduct = product;
52 | mSku = sku;
53 | mPayload = payload;
54 | mExtraParams = extraParams;
55 | }
56 |
57 | @Override
58 | void start(@Nonnull InAppBillingService service, @Nonnull String packageName) throws RemoteException, RequestException {
59 | final String payload = mPayload == null ? "" : mPayload;
60 | final Bundle bundle = mExtraParams != null ? service.getBuyIntentExtraParams(mApiVersion, packageName, mSku, mProduct, payload, mExtraParams) : service.getBuyIntent(mApiVersion, packageName, mSku, mProduct, payload);
61 | if (handleError(bundle)) {
62 | return;
63 | }
64 | final PendingIntent pendingIntent = bundle.getParcelable("BUY_INTENT");
65 | Check.isNotNull(pendingIntent);
66 | onSuccess(pendingIntent);
67 | }
68 |
69 | @Nullable
70 | @Override
71 | protected String getCacheKey() {
72 | return null;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/PurchaseVerifier.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import java.util.List;
26 |
27 | import javax.annotation.Nonnull;
28 |
29 | public interface PurchaseVerifier {
30 | /**
31 | * Verifies a list of purchases passing all the verified purchases to
32 | * listener. Note that this method might be called on any thread and methods of
33 | * listener must be called on the same thread (if this method was called on the main
34 | * thread listener methods should be also called on the main thread).
35 | * The actual verification though might use a background thread for communicating with a remote
36 | * server. {@link BasePurchaseVerifier} can be used as a base class for purchase verifiers that
37 | * should be executed on a background thread.
38 | *
39 | * @param purchases purchases to be verified
40 | * @param listener callback which gets a list of verified purchases
41 | * @see BasePurchaseVerifier
42 | */
43 | void verify(@Nonnull List purchases, @Nonnull RequestListener> listener);
44 | }
45 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/RequestException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import javax.annotation.Nonnull;
26 |
27 | final class RequestException extends Exception {
28 |
29 | RequestException(@Nonnull Exception cause) {
30 | super(cause);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/RequestListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import javax.annotation.Nonnull;
26 |
27 | /**
28 | * Listener associated with a request. Its {@link #onSuccess(Object)} method is called when the
29 | * result is ready and {@link #onError(int, Exception)} in case of any error.
30 | * Listener methods might be called either on a background thread or on the main application
31 | * thread. See {@link Billing} for more information.
32 | * Note: if a listener references an activity/context the associated request should
33 | * be cancelled through {@link Billing#cancel(int)} or {@link Billing#cancelAll()} methods when
34 | * the references activity/context is destroyed. Otherwise, the request will continue holding the
35 | * reference and the activity/context will leak.
36 | */
37 | public interface RequestListener {
38 | /**
39 | * Called when the request has finished successfully.
40 | *
41 | * @param result request result
42 | */
43 | void onSuccess(@Nonnull R result);
44 |
45 | /**
46 | * Called when the request has finished with an error (for example, exception was raised).
47 | *
48 | * @param response response code
49 | * @param e raised exception
50 | */
51 | void onError(int response, @Nonnull Exception e);
52 | }
53 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/RequestListenerWrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import javax.annotation.Nonnull;
26 |
27 | class RequestListenerWrapper implements CancellableRequestListener {
28 |
29 | @Nonnull
30 | protected final RequestListener mListener;
31 |
32 | RequestListenerWrapper(@Nonnull RequestListener listener) {
33 | mListener = listener;
34 | }
35 |
36 | @Override
37 | public void onSuccess(@Nonnull R result) {
38 | mListener.onSuccess(result);
39 | }
40 |
41 | @Override
42 | public void onError(int response, @Nonnull Exception e) {
43 | mListener.onError(response, e);
44 | }
45 |
46 | public final void cancel() {
47 | onCancel();
48 | Billing.cancel(mListener);
49 | }
50 |
51 | protected void onCancel() {
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/RequestRunnable.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import javax.annotation.Nullable;
26 |
27 | /**
28 | * Runnable which executes a billing request.
29 | */
30 | interface RequestRunnable {
31 |
32 | /**
33 | * @return associated request id
34 | */
35 | int getId();
36 |
37 | /**
38 | * @return associated request tag
39 | */
40 | @Nullable
41 | Object getTag();
42 |
43 | /**
44 | * Cancels request.
45 | * Note: nothing happens if request has already been executed
46 | */
47 | void cancel();
48 |
49 | /**
50 | * @return associated request, null if request was cancelled
51 | */
52 | @Nullable
53 | Request getRequest();
54 |
55 | /**
56 | * Note that implementation of this method should always check if the request was cancelled.
57 | *
58 | * @return true if request was successfully executed, false if request was not executed (and
59 | * should be rerun)
60 | */
61 | boolean run();
62 | }
63 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/RequestType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import javax.annotation.Nonnull;
26 |
27 | enum RequestType {
28 | BILLING_SUPPORTED("supported", Billing.DAY),
29 | GET_PURCHASES("purchases", 20L * Billing.MINUTE),
30 | GET_PURCHASE_HISTORY("history", 0L),
31 | GET_SKU_DETAILS("skus", Billing.DAY),
32 | PURCHASE("purchase", 0L),
33 | CHANGE_PURCHASE("change", 0L),
34 | CONSUME_PURCHASE("consume", 0L);
35 |
36 | final long expiresIn;
37 | @Nonnull
38 | final String cacheKeyName;
39 |
40 | RequestType(@Nonnull String cacheKeyName, long expiresIn) {
41 | this.cacheKeyName = cacheKeyName;
42 | this.expiresIn = expiresIn;
43 | }
44 |
45 | @Nonnull
46 | static String getCacheKeyName(int keyType) {
47 | return values()[keyType].cacheKeyName;
48 | }
49 |
50 | @Nonnull
51 | Cache.Key getCacheKey(@Nonnull String key) {
52 | final int keyType = getCacheKeyType();
53 | return new Cache.Key(keyType, key);
54 | }
55 |
56 | int getCacheKeyType() {
57 | return ordinal();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/ResponseCodes.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | /**
26 | * Billing response codes, codes {@literal >=} 10000 are defined by this library.
27 | * See In-App Billing docs
28 | * for more information.
29 | */
30 | public final class ResponseCodes {
31 |
32 | /**
33 | * Success
34 | */
35 | public static final int OK = 0;
36 | /**
37 | * User pressed back or canceled a dialog
38 | */
39 | public static final int USER_CANCELED = 1;
40 | /**
41 | * Account error, for example, user is not logged in
42 | */
43 | public static final int ACCOUNT_ERROR = 2;
44 | /**
45 | * This billing API version is not supported for the type requested
46 | */
47 | public static final int BILLING_UNAVAILABLE = 3;
48 | /**
49 | * Requested SKU is not available for purchase
50 | */
51 | public static final int ITEM_UNAVAILABLE = 4;
52 | /**
53 | * Invalid arguments provided to the API
54 | */
55 | public static final int DEVELOPER_ERROR = 5;
56 | /**
57 | * Fatal error during the API action
58 | */
59 | public static final int ERROR = 6;
60 | /**
61 | * Failure to purchase since item is already owned
62 | */
63 | public static final int ITEM_ALREADY_OWNED = 7;
64 | /**
65 | * Failure to consume since item is not owned
66 | */
67 | public static final int ITEM_NOT_OWNED = 8;
68 | /**
69 | * Billing service can't be connected, {@link android.content.Context#bindService} returned
70 | * false
.
71 | *
72 | * @see android.content.Context#bindService
73 | */
74 | public static final int SERVICE_NOT_CONNECTED = 10000;
75 | /**
76 | * Exception occurred during executing the request
77 | */
78 | public static final int EXCEPTION = 10001;
79 | /**
80 | * Purchase has a wrong signature
81 | */
82 | public static final int WRONG_SIGNATURE = 10002;
83 | /**
84 | * Intent passed to {@link UiCheckout#onActivityResult(int, int, android.content.Intent)}
85 | * is null
86 | */
87 | public static final int NULL_INTENT = 10003;
88 |
89 | private ResponseCodes() {
90 | throw new AssertionError();
91 | }
92 |
93 | /**
94 | * @return a name of the given response code
95 | */
96 | public static String toString(int code) {
97 | switch (code) {
98 | case OK:
99 | return "OK";
100 | case USER_CANCELED:
101 | return "USER_CANCELED";
102 | case ACCOUNT_ERROR:
103 | return "ACCOUNT_ERROR";
104 | case BILLING_UNAVAILABLE:
105 | return "BILLING_UNAVAILABLE";
106 | case ITEM_UNAVAILABLE:
107 | return "ITEM_UNAVAILABLE";
108 | case DEVELOPER_ERROR:
109 | return "DEVELOPER_ERROR";
110 | case ERROR:
111 | return "ERROR";
112 | case ITEM_ALREADY_OWNED:
113 | return "ITEM_ALREADY_OWNED";
114 | case ITEM_NOT_OWNED:
115 | return "ITEM_NOT_OWNED";
116 | case SERVICE_NOT_CONNECTED:
117 | return "SERVICE_NOT_CONNECTED";
118 | case EXCEPTION:
119 | return "EXCEPTION";
120 | case WRONG_SIGNATURE:
121 | return "WRONG_SIGNATURE";
122 | case NULL_INTENT:
123 | return "NULL_INTENT";
124 | default:
125 | return "UNKNOWN";
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/RobotmediaInventory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import android.content.Context;
26 |
27 | import java.util.concurrent.Executor;
28 | import java.util.concurrent.Executors;
29 |
30 | import javax.annotation.Nonnull;
31 |
32 | public final class RobotmediaInventory extends BaseInventory {
33 |
34 | private class Worker implements Runnable {
35 |
36 | @Nonnull
37 | private final Task mTask;
38 |
39 | Worker(@Nonnull Task task) {
40 | mTask = task;
41 | }
42 |
43 | @Override
44 | public void run() {
45 | if (RobotmediaDatabase.exists(mCheckout.getContext())) {
46 | mBackground.execute(new Loader());
47 | } else {
48 | onLoaded(RobotmediaDatabase.toInventoryProducts(ProductTypes.ALL));
49 | }
50 | }
51 |
52 | private class Loader implements Runnable {
53 | @Override
54 | public void run() {
55 | final Context context = mCheckout.getContext();
56 | final RobotmediaDatabase database = new RobotmediaDatabase(context);
57 | final Products products = database.load(mTask.getRequest());
58 | onLoaded(products);
59 | }
60 | }
61 |
62 | private void onLoaded(@Nonnull final Products products) {
63 | mOnLoadExecutor.execute(new Runnable() {
64 | @Override
65 | public void run() {
66 | mTask.onDone(products);
67 | }
68 | });
69 | }
70 | }
71 |
72 | @Nonnull
73 | private final Executor mBackground;
74 | @Nonnull
75 | private final Executor mOnLoadExecutor;
76 |
77 | public RobotmediaInventory(@Nonnull Checkout checkout, @Nonnull Executor onLoadExecutor) {
78 | this(checkout, Executors.newSingleThreadExecutor(), onLoadExecutor);
79 | }
80 |
81 | public RobotmediaInventory(@Nonnull Checkout checkout, @Nonnull Executor background,
82 | @Nonnull Executor onLoadExecutor) {
83 | super(checkout);
84 | mBackground = background;
85 | mOnLoadExecutor = onLoadExecutor;
86 | }
87 |
88 | @Nonnull
89 | @Override
90 | protected Runnable createWorker(@Nonnull Task task) {
91 | return new Worker(task);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/SafeCache.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import javax.annotation.Nonnull;
26 | import javax.annotation.Nullable;
27 |
28 | /**
29 | * Crash-safe wrapper around the cache
30 | */
31 | final class SafeCache implements Cache {
32 | @Nonnull
33 | private final Cache mCache;
34 |
35 | SafeCache(@Nonnull Cache cache) {
36 | mCache = cache;
37 | }
38 |
39 | @Override
40 | @Nullable
41 | public Entry get(@Nonnull Key key) {
42 | try {
43 | return mCache.get(key);
44 | } catch (Exception e) {
45 | Billing.error(e);
46 | return null;
47 | }
48 | }
49 |
50 | @Override
51 | public void put(@Nonnull Key key, @Nonnull Entry entry) {
52 | try {
53 | mCache.put(key, entry);
54 | } catch (Exception e) {
55 | Billing.error(e);
56 | }
57 | }
58 |
59 | @Override
60 | public void init() {
61 | try {
62 | mCache.init();
63 | } catch (Exception e) {
64 | Billing.error(e);
65 | }
66 | }
67 |
68 | @Override
69 | public void remove(@Nonnull Key key) {
70 | try {
71 | mCache.remove(key);
72 | } catch (Exception e) {
73 | Billing.error(e);
74 | }
75 | }
76 |
77 | @Override
78 | public void removeAll(int type) {
79 | try {
80 | mCache.removeAll(type);
81 | } catch (Exception e) {
82 | Billing.error(e);
83 | }
84 | }
85 |
86 | @Override
87 | public void clear() {
88 | try {
89 | mCache.clear();
90 | } catch (Exception e) {
91 | Billing.error(e);
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/SameThreadExecutor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import javax.annotation.Nonnull;
26 |
27 | class SameThreadExecutor implements CancellableExecutor {
28 |
29 | @Nonnull
30 | public static final SameThreadExecutor INSTANCE = new SameThreadExecutor();
31 |
32 | private SameThreadExecutor() {
33 | }
34 |
35 | @Override
36 | public void execute(@Nonnull Runnable command) {
37 | command.run();
38 | }
39 |
40 | @Override
41 | public void cancel(@Nonnull Runnable runnable) {
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/src/main/java/org/solovyev/android/checkout/Skus.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import org.json.JSONException;
26 |
27 | import android.os.Bundle;
28 |
29 | import com.android.vending.billing.InAppBillingServiceImpl;
30 |
31 | import java.util.ArrayList;
32 | import java.util.Collections;
33 | import java.util.List;
34 |
35 | import javax.annotation.Nonnull;
36 | import javax.annotation.Nullable;
37 | import javax.annotation.concurrent.Immutable;
38 |
39 | /**
40 | * List of SKUs as returned from {@link InAppBillingServiceImpl#getSkuDetails(int,
41 | * String, String, Bundle)} method.
42 | */
43 | @Immutable
44 | public final class Skus {
45 |
46 | @Nonnull
47 | static final String BUNDLE_LIST = "DETAILS_LIST";
48 |
49 | /**
50 | * Product type
51 | */
52 | @Nonnull
53 | public final String product;
54 |
55 | @Nonnull
56 | public final List list;
57 |
58 | Skus(@Nonnull String product, @Nonnull List list) {
59 | this.product = product;
60 | this.list = Collections.unmodifiableList(list);
61 | }
62 |
63 | @Nonnull
64 | static Skus fromBundle(@Nonnull Bundle bundle, @Nonnull String product) throws RequestException {
65 | final List list = extractList(bundle);
66 |
67 | final List skus = new ArrayList<>(list.size());
68 | for (String response : list) {
69 | try {
70 | skus.add(Sku.fromJson(response, product));
71 | } catch (JSONException e) {
72 | throw new RequestException(e);
73 | }
74 |
75 | }
76 | return new Skus(product, skus);
77 | }
78 |
79 | @Nonnull
80 | private static List extractList(@Nonnull Bundle bundle) {
81 | final List list = bundle.getStringArrayList(BUNDLE_LIST);
82 | return list != null ? list : Collections.emptyList();
83 | }
84 |
85 | @Nullable
86 | public Sku getSku(@Nonnull String sku) {
87 | for (Sku s : list) {
88 | if (s.id.code.equals(sku)) {
89 | return s;
90 | }
91 | }
92 | return null;
93 | }
94 |
95 | public boolean hasSku(@Nonnull String sku) {
96 | return getSku(sku) != null;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/AsyncServiceConnector.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import com.android.vending.billing.InAppBillingServiceImpl;
26 | import com.android.vending.billing.InAppBillingService;
27 |
28 | import java.util.concurrent.Executor;
29 | import java.util.concurrent.Executors;
30 |
31 | import javax.annotation.Nonnull;
32 |
33 | import static org.mockito.Mockito.mock;
34 |
35 | class AsyncServiceConnector implements Billing.ServiceConnector {
36 |
37 | @Nonnull
38 | private final Executor mBackground;
39 | @Nonnull
40 | private final Billing mBilling;
41 |
42 | public AsyncServiceConnector(@Nonnull Billing billing) {
43 | mBilling = billing;
44 | mBackground = Executors.newSingleThreadExecutor();
45 | }
46 |
47 | @Override
48 | public boolean connect() {
49 | setService(mock(InAppBillingService.class));
50 | return true;
51 | }
52 |
53 | private void setService(final InAppBillingService service) {
54 | mBackground.execute(new Runnable() {
55 | @Override
56 | public void run() {
57 | mBilling.setService(service, service != null);
58 | }
59 | });
60 | }
61 |
62 | @Override
63 | public void disconnect() {
64 | setService(null);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/BillingDB.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import android.content.ContentValues;
26 | import android.content.Context;
27 | import android.database.Cursor;
28 | import android.database.sqlite.SQLiteDatabase;
29 | import android.database.sqlite.SQLiteOpenHelper;
30 |
31 | public class BillingDB {
32 | public static final String COLUMN__ID = "_id";
33 | public static final String COLUMN_STATE = "state";
34 | public static final String COLUMN_PRODUCT_ID = "productId";
35 | public static final String COLUMN_PURCHASE_TIME = "purchaseTime";
36 | public static final String COLUMN_DEVELOPER_PAYLOAD = "developerPayload";
37 | static final String DATABASE_NAME = "billing.db";
38 | static final int DATABASE_VERSION = 1;
39 | static final String TABLE_TRANSACTIONS = "purchases";
40 | private static final String[] TABLE_TRANSACTIONS_COLUMNS = {
41 | COLUMN__ID, COLUMN_PRODUCT_ID, COLUMN_STATE,
42 | COLUMN_PURCHASE_TIME, COLUMN_DEVELOPER_PAYLOAD
43 | };
44 |
45 | SQLiteDatabase mDb;
46 | private DatabaseHelper mDatabaseHelper;
47 |
48 | public BillingDB(Context context) {
49 | mDatabaseHelper = new DatabaseHelper(context);
50 | mDb = mDatabaseHelper.getWritableDatabase();
51 | }
52 |
53 | protected static final Transaction createTransaction(Cursor cursor) {
54 | final Transaction purchase = new Transaction();
55 | purchase.orderId = cursor.getString(0);
56 | purchase.productId = cursor.getString(1);
57 | purchase.purchaseState = Transaction.PurchaseState.valueOf(cursor.getInt(2));
58 | purchase.purchaseTime = cursor.getLong(3);
59 | purchase.developerPayload = cursor.getString(4);
60 | return purchase;
61 | }
62 |
63 | public void close() {
64 | mDatabaseHelper.close();
65 | }
66 |
67 | public void insert(Transaction transaction) {
68 | ContentValues values = new ContentValues();
69 | values.put(COLUMN__ID, transaction.orderId);
70 | values.put(COLUMN_PRODUCT_ID, transaction.productId);
71 | values.put(COLUMN_STATE, transaction.purchaseState.ordinal());
72 | values.put(COLUMN_PURCHASE_TIME, transaction.purchaseTime);
73 | values.put(COLUMN_DEVELOPER_PAYLOAD, transaction.developerPayload);
74 | mDb.replace(TABLE_TRANSACTIONS, null /* nullColumnHack */, values);
75 | }
76 |
77 | public Cursor queryTransactions() {
78 | return mDb.query(TABLE_TRANSACTIONS, TABLE_TRANSACTIONS_COLUMNS, null,
79 | null, null, null, null);
80 | }
81 |
82 | public Cursor queryTransactions(String productId) {
83 | return mDb.query(TABLE_TRANSACTIONS, TABLE_TRANSACTIONS_COLUMNS, COLUMN_PRODUCT_ID + " = ?",
84 | new String[]{productId}, null, null, null);
85 | }
86 |
87 | public Cursor queryTransactions(String productId, Transaction.PurchaseState state) {
88 | return mDb.query(TABLE_TRANSACTIONS, TABLE_TRANSACTIONS_COLUMNS, COLUMN_PRODUCT_ID + " = ? AND " + COLUMN_STATE + " = ?",
89 | new String[]{productId, String.valueOf(state.ordinal())}, null, null, null);
90 | }
91 |
92 | private class DatabaseHelper extends SQLiteOpenHelper {
93 | public DatabaseHelper(Context context) {
94 | super(context, DATABASE_NAME, null, DATABASE_VERSION);
95 | }
96 |
97 | @Override
98 | public void onCreate(SQLiteDatabase db) {
99 | createTransactionsTable(db);
100 | }
101 |
102 | private void createTransactionsTable(SQLiteDatabase db) {
103 | db.execSQL("CREATE TABLE " + TABLE_TRANSACTIONS + "(" +
104 | COLUMN__ID + " TEXT PRIMARY KEY, " +
105 | COLUMN_PRODUCT_ID + " INTEGER, " +
106 | COLUMN_STATE + " TEXT, " +
107 | COLUMN_PURCHASE_TIME + " TEXT, " +
108 | COLUMN_DEVELOPER_PAYLOAD + " INTEGER)");
109 | }
110 |
111 | @Override
112 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/BillingSupportedRequestTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import com.android.vending.billing.InAppBillingServiceImpl;
26 | import com.android.vending.billing.InAppBillingService;
27 |
28 | import org.junit.Test;
29 |
30 | import android.os.Bundle;
31 |
32 | import javax.annotation.Nonnull;
33 |
34 | import static org.junit.Assert.assertNotEquals;
35 | import static org.junit.Assert.assertNull;
36 | import static org.mockito.ArgumentMatchers.eq;
37 | import static org.mockito.Mockito.mock;
38 | import static org.mockito.Mockito.verify;
39 |
40 | public class BillingSupportedRequestTest extends RequestTestBase {
41 |
42 | @Test
43 | public void testShouldHaveDifferentCacheKeys() throws Exception {
44 | final BillingSupportedRequest r1 = newRequest("test1");
45 | final BillingSupportedRequest r2 = newRequest("test2");
46 |
47 | assertNotEquals(r1.getCacheKey(), r2.getCacheKey());
48 | }
49 |
50 | @Override
51 | protected BillingSupportedRequest newRequest() {
52 | return newRequest("test");
53 | }
54 |
55 | @Nonnull
56 | private BillingSupportedRequest newRequest(@Nonnull String product) {
57 | return new BillingSupportedRequest(product);
58 | }
59 |
60 | @Test
61 | public void testShouldNotBeCachedWithExtraParams() throws Exception {
62 | final BillingSupportedRequest request = new BillingSupportedRequest("test", Billing.V7, new Bundle());
63 | assertNull(request.getCacheKey());
64 | }
65 |
66 | @Test
67 | public void testShouldUseExtraParams() throws Exception {
68 | final Bundle extraParams = new Bundle();
69 | extraParams.putString("extra", "test");
70 | final BillingSupportedRequest request = new BillingSupportedRequest("product", Billing.V7, extraParams);
71 | final InAppBillingService service = mock(InAppBillingService.class);
72 |
73 | request.start(service, "package");
74 |
75 | verify(service).isBillingSupportedExtraParams(eq(Billing.V7), eq("package"), eq("product"), eq(extraParams));
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/CacheKeyTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import org.junit.Test;
26 |
27 | import static org.junit.Assert.assertEquals;
28 | import static org.junit.Assert.assertNotEquals;
29 |
30 | public class CacheKeyTest {
31 |
32 | @Test
33 | public void testHashCodeAndEquals() throws Exception {
34 | final Cache.Key k1 = new Cache.Key(1, "test");
35 | final Cache.Key k2 = new Cache.Key(2, "test");
36 | final Cache.Key k3 = new Cache.Key(1, "test1");
37 | final Cache.Key k4 = new Cache.Key(3, "test1");
38 | final Cache.Key k5 = new Cache.Key(1, "test");
39 |
40 | assertNotEquals(k1.hashCode(), k2.hashCode());
41 | assertNotEquals(k1.hashCode(), k3.hashCode());
42 | assertNotEquals(k1.hashCode(), k4.hashCode());
43 | assertEquals(k1.hashCode(), k5.hashCode());
44 |
45 | assertNotEquals(k1, k2);
46 | assertNotEquals(k1, k3);
47 | assertNotEquals(k1, k4);
48 | assertEquals(k1, k5);
49 |
50 | assertNotEquals(k1.toString(), k2.toString());
51 | assertNotEquals(k1.toString(), k3.toString());
52 | assertNotEquals(k1.toString(), k4.toString());
53 | assertEquals(k1.toString(), k5.toString());
54 | }
55 | }
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/CacheTestBase.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import org.junit.runner.RunWith;
26 | import org.robolectric.RobolectricTestRunner;
27 | import org.robolectric.annotation.Config;
28 |
29 | import java.util.concurrent.atomic.AtomicInteger;
30 |
31 | import javax.annotation.Nonnull;
32 |
33 | import static java.lang.System.currentTimeMillis;
34 | import static org.solovyev.android.checkout.Billing.DAY;
35 |
36 | @RunWith(RobolectricTestRunner.class)
37 | @Config(manifest = Config.NONE)
38 | abstract class CacheTestBase {
39 |
40 | @Nonnull
41 | private final AtomicInteger mCounter = new AtomicInteger();
42 |
43 | @Nonnull
44 | protected final Cache.Key newKey() {
45 | return new Cache.Key(mCounter.getAndIncrement() % RequestType.values().length, "test");
46 | }
47 |
48 | @Nonnull
49 | protected final Cache.Entry newEntry() {
50 | return newEntry(DAY);
51 | }
52 |
53 | @Nonnull
54 | protected final Cache.Entry newEntry(long expiresIn) {
55 | return new Cache.Entry(mCounter.getAndIncrement(), currentTimeMillis() + expiresIn);
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/ConsumePurchaseRequestTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import org.junit.Test;
26 |
27 | import javax.annotation.Nonnull;
28 |
29 | import static org.junit.Assert.assertNull;
30 |
31 | public class ConsumePurchaseRequestTest extends RequestTestBase {
32 |
33 | @Override
34 | protected ConsumePurchaseRequest newRequest() {
35 | return newRequest("token");
36 | }
37 |
38 | @Nonnull
39 | private ConsumePurchaseRequest newRequest(@Nonnull String token) {
40 | return new ConsumePurchaseRequest(token, null);
41 | }
42 |
43 | @Test
44 | public void testShouldNotBeCached() throws Exception {
45 | assertNull(newRequest().getCacheKey());
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/DefaultPurchaseVerifierTest.java:
--------------------------------------------------------------------------------
1 | package org.solovyev.android.checkout;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.mockito.Mockito;
7 | import org.robolectric.RobolectricTestRunner;
8 | import org.robolectric.annotation.Config;
9 |
10 | import java.util.ArrayList;
11 | import java.util.Collections;
12 | import java.util.List;
13 |
14 | import javax.annotation.Nonnull;
15 |
16 | @RunWith(RobolectricTestRunner.class)
17 | @Config(manifest = Config.NONE)
18 | public class DefaultPurchaseVerifierTest {
19 | @Nonnull
20 | private DefaultPurchaseVerifier mVerifier;
21 |
22 | @Before
23 | public void setUp() throws Exception {
24 | mVerifier = new DefaultPurchaseVerifier("PK");
25 | }
26 |
27 | @SuppressWarnings("unchecked")
28 | @Test
29 | public void testShouldAutoVerifyTestPurchases() throws Exception {
30 | final List testPurchases = new ArrayList<>();
31 | for (String sku : DefaultPurchaseVerifier.TEST_SKUS) {
32 | testPurchases.add(new Purchase(sku, "", "", 0, 0, "", "", false, "", ""));
33 | }
34 | final RequestListener> listener = Mockito.mock(RequestListener.class);
35 | mVerifier.verify(testPurchases, listener);
36 | Mockito.verify(listener).onSuccess(Mockito.eq(testPurchases));
37 | }
38 |
39 | @SuppressWarnings("unchecked")
40 | @Test
41 | public void testShouldNotAutoVerifyRealPurchases() throws Exception {
42 | final List purchases = Collections.singletonList(
43 | new Purchase("android.test.boom", "", "", 0, 0, "", "", false, "", ""));
44 | final RequestListener> listener = Mockito.mock(RequestListener.class);
45 | mVerifier.verify(purchases, listener);
46 | Mockito.verify(listener).onSuccess(Mockito.eq(Collections.emptyList()));
47 | }
48 | }
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/FallingBackInventoryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import com.android.vending.billing.InAppBillingService;
26 |
27 | import org.junit.Before;
28 | import org.junit.Test;
29 | import org.robolectric.RuntimeEnvironment;
30 |
31 | import android.database.sqlite.SQLiteDatabase;
32 |
33 | import java.util.List;
34 |
35 | import javax.annotation.Nonnull;
36 |
37 | import static java.util.Arrays.asList;
38 | import static org.junit.Assert.assertEquals;
39 | import static org.mockito.ArgumentMatchers.anyInt;
40 | import static org.mockito.ArgumentMatchers.any;
41 | import static org.mockito.ArgumentMatchers.eq;
42 | import static org.mockito.Mockito.when;
43 | import static org.solovyev.android.checkout.ProductTypes.IN_APP;
44 | import static org.solovyev.android.checkout.ProductTypes.SUBSCRIPTION;
45 | import static org.solovyev.android.checkout.Tests.sameThreadExecutor;
46 |
47 | public class FallingBackInventoryTest extends InventoryTestBase {
48 |
49 | @Override
50 | @Before
51 | public void setUp() throws Exception {
52 | SQLiteDatabase db = RuntimeEnvironment.application.openOrCreateDatabase(RobotmediaDatabase.NAME, 0, null);
53 | db.close();
54 |
55 | super.setUp();
56 |
57 | final InAppBillingService service = ((TestServiceConnector) mBilling.getConnector()).mService;
58 | when(service.isBillingSupported(anyInt(), any(), eq(SUBSCRIPTION))).thenReturn(ResponseCodes.ERROR);
59 | }
60 |
61 | @Test
62 | public void testShouldLoadSkus() throws Exception {
63 | populateSkus();
64 |
65 | mCheckout.start();
66 |
67 | final TestCallback c1 = new TestCallback();
68 | mInventory.load(Inventory.Request.create().loadSkus(SUBSCRIPTION, asList("subX", "sub2", "sub3")), c1);
69 | final TestCallback c2 = new TestCallback();
70 | mInventory.load(Inventory.Request.create().loadSkus(IN_APP, asList("1", "2", "5")), c2);
71 |
72 | Tests.waitWhileLoading(mInventory);
73 |
74 | assertEquals(0, c1.mProducts.get(SUBSCRIPTION).getSkus().size());
75 | assertEquals(2, c2.mProducts.get(IN_APP).getSkus().size());
76 | }
77 |
78 | @Nonnull
79 | @Override
80 | protected Billing newBilling() {
81 | final Billing billing = super.newBilling();
82 | billing.setMainThread(sameThreadExecutor());
83 | return billing;
84 | }
85 |
86 | @Nonnull
87 | @Override
88 | protected FallingBackInventory newInventory(@Nonnull Checkout checkout) {
89 | return new FallingBackInventory(checkout, new RobotmediaInventory(checkout, sameThreadExecutor()));
90 | }
91 |
92 | @Override
93 | protected boolean shouldVerifyPurchaseCompletely() {
94 | return false;
95 | }
96 |
97 | @Override
98 | protected void insertPurchases(@Nonnull String product, @Nonnull List purchases) throws Exception {
99 | if (IN_APP.equals(product)) {
100 | Tests.mockGetPurchases(mBilling, product, purchases);
101 | } else {
102 | RobotmediaInventoryTest.insertPurchases(new BillingDB(RuntimeEnvironment.application), purchases);
103 | }
104 | }
105 |
106 | @Override
107 | protected void insertSkus(@Nonnull String product, @Nonnull List skus) throws Exception {
108 | Tests.mockGetSkuDetails(mBilling, product, skus);
109 | }
110 |
111 | @Test
112 | public void testShouldFallbackIfProductIsNotSupported() throws Exception {
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/GetPurchaseHistoryRequestTest.java:
--------------------------------------------------------------------------------
1 | package org.solovyev.android.checkout;
2 |
3 | import android.os.Bundle;
4 |
5 | public class GetPurchaseHistoryRequestTest extends RequestTestBase {
6 |
7 | @Override
8 | protected Request newRequest() {
9 | final Bundle extraParams = new Bundle();
10 | extraParams.putString("extra", "42");
11 | return new GetPurchaseHistoryRequest(ProductTypes.SUBSCRIPTION, null, extraParams);
12 | }
13 | }
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/InventoryProductTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import org.junit.Test;
26 | import org.junit.runner.RunWith;
27 | import org.robolectric.RobolectricTestRunner;
28 | import org.robolectric.annotation.Config;
29 |
30 | import static org.junit.Assert.assertFalse;
31 | import static org.junit.Assert.assertTrue;
32 | import static org.solovyev.android.checkout.Purchase.State.CANCELLED;
33 | import static org.solovyev.android.checkout.Purchase.State.REFUNDED;
34 |
35 | @RunWith(RobolectricTestRunner.class)
36 | @Config(manifest = Config.NONE)
37 | public class InventoryProductTest {
38 |
39 | @Test
40 | public void testShouldAddPurchases() throws Exception {
41 | final Inventory.Product product = new Inventory.Product(ProductTypes.IN_APP, true);
42 | product.mPurchases.add(Purchase.fromJson(PurchaseTest.newJson(0, Purchase.State.PURCHASED), null));
43 | product.mPurchases.add(Purchase.fromJson(PurchaseTest.newJson(1, CANCELLED), null));
44 |
45 | assertTrue(product.isPurchased("0"));
46 | assertTrue(product.hasPurchaseInState("1", CANCELLED));
47 | assertFalse(product.hasPurchaseInState("0", REFUNDED));
48 | assertFalse(product.hasPurchaseInState("1", REFUNDED));
49 | }
50 | }
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/InventoryProductsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import org.junit.Before;
26 | import org.junit.Test;
27 | import org.junit.runner.RunWith;
28 | import org.robolectric.RobolectricTestRunner;
29 | import org.robolectric.annotation.Config;
30 |
31 | import javax.annotation.Nonnull;
32 |
33 | import static org.junit.Assert.assertEquals;
34 | import static org.junit.Assert.assertTrue;
35 |
36 | @RunWith(RobolectricTestRunner.class)
37 | @Config(manifest = Config.NONE)
38 | public class InventoryProductsTest {
39 |
40 | @Nonnull
41 | private Inventory.Products mProducts;
42 |
43 | @Before
44 | public void setUp() throws Exception {
45 | mProducts = new Inventory.Products();
46 | }
47 |
48 | @Test
49 | public void testShouldAddProduct() throws Exception {
50 | mProducts.add(new Inventory.Product(ProductTypes.IN_APP, true));
51 | mProducts.add(new Inventory.Product(ProductTypes.SUBSCRIPTION, true));
52 |
53 | assertEquals(2, mProducts.size());
54 | assertEquals(ProductTypes.IN_APP, mProducts.get(ProductTypes.IN_APP).id);
55 | assertEquals(ProductTypes.SUBSCRIPTION, mProducts.get(ProductTypes.SUBSCRIPTION).id);
56 | }
57 |
58 | @Test
59 | public void testShouldIterateOverAllProducts() throws Exception {
60 | int count = 0;
61 | for (Inventory.Product product : mProducts) {
62 | assertTrue(ProductTypes.ALL.contains(product.id));
63 | count++;
64 | }
65 |
66 | assertEquals(2, count);
67 | }
68 | }
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/MainThreadRequestListenerTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import org.junit.Test;
26 |
27 | import javax.annotation.Nonnull;
28 |
29 | import static org.junit.Assert.assertNotNull;
30 | import static org.junit.Assert.assertNull;
31 | import static org.junit.Assert.assertSame;
32 | import static org.mockito.ArgumentMatchers.eq;
33 | import static org.mockito.Mockito.mock;
34 | import static org.mockito.Mockito.verify;
35 |
36 | public class MainThreadRequestListenerTest {
37 |
38 | @Test
39 | public void testShouldCallOnSuccess() throws Exception {
40 | final RequestListener l = mock(RequestListener.class);
41 | final MainThreadRequestListener mtl = new MainThreadRequestListener(Tests.sameThreadExecutor(), l);
42 |
43 | final Object o = new Object();
44 | mtl.onSuccess(o);
45 |
46 | verify(l).onSuccess(eq(o));
47 | }
48 |
49 | @Test
50 | public void testShouldCallOnError() throws Exception {
51 | final RequestListener l = mock(RequestListener.class);
52 | final MainThreadRequestListener mtl = new MainThreadRequestListener(Tests.sameThreadExecutor(), l);
53 |
54 | final Exception e = new Exception();
55 | mtl.onError(3, e);
56 |
57 | verify(l).onError(eq(3), eq(e));
58 | }
59 |
60 | @Test
61 | public void testShouldCancelSuccessRunnable() throws Exception {
62 | final RequestListener l = mock(RequestListener.class);
63 | final CancellableExecutor executor = new TestExecutor();
64 | final MainThreadRequestListener mtl = new MainThreadRequestListener(executor, l);
65 |
66 | mtl.onSuccess(new Object());
67 |
68 | mtl.cancel();
69 | }
70 |
71 | @Test
72 | public void testShouldCancelErrorRunnable() throws Exception {
73 | final RequestListener l = mock(RequestListener.class);
74 | final CancellableExecutor executor = new TestExecutor();
75 | final MainThreadRequestListener mtl = new MainThreadRequestListener(executor, l);
76 |
77 | mtl.onError(3, new Exception());
78 |
79 | mtl.cancel();
80 | }
81 |
82 | private static class TestExecutor implements CancellableExecutor {
83 |
84 | private Runnable mExecuting;
85 |
86 | @Override
87 | public void execute(@Nonnull Runnable runnable) {
88 | assertNull(mExecuting);
89 | mExecuting = runnable;
90 | }
91 |
92 | @Override
93 | public void cancel(@Nonnull Runnable runnable) {
94 | assertNotNull(mExecuting);
95 | assertSame(mExecuting, runnable);
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/MapCacheTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import org.junit.Assert;
26 | import org.junit.Test;
27 |
28 | public class MapCacheTest extends CacheTestBase {
29 |
30 | @Test
31 | public void testShouldAddEntries() throws Exception {
32 | final MapCache cache = new MapCache();
33 | final Cache.Entry expected = newEntry();
34 | final Cache.Key key = newKey();
35 |
36 | cache.put(key, expected);
37 |
38 | Assert.assertSame(expected, cache.get(key));
39 | }
40 |
41 | @Test
42 | public void testShouldRemoveEntries() throws Exception {
43 | final MapCache cache = new MapCache();
44 | final Cache.Entry expected = newEntry();
45 | final Cache.Key key = newKey();
46 | cache.put(key, expected);
47 |
48 | cache.remove(key);
49 |
50 | Assert.assertNull(cache.get(key));
51 | }
52 |
53 | @Test
54 | public void testShouldRemoveEntriesByType() throws Exception {
55 | final MapCache cache = new MapCache();
56 | final Cache.Key key = new Cache.Key(1, "test");
57 | final Cache.Key key1 = new Cache.Key(1, "test1");
58 | final Cache.Key key2 = new Cache.Key(1, "test2");
59 | final Cache.Key key3 = new Cache.Key(2, "test2");
60 |
61 | cache.put(key, newEntry());
62 | cache.put(key1, newEntry());
63 | cache.put(key2, newEntry());
64 | cache.put(key3, newEntry());
65 |
66 | Assert.assertNotNull(cache.get(key));
67 | Assert.assertNotNull(cache.get(key1));
68 | Assert.assertNotNull(cache.get(key2));
69 | Assert.assertNotNull(cache.get(key3));
70 |
71 | cache.removeAll(1);
72 | Assert.assertNull(cache.get(key));
73 | Assert.assertNull(cache.get(key1));
74 | Assert.assertNull(cache.get(key2));
75 | Assert.assertNotNull(cache.get(key3));
76 | }
77 | }
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/PurchaseRequestTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import com.android.vending.billing.InAppBillingServiceImpl;
26 | import com.android.vending.billing.InAppBillingService;
27 |
28 | import org.junit.Test;
29 |
30 | import android.os.Bundle;
31 |
32 | import static org.mockito.ArgumentMatchers.eq;
33 | import static org.mockito.Mockito.mock;
34 | import static org.mockito.Mockito.verify;
35 |
36 | public class PurchaseRequestTest extends RequestTestBase {
37 |
38 | @Override
39 | protected Request newRequest() {
40 | return new PurchaseRequest("test", "sku", null);
41 | }
42 |
43 | @Test
44 | public void testShouldUseExtraParams() throws Exception {
45 | final Bundle extraParams = new Bundle();
46 | extraParams.putString("extra", "test");
47 | final PurchaseRequest request = new PurchaseRequest("product", "sku", "payload", extraParams);
48 | final InAppBillingService service = mock(InAppBillingService.class);
49 |
50 | request.start(service, "package");
51 |
52 | verify(service).getBuyIntentExtraParams(eq(Billing.V6), eq("package"), eq("sku"), eq("product"), eq("payload"), eq(extraParams));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/RobotmediaInventoryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import org.junit.Before;
26 | import org.robolectric.RuntimeEnvironment;
27 |
28 | import java.util.List;
29 |
30 | import javax.annotation.Nonnull;
31 |
32 | import static org.solovyev.android.checkout.Tests.sameThreadExecutor;
33 |
34 | public class RobotmediaInventoryTest extends InventoryTestBase {
35 |
36 | @Nonnull
37 | private BillingDB mDb;
38 |
39 | static void insertPurchases(@Nonnull BillingDB db, @Nonnull List purchases) {
40 | for (Purchase purchase : purchases) {
41 | db.insert(toTransaction(purchase));
42 | }
43 | }
44 |
45 | @Nonnull
46 | private static Transaction toTransaction(@Nonnull Purchase purchase) {
47 | Transaction.PurchaseState[] states = Transaction.PurchaseState.values();
48 | Transaction.PurchaseState state = states[purchase.state.id];
49 | return new Transaction(purchase.orderId, purchase.sku, purchase.packageName, state, null, purchase.time, purchase.payload);
50 | }
51 |
52 | @Override
53 | protected void insertSkus(@Nonnull String product, @Nonnull List skus) throws Exception {
54 | }
55 |
56 | @Override
57 | @Before
58 | public void setUp() throws Exception {
59 | mDb = new BillingDB(RuntimeEnvironment.application);
60 | super.setUp();
61 | }
62 |
63 | @Nonnull
64 | @Override
65 | protected Billing newBilling() {
66 | final Billing billing = super.newBilling();
67 | billing.setMainThread(sameThreadExecutor());
68 | return billing;
69 | }
70 |
71 | @Nonnull
72 | @Override
73 | protected Inventory newInventory(@Nonnull Checkout checkout) {
74 | return new RobotmediaInventory(checkout, sameThreadExecutor());
75 | }
76 |
77 | @Override
78 | protected boolean shouldVerifyPurchaseCompletely() {
79 | return false;
80 | }
81 |
82 | @Override
83 | protected void insertPurchases(@Nonnull String product, @Nonnull List purchases) throws Exception {
84 | insertPurchases(mDb, purchases);
85 | }
86 | }
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/SkusTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import org.junit.Before;
26 | import org.junit.Test;
27 | import org.junit.runner.RunWith;
28 | import org.robolectric.RobolectricTestRunner;
29 | import org.robolectric.annotation.Config;
30 |
31 | import android.os.Bundle;
32 |
33 | import java.util.ArrayList;
34 |
35 | import javax.annotation.Nonnull;
36 |
37 | import static java.util.Arrays.asList;
38 | import static org.junit.Assert.assertEquals;
39 | import static org.junit.Assert.assertFalse;
40 | import static org.junit.Assert.assertNotNull;
41 | import static org.junit.Assert.assertNull;
42 | import static org.junit.Assert.assertTrue;
43 |
44 | @RunWith(RobolectricTestRunner.class)
45 | @Config(manifest = Config.NONE)
46 | public class SkusTest {
47 |
48 | @Nonnull
49 | private Skus mSkus;
50 |
51 | @Before
52 | public void setUp() throws Exception {
53 | mSkus = new Skus("test", asList(newSku("1"), newSku("2"), newSku("3")));
54 | }
55 |
56 | @Test
57 | public void testShouldReturnSkuById() throws Exception {
58 | assertNotNull(mSkus.getSku("2"));
59 | }
60 |
61 | @Test
62 | public void testShouldNotReturnSkuById() throws Exception {
63 | assertNull(mSkus.getSku("4"));
64 | }
65 |
66 | @Test
67 | public void testShouldHaveSku() throws Exception {
68 | assertTrue(mSkus.hasSku("2"));
69 | }
70 |
71 | @Test
72 | public void testShouldNotHaveSku() throws Exception {
73 | assertFalse(mSkus.hasSku("4"));
74 | }
75 |
76 | @Test
77 | public void testShouldReadNullList() throws Exception {
78 | final Skus skus = Skus.fromBundle(new Bundle(), "test");
79 | assertTrue(skus.list.isEmpty());
80 | }
81 |
82 | @Test
83 | public void testShouldReadEmptyList() throws Exception {
84 | final Bundle bundle = new Bundle();
85 | bundle.putStringArrayList(Skus.BUNDLE_LIST, new ArrayList());
86 | final Skus skus = Skus.fromBundle(bundle, "test");
87 | assertTrue(skus.list.isEmpty());
88 | }
89 |
90 | @Test
91 | public void testShouldHaveCorrectProduct() throws Exception {
92 | final Skus skus = Skus.fromBundle(new Bundle(), "test");
93 | assertEquals("test", skus.product);
94 | }
95 |
96 | @Test
97 | public void testShouldReadSkus() throws Exception {
98 | final ArrayList list = new ArrayList();
99 | list.add(SkuTest.newInAppJson("1"));
100 | list.add(SkuTest.newInAppJson("2"));
101 | list.add(SkuTest.newSubscriptionJson("3"));
102 | list.add(SkuTest.newSubscriptionJson("4"));
103 |
104 | final Bundle bundle = new Bundle();
105 | bundle.putStringArrayList(Skus.BUNDLE_LIST, list);
106 | final Skus skus = Skus.fromBundle(bundle, "dummy");
107 | assertEquals(4, skus.list.size());
108 | SkuTest.verifySku(skus.list.get(0), "1");
109 | SkuTest.verifySku(skus.list.get(3), "4");
110 | }
111 |
112 | private Sku newSku(String id) {
113 | return new Sku("test", id, id, Sku.Price.EMPTY, id, id, id, Sku.Price.EMPTY, id, id, id, 0);
114 | }
115 | }
--------------------------------------------------------------------------------
/lib/src/test/java/org/solovyev/android/checkout/TestServiceConnector.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 serso aka se.solovyev
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 | * Contact details
18 | *
19 | * Email: se.solovyev@gmail.com
20 | * Site: http://se.solovyev.org
21 | */
22 |
23 | package org.solovyev.android.checkout;
24 |
25 | import com.android.vending.billing.InAppBillingService;
26 |
27 | import javax.annotation.Nonnull;
28 |
29 | class TestServiceConnector implements Billing.ServiceConnector {
30 | @Nonnull
31 | public final InAppBillingService mService;
32 | @Nonnull
33 | private final Billing mBilling;
34 |
35 | public TestServiceConnector(@Nonnull Billing billing, @Nonnull InAppBillingService service) {
36 | mBilling = billing;
37 | mService = service;
38 | }
39 |
40 | @Override
41 | public boolean connect() {
42 | mBilling.setService(mService, true);
43 | return true;
44 | }
45 |
46 | @Override
47 | public void disconnect() {
48 | mBilling.setService(null, false);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':lib'
2 | include ':app'
3 |
--------------------------------------------------------------------------------