diff --git a/.travis.yml b/.travis.yml index 3dff1018..a8b0dc5a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ before_script: - adb shell input keyevent 82 & script: - - ./gradlew connectedCheck --project-prop sonatypeUsername=none --project-prop sonatypePassword=none --project-prop sonatypeRepo=none + - ./gradlew connectedCheck before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock diff --git a/library/build.gradle b/library/build.gradle index c0b6158e..2439118d 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -35,6 +35,7 @@ android { dependencies { androidTestCompile 'com.android.support.test:runner:0.5' androidTestCompile 'com.android.support.test:rules:0.5' + compile 'com.android.support:support-annotations:25.3.1' } configurations.archives.extendsFrom configurations.default @@ -69,8 +70,10 @@ uploadArchives { repositories.mavenDeployer { beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - repository(url: sonatypeRepo) { - authentication(userName: sonatypeUsername, password: sonatypePassword) + if (project.hasProperty('sonatypeRepo')) { + repository(url: sonatypeRepo) { + authentication(userName: sonatypeUsername, password: sonatypePassword) + } } pom.project { diff --git a/library/src/androidTest/java/com/anjlab/android/iab/v3/PurchaseInfoParcelableTest.java b/library/src/androidTest/java/com/anjlab/android/iab/v3/PurchaseInfoParcelableTest.java index c0bf8bdb..43b3dd97 100644 --- a/library/src/androidTest/java/com/anjlab/android/iab/v3/PurchaseInfoParcelableTest.java +++ b/library/src/androidTest/java/com/anjlab/android/iab/v3/PurchaseInfoParcelableTest.java @@ -36,7 +36,7 @@ public void testParcelable() throws Exception @Test public void testResponseDataParcelable() throws Exception { - PurchaseData responseData = purchaseInfo.parseResponseData(); + PurchaseData responseData = purchaseInfo.parseResponseDataImpl(); Parcel parcel = Parcel.obtain(); responseData.writeToParcel(parcel, 0); diff --git a/library/src/androidTest/java/com/anjlab/android/iab/v3/TransactionDetailsParcelableTest.java b/library/src/androidTest/java/com/anjlab/android/iab/v3/TransactionDetailsParcelableTest.java index 79b92d9e..ea1dea5a 100644 --- a/library/src/androidTest/java/com/anjlab/android/iab/v3/TransactionDetailsParcelableTest.java +++ b/library/src/androidTest/java/com/anjlab/android/iab/v3/TransactionDetailsParcelableTest.java @@ -24,12 +24,9 @@ public void testParcelable() throws Exception TransactionDetails result = TransactionDetails.CREATOR.createFromParcel(parcel); - assertEquals(details.productId, result.productId); - assertEquals(details.orderId, result.orderId); - assertEquals(details.purchaseToken, result.purchaseToken); - assertEquals(details.purchaseTime, result.purchaseTime); + assertEquals(details.purchaseInfo, result.purchaseInfo); - // Only check that purchase info is not null, we check it's parcel implementationin it's own tests + // Only check that purchase info is not null, we check its parcel implementation in its own tests assertNotNull(result.purchaseInfo); } } diff --git a/library/src/main/java/com/anjlab/android/iab/v3/BillingProcessor.java b/library/src/main/java/com/anjlab/android/iab/v3/BillingProcessor.java index f56f0d67..7f792958 100644 --- a/library/src/main/java/com/anjlab/android/iab/v3/BillingProcessor.java +++ b/library/src/main/java/com/anjlab/android/iab/v3/BillingProcessor.java @@ -27,6 +27,8 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; @@ -36,6 +38,7 @@ import org.json.JSONObject; import java.util.ArrayList; +import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.List; @@ -49,17 +52,26 @@ public class BillingProcessor extends BillingBase */ public interface IBillingHandler { - void onProductPurchased(String productId, TransactionDetails details); + void onProductPurchased(@NonNull String productId, @Nullable TransactionDetails details); void onPurchaseHistoryRestored(); - void onBillingError(int errorCode, Throwable error); + void onBillingError(int errorCode, @Nullable Throwable error); void onBillingInitialized(); } - private static final Date DATE_MERCHANT_LIMIT_1 = new Date(2012, 12, 5); //5th December 2012 - private static final Date DATE_MERCHANT_LIMIT_2 = new Date(2015, 7, 20); //21st July 2015 + private static final Date DATE_MERCHANT_LIMIT_1; //5th December 2012 + private static final Date DATE_MERCHANT_LIMIT_2; //21st July 2015 + + static + { + Calendar calendar = Calendar.getInstance(); + calendar.set(2012, Calendar.DECEMBER, 5); + DATE_MERCHANT_LIMIT_1 = calendar.getTime(); + calendar.set(2015, Calendar.JULY, 21); + DATE_MERCHANT_LIMIT_2 = calendar.getTime(); + } private static final int PURCHASE_FLOW_REQUEST_CODE = 32459; private static final String LOG_TAG = "iabv3"; @@ -285,8 +297,8 @@ private boolean loadPurchasesByType(String type, BillingCache cacheStorage) } } } + return true; } - return true; } catch (Exception e) { @@ -296,10 +308,13 @@ private boolean loadPurchasesByType(String type, BillingCache cacheStorage) return false; } + /** + * Attempt to fetch purchases from the server and update our cache if successful + * @return {@code true} if all retrievals are successful, {@code false} otherwise + */ public boolean loadOwnedPurchasesFromGoogle() { - return isInitialized() && - loadPurchasesByType(Constants.PRODUCT_TYPE_MANAGED, cachedProducts) && + return loadPurchasesByType(Constants.PRODUCT_TYPE_MANAGED, cachedProducts) && loadPurchasesByType(Constants.PRODUCT_TYPE_SUBSCRIPTION, cachedSubscriptions); } @@ -328,7 +343,7 @@ public boolean subscribe(Activity activity, String productId, String developerPa * * @param activity the activity calling this method * @param productId the product id to purchase - * @param extraParamsBundle A bundle object containing extra parameters to pass to + * @param extraParams A bundle object containing extra parameters to pass to * getBuyIntentExtraParams() * @see extra * params documentation on developer.android.com @@ -352,7 +367,7 @@ public boolean purchase(Activity activity, String productId, String developerPay * * @param activity the activity calling this method * @param productId the product id to purchase - * @param extraParamsBundle A bundle object containing extra parameters to pass to getBuyIntentExtraParams() + * @param extraParams A bundle object containing extra parameters to pass to getBuyIntentExtraParams() * @see extra * params documentation on developer.android.com * @return {@code false} if the billing system is not initialized, {@code productId} is empty or if an exception occurs. @@ -612,11 +627,6 @@ private boolean purchase(Activity activity, List oldProductIds, String p } else // API v7+ supported { - if (extraParamsBundle == null) - { - extraParamsBundle = new Bundle(); - } - if (!extraParamsBundle.containsKey(Constants.EXTRA_PARAMS_KEY_SKU_TO_REPLACE)) { extraParamsBundle.putStringArrayList(Constants.EXTRA_PARAMS_KEY_SKU_TO_REPLACE, @@ -713,41 +723,35 @@ private boolean checkMerchant(TransactionDetails details) { return true; } - if (details.purchaseTime.before(DATE_MERCHANT_LIMIT_1)) //new format [merchantId].[orderId] applied or not? + if (details.purchaseInfo.purchaseData.purchaseTime.before(DATE_MERCHANT_LIMIT_1)) //newest format applied { return true; } - if (details.purchaseTime.after(DATE_MERCHANT_LIMIT_2)) //newest format applied + if (details.purchaseInfo.purchaseData.purchaseTime.after(DATE_MERCHANT_LIMIT_2)) //newest format applied { return true; } - if (details.orderId == null || details.orderId.trim().length() == 0) + if (details.purchaseInfo.purchaseData.orderId == null || details.purchaseInfo.purchaseData.orderId.trim().length() == 0) { return false; } - int index = details.orderId.indexOf('.'); + int index = details.purchaseInfo.purchaseData.orderId.indexOf('.'); if (index <= 0) { return false; //protect on missing merchant id } //extract merchant id - String merchantId = details.orderId.substring(0, index); + String merchantId = details.purchaseInfo.purchaseData.orderId.substring(0, index); return merchantId.compareTo(developerMerchantId) == 0; } + @Nullable private TransactionDetails getPurchaseTransactionDetails(String productId, BillingCache cache) { PurchaseInfo details = cache.getDetails(productId); if (details != null && !TextUtils.isEmpty(details.responseData)) { - try - { - return new TransactionDetails(details); - } - catch (JSONException e) - { - Log.e(LOG_TAG, "Failed to load saved purchase details for " + productId, e); - } + return new TransactionDetails(details); } return null; } @@ -865,11 +869,13 @@ public List getSubscriptionListingDetails(ArrayList productI return getSkuDetails(productIdList, Constants.PRODUCT_TYPE_SUBSCRIPTION); } + @Nullable public TransactionDetails getPurchaseTransactionDetails(String productId) { return getPurchaseTransactionDetails(productId, cachedProducts); } + @Nullable public TransactionDetails getSubscriptionTransactionDetails(String productId) { return getPurchaseTransactionDetails(productId, cachedSubscriptions); diff --git a/library/src/main/java/com/anjlab/android/iab/v3/PurchaseInfo.java b/library/src/main/java/com/anjlab/android/iab/v3/PurchaseInfo.java index ef1aa04e..56a06471 100644 --- a/library/src/main/java/com/anjlab/android/iab/v3/PurchaseInfo.java +++ b/library/src/main/java/com/anjlab/android/iab/v3/PurchaseInfo.java @@ -44,14 +44,19 @@ public PurchaseInfo(String responseData, String signature) { this.responseData = responseData; this.signature = signature; - this.purchaseData = parseResponseData(); + this.purchaseData = parseResponseDataImpl(); } /** - * @deprecated dont call it directly, use {@see purchaseData}} instead. + * @deprecated don't call it directly, use {@see purchaseData} instead. */ @Deprecated public PurchaseData parseResponseData() + { + return parseResponseDataImpl(); + } + + PurchaseData parseResponseDataImpl() { try { @@ -92,7 +97,7 @@ protected PurchaseInfo(Parcel in) { this.responseData = in.readString(); this.signature = in.readString(); - this.purchaseData = parseResponseData(); + this.purchaseData = parseResponseDataImpl(); } public static final Parcelable.Creator CREATOR = @@ -108,4 +113,22 @@ public PurchaseInfo[] newArray(int size) return new PurchaseInfo[size]; } }; + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (o == null || !(o instanceof PurchaseInfo)) + { + return false; + } + PurchaseInfo other = (PurchaseInfo) o; + return responseData.equals(other.responseData) + && signature.equals(other.signature) + && purchaseData.purchaseToken.equals(other.purchaseData.purchaseToken) + && purchaseData.purchaseTime.equals(other.purchaseData.purchaseTime); + } } diff --git a/library/src/main/java/com/anjlab/android/iab/v3/SkuDetails.java b/library/src/main/java/com/anjlab/android/iab/v3/SkuDetails.java index 11c10055..5d5d1a3b 100644 --- a/library/src/main/java/com/anjlab/android/iab/v3/SkuDetails.java +++ b/library/src/main/java/com/anjlab/android/iab/v3/SkuDetails.java @@ -18,6 +18,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; + import org.json.JSONException; import org.json.JSONObject; diff --git a/library/src/main/java/com/anjlab/android/iab/v3/TransactionDetails.java b/library/src/main/java/com/anjlab/android/iab/v3/TransactionDetails.java index 98a35a53..8287e111 100644 --- a/library/src/main/java/com/anjlab/android/iab/v3/TransactionDetails.java +++ b/library/src/main/java/com/anjlab/android/iab/v3/TransactionDetails.java @@ -18,8 +18,6 @@ import android.os.Parcel; import android.os.Parcelable; -import org.json.JSONException; - import java.util.Date; import java.util.Locale; @@ -52,7 +50,7 @@ public class TransactionDetails implements Parcelable public final PurchaseInfo purchaseInfo; - public TransactionDetails(PurchaseInfo info) throws JSONException + public TransactionDetails(PurchaseInfo info) { purchaseInfo = info; productId = purchaseInfo.purchaseData.productId; diff --git a/sample/res/drawable/ic_launcher.png b/sample/res/drawable-anydpi/ic_launcher.png similarity index 100% rename from sample/res/drawable/ic_launcher.png rename to sample/res/drawable-anydpi/ic_launcher.png diff --git a/sample/res/values/strings.xml b/sample/res/values/strings.xml index 5d084e81..a15dd893 100644 --- a/sample/res/values/strings.xml +++ b/sample/res/values/strings.xml @@ -2,7 +2,7 @@ In-app Billing v3 Sample - wait... + wait… Sample Activity #%d diff --git a/sample/src/com/anjlab/android/iab/v3/sample2/MainActivity.java b/sample/src/com/anjlab/android/iab/v3/sample2/MainActivity.java index 36ca2127..2054627a 100644 --- a/sample/src/com/anjlab/android/iab/v3/sample2/MainActivity.java +++ b/sample/src/com/anjlab/android/iab/v3/sample2/MainActivity.java @@ -18,6 +18,8 @@ import android.app.Activity; import android.content.Intent; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.Log; import android.view.View; import android.widget.TextView; @@ -58,12 +60,12 @@ protected void onCreate(Bundle savedInstanceState) { bp = new BillingProcessor(this, LICENSE_KEY, MERCHANT_ID, new BillingProcessor.IBillingHandler() { @Override - public void onProductPurchased(String productId, TransactionDetails details) { + public void onProductPurchased(@NonNull String productId, @Nullable TransactionDetails details) { showToast("onProductPurchased: " + productId); updateTextViews(); } @Override - public void onBillingError(int errorCode, Throwable error) { + public void onBillingError(int errorCode, @Nullable Throwable error) { showToast("onBillingError: " + Integer.toString(errorCode)); } @Override