From 1c2d495fca6016a2f0bc90b2c1504ea021949cdc Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Wed, 2 Jul 2025 12:27:39 +0200 Subject: [PATCH] Update billing library to 7.1.1 --- app/build.gradle | 2 +- .../emanuelef/remote_capture/PlayBilling.java | 84 ++++++++++++------- .../remote_capture/adapters/SKUsAdapter.java | 14 ++-- .../model/SkusAvailability.java | 12 ++- 4 files changed, 69 insertions(+), 43 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 4e55c32d..64379a82 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -130,5 +130,5 @@ dependencies { implementation 'com.github.AppIntro:AppIntro:6.2.0' //implementation 'com.github.androidmads:QRGenerator:1.0.1' - implementation "com.android.billingclient:billing:6.1.0" + implementation "com.android.billingclient:billing:7.1.1" } diff --git a/app/src/main/java/com/emanuelef/remote_capture/PlayBilling.java b/app/src/main/java/com/emanuelef/remote_capture/PlayBilling.java index de0c4382..154c4dbc 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/PlayBilling.java +++ b/app/src/main/java/com/emanuelef/remote_capture/PlayBilling.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2020-22 - Emanuele Faranda + * Copyright 2020-25 - Emanuele Faranda */ package com.emanuelef.remote_capture; @@ -41,14 +41,18 @@ import com.android.billingclient.api.BillingClient; import com.android.billingclient.api.BillingClient.BillingResponseCode; import com.android.billingclient.api.BillingClientStateListener; import com.android.billingclient.api.BillingFlowParams; +import com.android.billingclient.api.BillingFlowParams.ProductDetailsParams; import com.android.billingclient.api.BillingResult; import com.android.billingclient.api.ConsumeParams; +import com.android.billingclient.api.PendingPurchasesParams; +import com.android.billingclient.api.ProductDetails; +import com.android.billingclient.api.ProductDetailsResponseListener; import com.android.billingclient.api.Purchase; import com.android.billingclient.api.Purchase.PurchaseState; import com.android.billingclient.api.PurchasesUpdatedListener; -import com.android.billingclient.api.SkuDetails; -import com.android.billingclient.api.SkuDetailsParams; -import com.android.billingclient.api.SkuDetailsResponseListener; +import com.android.billingclient.api.QueryProductDetailsParams; +import com.android.billingclient.api.QueryProductDetailsParams.Product; +import com.android.billingclient.api.QueryPurchasesParams; import com.emanuelef.remote_capture.model.SkusAvailability; import java.io.BufferedOutputStream; @@ -59,16 +63,17 @@ import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -public class PlayBilling extends Billing implements BillingClientStateListener, PurchasesUpdatedListener, SkuDetailsResponseListener { +public class PlayBilling extends Billing implements BillingClientStateListener, PurchasesUpdatedListener, ProductDetailsResponseListener { public static final String TAG = "PlayBilling"; private static final String PREF_LAST_UNLOCK_TOKEN = "unlock_token"; private static final String LICENSE_GEN_URL = "https://pcapdroid.org/getlicense"; private final Handler mHandler; - private final ArrayMap mDetails; + private final ArrayMap mDetails; private final ArrayMap mSkuToPurchToken; private BillingClient mBillingClient; private PurchaseReadyListener mListener; @@ -112,12 +117,12 @@ public class PlayBilling extends Billing implements BillingClientStateListener, } public static String purchstate2Str(int state) { - switch (state) { - case PurchaseState.PENDING: return "PENDING"; - case PurchaseState.PURCHASED: return "PURCHASED"; - case PurchaseState.UNSPECIFIED_STATE: return "UNSPECIFIED"; - } - return "UNKNOWN"; + return switch (state) { + case PurchaseState.PENDING -> "PENDING"; + case PurchaseState.PURCHASED -> "PURCHASED"; + case PurchaseState.UNSPECIFIED_STATE -> "UNSPECIFIED"; + default -> "UNKNOWN"; + }; } private void processPurchases(BillingResult billingResult, @Nullable List purchases) { @@ -129,7 +134,7 @@ public class PlayBilling extends Billing implements BillingClientStateListener, for(Purchase purchase : purchases) { boolean newPurchase = false; - for(String sku: purchase.getSkus()) { + for(String sku: purchase.getProducts()) { Log.d(TAG, "\tPurchase: " + sku + " -> " + purchstate2Str(purchase.getPurchaseState())); switch (purchase.getPurchaseState()) { @@ -234,20 +239,22 @@ public class PlayBilling extends Billing implements BillingClientStateListener, } @Override - public void onSkuDetailsResponse(@NonNull BillingResult billingResult, @Nullable List list) { - Log.d(TAG, "onSkuDetailsResponse: " + billingResult.getResponseCode() + " " + billingResult.getDebugMessage()); + public void onProductDetailsResponse(@NonNull BillingResult billingResult, @NonNull List list) { + Log.d(TAG, "onProductDetailsResponse: " + billingResult.getResponseCode() + " " + billingResult.getDebugMessage()); - if((billingResult.getResponseCode() == BillingResponseCode.OK) && (list != null)) { + if(billingResult.getResponseCode() == BillingResponseCode.OK) { mAvailability.update(list, mPrefs); Log.d(TAG, "Num available SKUs: " + list.size()); mDetails.clear(); - for(SkuDetails sku: list) { - //Log.d(TAG, "Available: " + sku); - mDetails.put(sku.getSku(), sku); + for(ProductDetails product: list) { + //Log.d(TAG, "Available: " + product); + mDetails.put(product.getProductId(), product); } - mBillingClient.queryPurchasesAsync(BillingClient.SkuType.INAPP, (billingResult1, purchases) -> { + mBillingClient.queryPurchasesAsync( + QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(), + (billingResult1, purchases) -> { Log.d(TAG, "queryPurchasesAsync: " + billingResult1.getResponseCode() + " " + billingResult1.getDebugMessage()); processPurchases(billingResult1, purchases); }); @@ -260,11 +267,20 @@ public class PlayBilling extends Billing implements BillingClientStateListener, Log.d(TAG, "onBillingSetupFinished: " + billingResult.getResponseCode() + " " + billingResult.getDebugMessage()); if(billingResult.getResponseCode() == BillingResponseCode.OK) { - SkuDetailsParams.Builder builder = SkuDetailsParams.newBuilder() - .setType(BillingClient.SkuType.INAPP) - .setSkusList(ALL_SKUS); + List productList = new ArrayList<>(); + for (String sku: ALL_SKUS) { + productList.add( + Product.newBuilder() + .setProductId(sku) + .setProductType(BillingClient.ProductType.INAPP) + .build()); + } - mBillingClient.querySkuDetailsAsync(builder.build(), this); + QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder() + .setProductList(productList) + .build(); + + mBillingClient.queryProductDetailsAsync(params, this); } else onPurchasesError(billingResult); } @@ -285,7 +301,7 @@ public class PlayBilling extends Billing implements BillingClientStateListener, } /* - * connectBilling -> onBillingSetupFinished -> querySkuDetailsAsync -> queryPurchasesAsync -> processPurchases + * connectBilling -> onBillingSetupFinished -> queryProductDetailsAsync -> queryPurchasesAsync -> processPurchases * Starts the connection to Google Play. * IMPORTANT: the client must call disconnectBilling to prevent leaks * */ @@ -301,7 +317,8 @@ public class PlayBilling extends Billing implements BillingClientStateListener, mBillingClient = BillingClient.newBuilder(mContext) .setListener(this) - .enablePendingPurchases() + .enablePendingPurchases(PendingPurchasesParams.newBuilder() + .enableOneTimeProducts().build()) .build(); // Will call onBillingSetupFinished when ready @@ -399,8 +416,8 @@ public class PlayBilling extends Billing implements BillingClientStateListener, } @Nullable - public SkuDetails getSkuDetails(String sku) { - return mDetails.get(sku); + public ProductDetails getProductDetails(String productId) { + return mDetails.get(productId); } public boolean purchase(Activity activity, String sku) { @@ -409,7 +426,7 @@ public class PlayBilling extends Billing implements BillingClientStateListener, return false; } - SkuDetails details = mDetails.get(sku); + ProductDetails details = mDetails.get(sku); if(details == null) { mHandler.post(() -> Utils.showToast(mContext, R.string.feature_not_available)); return false; @@ -417,8 +434,15 @@ public class PlayBilling extends Billing implements BillingClientStateListener, Log.d(TAG, "Starting purchasing SKU " + sku); + List productDetailsParamsList = new ArrayList<>(); + productDetailsParamsList.add( + ProductDetailsParams.newBuilder() + .setProductDetails(details) + .build() + ); + BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() - .setSkuDetails(details) + .setProductDetailsParamsList(productDetailsParamsList) .build(); // will call onPurchasesUpdated when done diff --git a/app/src/main/java/com/emanuelef/remote_capture/adapters/SKUsAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/adapters/SKUsAdapter.java index eb6fccfb..07e63ca2 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/adapters/SKUsAdapter.java +++ b/app/src/main/java/com/emanuelef/remote_capture/adapters/SKUsAdapter.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2022 - Emanuele Faranda + * Copyright 2022-25 - Emanuele Faranda */ package com.emanuelef.remote_capture.adapters; @@ -31,7 +31,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; -import com.android.billingclient.api.SkuDetails; +import com.android.billingclient.api.ProductDetails; import com.emanuelef.remote_capture.Billing; import com.emanuelef.remote_capture.PlayBilling; import com.emanuelef.remote_capture.R; @@ -125,16 +125,20 @@ public class SKUsAdapter extends ArrayAdapter { if(!mIab.isAvailable(sku)) return; - SkuDetails sd = mIab.getSkuDetails(sku); + ProductDetails sd = mIab.getProductDetails(sku); if(sd == null) return; - Log.d(TAG, "SKU [" + sd.getSku() + "]: " + sd.getTitle() + " -> " + sd.getPrice() + " " + sd.getPriceCurrencyCode()); + ProductDetails.OneTimePurchaseOfferDetails details = sd.getOneTimePurchaseOfferDetails(); + if (details == null) + return; + + Log.d(TAG, "SKU [" + sd.getProductId() + "]: " + sd.getTitle() + " -> " + details.getFormattedPrice()); add(new SKUItem(sku, mCtx.getString(title), (descr > 0) ? mCtx.getString(descr) : "", - sd.getPrice(), + details.getFormattedPrice(), docs_url)); } diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/SkusAvailability.java b/app/src/main/java/com/emanuelef/remote_capture/model/SkusAvailability.java index 61689eb3..b0c12ab3 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/SkusAvailability.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/SkusAvailability.java @@ -14,16 +14,14 @@ * You should have received a copy of the GNU General Public License * along with PCAPdroid. If not, see . * - * Copyright 2020-21 - Emanuele Faranda + * Copyright 2020-25 - Emanuele Faranda */ package com.emanuelef.remote_capture.model; import android.content.SharedPreferences; -import androidx.collection.ArraySet; - -import com.android.billingclient.api.SkuDetails; +import com.android.billingclient.api.ProductDetails; import com.emanuelef.remote_capture.Log; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; @@ -67,13 +65,13 @@ public class SkusAvailability implements Serializable { prefsEditor.apply(); } - public boolean update(List details, SharedPreferences prefs) { + public boolean update(List details, SharedPreferences prefs) { boolean changed = false; HashSet available = new HashSet<>(); // Check new skus - for(SkuDetails detail: details) { - String sku = detail.getSku(); + for(ProductDetails detail: details) { + String sku = detail.getProductId(); available.add(sku); if(!mSkus.contains(sku)) {