mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-24 01:54:22 +00:00
Android IAP: Ensure that IAP requests will still succeed even if the IAP service did not yet bind to our app
This commit is contained in:
parent
38f9e951bb
commit
0413331cc3
1 changed files with 48 additions and 51 deletions
|
|
@ -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));
|
||||
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());
|
||||
|
||||
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…
Add table
Add a link
Reference in a new issue