From 79350e675b620e454be6d999275dee6be3744486 Mon Sep 17 00:00:00 2001 From: Johan Stuyts Date: Fri, 20 Dec 2024 16:16:58 +0100 Subject: [PATCH] #265: Added reconnection with billing service if initial connection fails, or the connection is lost later on. The behavior is as follows: * If multiple attempts are needed to create the initial connection, `handleInstallError(...)` of the observer can be invoked multiple times before other methods are invoked. * If the connection is lost, the observer is not notified of the connection loss, nor of a re-establishment of the connection. After a successful reconnection, the offer details are not fetched again. --- .../PurchaseManagerGoogleBilling.java | 62 ++++++++++++++----- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/gdx-pay-android-googlebilling/src/com/badlogic/gdx/pay/android/googlebilling/PurchaseManagerGoogleBilling.java b/gdx-pay-android-googlebilling/src/com/badlogic/gdx/pay/android/googlebilling/PurchaseManagerGoogleBilling.java index b125020..b9dcb87 100644 --- a/gdx-pay-android-googlebilling/src/com/badlogic/gdx/pay/android/googlebilling/PurchaseManagerGoogleBilling.java +++ b/gdx-pay-android-googlebilling/src/com/badlogic/gdx/pay/android/googlebilling/PurchaseManagerGoogleBilling.java @@ -1,6 +1,8 @@ package com.badlogic.gdx.pay.android.googlebilling; import android.app.Activity; +import android.os.Handler; +import android.os.Looper; import com.android.billingclient.api.*; import com.android.billingclient.api.BillingClient.ProductType; import com.badlogic.gdx.Gdx; @@ -16,11 +18,18 @@ *

* Reference docs *

+ * Exponential back-off copied from (a clone of) an + * Android + * sample. + *

* Created by Benjamin Schulte on 07.07.2018. */ public class PurchaseManagerGoogleBilling implements PurchaseManager, PurchasesUpdatedListener { private static final String TAG = "GdxPay/GoogleBilling"; + private static final long RECONNECT_TIMER_START_MILLISECONDS = 1000L; + private static final long RECONNECT_TIMER_MAX_TIME_MILLISECONDS = 1000L * 60L * 15L; // 15 mins + private static final Handler handler = new Handler(Looper.getMainLooper()); private final Map informationMap = new ConcurrentHashMap<>(); private final Activity activity; @@ -30,6 +39,8 @@ public class PurchaseManagerGoogleBilling implements PurchaseManager, PurchasesU private BillingClient mBillingClient; private PurchaseObserver observer; private PurchaseManagerConfig config; + private boolean hasBillingSetupFinishedSuccessfully = false; + private long reconnectMilliseconds = RECONNECT_TIMER_START_MILLISECONDS; public PurchaseManagerGoogleBilling(Activity activity) { this.activity = activity; @@ -58,23 +69,22 @@ public void install(PurchaseObserver observer, PurchaseManagerConfig config, boo // make sure to call the observer again installationComplete = false; - startServiceConnection(new Runnable() { - @Override - public void run() { - // it might happen that this was already disposed until the service connection was established - if (PurchaseManagerGoogleBilling.this.observer == null) - return; + startServiceConnection(this::handleBillingSetupFinished); + } - if (!serviceConnected) - PurchaseManagerGoogleBilling.this.observer.handleInstallError( - new GdxPayException("Connection to Play Billing not possible")); - else - fetchOfferDetails(); - } - }); + private void handleBillingSetupFinished() { + // it might happen that this was already disposed until the service connection was established + if (PurchaseManagerGoogleBilling.this.observer == null) + return; + + if (!serviceConnected) + PurchaseManagerGoogleBilling.this.observer.handleInstallError( + new GdxPayException("Connection to Play Billing not possible")); + else + fetchOfferDetails(); } - private void startServiceConnection(final Runnable excecuteOnSetupFinished) { + private void startServiceConnection(final Runnable executeOnSetupFinished) { mBillingClient.startConnection(new BillingClientStateListener() { @Override public void onBillingSetupFinished(@Nonnull BillingResult result) { @@ -83,19 +93,39 @@ public void onBillingSetupFinished(@Nonnull BillingResult result) { Gdx.app.debug(TAG, "Setup finished. Response code: " + billingResponseCode); serviceConnected = (billingResponseCode == BillingClient.BillingResponseCode.OK); + if (serviceConnected) { + reconnectMilliseconds = RECONNECT_TIMER_START_MILLISECONDS; + hasBillingSetupFinishedSuccessfully = true; + } else { + reconnectWithService(); + } - if (excecuteOnSetupFinished != null) { - excecuteOnSetupFinished.run(); + if (executeOnSetupFinished != null) { + executeOnSetupFinished.run(); } } @Override public void onBillingServiceDisconnected() { serviceConnected = false; + reconnectWithService(); } }); } + private void reconnectWithService() { + handler.postDelayed(new Runnable() { + @Override + public void run() { + Runnable executeOnSetupFinished = hasBillingSetupFinishedSuccessfully + ? null + : PurchaseManagerGoogleBilling.this::handleBillingSetupFinished; + startServiceConnection(executeOnSetupFinished); + } + }, reconnectMilliseconds); + reconnectMilliseconds = Math.min(reconnectMilliseconds * 2, RECONNECT_TIMER_MAX_TIME_MILLISECONDS); + } + private void fetchOfferDetails() { Gdx.app.debug(TAG,"Called fetchOfferDetails()"); productDetailsMap.clear();