Browse Source

Android IAP: Ensure that IAP requests will still succeed even if the IAP service did not yet bind to our app

tags/2021-05-28
hogliux 8 years ago
parent
commit
0413331cc3
1 changed files with 48 additions and 51 deletions
  1. +48
    -51
      modules/juce_product_unlocking/native/juce_android_InAppPurchases.cpp

+ 48
- 51
modules/juce_product_unlocking/native/juce_android_InAppPurchases.cpp View File

@@ -139,15 +139,10 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
serviceConnection = GlobalRef (CreateJavaInterface (this, "android/content/ServiceConnection").get());
android.activity.callBooleanMethod (JuceAppActivity.bindService, intent,
serviceConnection.get(), 1 /*BIND_AUTO_CREATE*/);
if (threadPool == nullptr)
threadPool = new ThreadPool (1);
}
~Pimpl()
{
threadPool = nullptr;
if (serviceConnection != nullptr)
{
android.activity.callVoidMethod (JuceAppActivity.unbindService, serviceConnection.get());
@@ -162,6 +157,9 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
void getProductsInformation (const StringArray& productIdentifiers)
{
if (! checkIsReady())
return;
auto callback = [this](const Array<InAppPurchases::Product>& products)
{
const ScopedLock lock (getProductsInformationJobResultsLock);
@@ -169,13 +167,16 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
triggerAsyncUpdate();
};
threadPool->addJob (new GetProductsInformationJob (*this, getPackageName(),
threadPool->addJob (new GetProductsInformationJob (inAppBillingService, getPackageName(),
productIdentifiers, callback), true);
}
void purchaseProduct (const String& productIdentifier, bool isSubscription,
const StringArray& subscriptionIdentifiers, bool creditForUnusedSubscription)
{
if (! checkIsReady())
return;
// Upgrading/downgrading only makes sense for subscriptions!
jassert (subscriptionIdentifiers.isEmpty() || isSubscription);
@@ -205,6 +206,9 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
void restoreProductsBoughtList (bool, const juce::String&)
{
if (! checkIsReady())
return;
auto callback = [this](const Array<InAppPurchases::Listener::PurchaseInfo>& purchases)
{
const ScopedLock lock (getProductsBoughtJobResultsLock);
@@ -212,12 +216,15 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
triggerAsyncUpdate();
};
threadPool->addJob (new GetProductsBoughtJob (*this,
threadPool->addJob (new GetProductsBoughtJob (inAppBillingService,
getPackageName(), callback), true);
}
void consumePurchase (const String& productIdentifier, const String& purchaseToken)
{
if (! checkIsReady())
return;
auto callback = [this](const ConsumePurchaseJob::Result& r)
{
const ScopedLock lock (consumePurchaseJobResultsLock);
@@ -225,7 +232,7 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
triggerAsyncUpdate();
};
threadPool->addJob (new ConsumePurchaseJob (*this, getPackageName(), productIdentifier,
threadPool->addJob (new ConsumePurchaseJob (inAppBillingService, getPackageName(), productIdentifier,
purchaseToken, callback), true);
}
@@ -314,10 +321,6 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
//==============================================================================
bool checkIsReady()
{
// It may take a few seconds for the in-app purchase service to connect
for (auto retries = 0; retries < 10 && inAppBillingService.get() == 0; ++retries)
Thread::sleep (500);
return (inAppBillingService.get() != 0);
}
@@ -344,8 +347,6 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
// Connecting to the in-app purchase server failed! This could have multiple reasons:
// 1) Your phone/emulator must support the google play store
// 2) Your phone must be logged into the google play store and be able to receive updates
// 3) It can take a few seconds after instantiation of the InAppPurchase class for
// in-app purchases to be avaialable on Android.
return false;
}
@@ -359,7 +360,12 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
iBinder));
if (isInAppPurchasesSupported (iapService))
{
if (threadPool == nullptr)
threadPool = new ThreadPool (1);
inAppBillingService = GlobalRef (iapService);
}
// If you hit this assert, then in-app purchases is not available on your device,
// most likely due to too old version of Google Play API (hint: update Google Play on the device).
@@ -368,6 +374,7 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
void onServiceDisconnected (jobject) override
{
threadPool = nullptr;
inAppBillingService.clear();
}
@@ -382,12 +389,12 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
{
using Callback = std::function<void(const Array<InAppPurchases::Product>&)>;
GetProductsInformationJob (Pimpl& parent,
GetProductsInformationJob (const GlobalRef& inAppBillingServiceToUse,
const LocalRef<jstring>& packageNameToUse,
const StringArray& productIdentifiersToUse,
const Callback& callbackToUse)
: ThreadPoolJob ("GetProductsInformationJob"),
owner (parent),
inAppBillingService (inAppBillingServiceToUse),
packageName (packageNameToUse.get()),
productIdentifiers (productIdentifiersToUse),
callback (callbackToUse)
@@ -397,7 +404,7 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
{
jassert (callback);
if (owner.checkIsReady())
if (inAppBillingService.get() != 0)
{
// Google's Billing API limitation
auto maxQuerySize = 20;
@@ -459,7 +466,7 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
auto productTypeString = javaString (productType);
auto productDetails = LocalRef<jobject> (owner.inAppBillingService.callObjectMethod (IInAppBillingService.getSkuDetails,
auto productDetails = LocalRef<jobject> (inAppBillingService.callObjectMethod (IInAppBillingService.getSkuDetails,
3, (jstring) packageName.get(),
productTypeString.get(), querySkus.get()));
@@ -470,7 +477,7 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
{
Array<InAppPurchases::Product> products;
if (owner.checkIsReady())
if (retrievedProducts.get() != 0)
{
auto* env = getEnv();
@@ -535,8 +542,8 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
return products;
}
Pimpl& owner;
GlobalRef packageName;
GlobalRef inAppBillingService, packageName;
const StringArray productIdentifiers;
Callback callback;
};
@@ -546,11 +553,11 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
{
using Callback = std::function<void(const Array<InAppPurchases::Listener::PurchaseInfo>&)>;
GetProductsBoughtJob (Pimpl& parent,
GetProductsBoughtJob (const GlobalRef& inAppBillingServiceToUse,
const LocalRef<jstring>& packageNameToUse,
const Callback& callbackToUse)
: ThreadPoolJob ("GetProductsBoughtJob"),
owner (parent),
inAppBillingService (inAppBillingServiceToUse),
packageName (packageNameToUse.get()),
callback (callbackToUse)
{}
@@ -559,7 +566,7 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
{
jassert (callback);
if (owner.checkIsReady())
if (inAppBillingService.get() != 0)
{
auto inAppPurchases = getProductsBought ("inapp", 0);
auto subsPurchases = getProductsBought ("subs", 0);
@@ -590,7 +597,7 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
auto* env = getEnv();
auto productTypeString = javaString (productType);
auto ownedItems = LocalRef<jobject> (owner.inAppBillingService.callObjectMethod (IInAppBillingService.getPurchases, 3,
auto ownedItems = LocalRef<jobject> (inAppBillingService.callObjectMethod (IInAppBillingService.getPurchases, 3,
(jstring) packageName.get(), productTypeString.get(),
continuationToken));
@@ -648,8 +655,7 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
return purchases;
}
Pimpl& owner;
GlobalRef packageName;
GlobalRef inAppBillingService, packageName;
Callback callback;
};
@@ -666,13 +672,13 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
using Callback = std::function<void(const Result&)>;
ConsumePurchaseJob (Pimpl& parent,
ConsumePurchaseJob (const GlobalRef& inAppBillingServiceToUse,
const LocalRef<jstring>& packageNameToUse,
const String& productIdentifierToUse,
const String& purchaseTokenToUse,
const Callback& callbackToUse)
: ThreadPoolJob ("ConsumePurchaseJob"),
owner (parent),
inAppBillingService (inAppBillingServiceToUse),
packageName (packageNameToUse.get()),
productIdentifier (productIdentifierToUse),
purchaseToken (purchaseTokenToUse),
@@ -683,30 +689,22 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
{
jassert (callback);
if (owner.checkIsReady())
{
auto token = (! purchaseToken.isEmpty() ? purchaseToken : getPurchaseTokenForProductId (productIdentifier, false, 0));
if (token.isEmpty())
{
if (callback)
callback ({ productIdentifier, false, NEEDS_TRANS ("Item not owned") });
return jobHasFinished;
}
auto responseCode = owner.inAppBillingService.callIntMethod (IInAppBillingService.consumePurchase, 3,
(jstring)packageName.get(), javaString (token).get());
auto token = (! purchaseToken.isEmpty() ? purchaseToken : getPurchaseTokenForProductId (productIdentifier, false, 0));
if (callback)
callback ({ productIdentifier, responseCode == 0, statusCodeToUserString (responseCode) });
}
else
if (token.isEmpty())
{
if (callback)
callback ({{}, false, "In-App purchases unavailable"});
callback ({ productIdentifier, false, NEEDS_TRANS ("Item not owned") });
return jobHasFinished;
}
auto responseCode = inAppBillingService.callIntMethod (IInAppBillingService.consumePurchase, 3,
(jstring)packageName.get(), javaString (token).get());
if (callback)
callback ({ productIdentifier, responseCode == 0, statusCodeToUserString (responseCode) });
return jobHasFinished;
}
@@ -714,7 +712,7 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
String getPurchaseTokenForProductId (const String productIdToLookFor, bool isSubscription, jstring continuationToken)
{
auto productTypeString = javaString (isSubscription ? "subs" : "inapp");
auto ownedItems = LocalRef<jobject> (owner.inAppBillingService.callObjectMethod (IInAppBillingService.getPurchases, 3,
auto ownedItems = LocalRef<jobject> (inAppBillingService.callObjectMethod (IInAppBillingService.getPurchases, 3,
(jstring) packageName.get(), productTypeString.get(),
continuationToken));
@@ -760,8 +758,7 @@ struct InAppPurchases::Pimpl : private AsyncUpdater,
return {};
}
Pimpl& owner;
GlobalRef packageName;
GlobalRef inAppBillingService, packageName;
const String productIdentifier, purchaseToken;
Callback callback;
};


Loading…
Cancel
Save