Browse Source

Revert commit (as already applied with earlier commit): 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
3d379863f1
1 changed files with 51 additions and 48 deletions
  1. +51
    -48
      modules/juce_product_unlocking/native/juce_android_InAppPurchases.cpp

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

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


Loading…
Cancel
Save