Formatting and cleanup

This commit is contained in:
Björn Ritzl 2020-07-23 14:04:17 +02:00
parent 83876a508b
commit 8cf185056f
2 changed files with 81 additions and 65 deletions

View File

@ -37,10 +37,8 @@ import com.android.billingclient.api.SkuDetailsResponseListener;
public class IapGooglePlay implements PurchasesUpdatedListener { public class IapGooglePlay implements PurchasesUpdatedListener {
public static final String TAG = "IapGooglePlay"; public static final String TAG = "IapGooglePlay";
private Map<String, SkuDetails> products = new HashMap<String, SkuDetails>();
private BillingClient billingClient; private BillingClient billingClient;
private Map<String, SkuDetails> products;
private IPurchaseListener purchaseListener; private IPurchaseListener purchaseListener;
private boolean autoFinishTransactions; private boolean autoFinishTransactions;
private Activity activity; private Activity activity;
@ -48,7 +46,6 @@ public class IapGooglePlay implements PurchasesUpdatedListener {
public IapGooglePlay(Activity activity, boolean autoFinishTransactions) { public IapGooglePlay(Activity activity, boolean autoFinishTransactions) {
this.activity = activity; this.activity = activity;
this.autoFinishTransactions = autoFinishTransactions; this.autoFinishTransactions = autoFinishTransactions;
products = new HashMap<String, SkuDetails>();
billingClient = BillingClient.newBuilder(activity).setListener(this).enablePendingPurchases().build(); billingClient = BillingClient.newBuilder(activity).setListener(this).enablePendingPurchases().build();
billingClient.startConnection(new BillingClientStateListener() { billingClient.startConnection(new BillingClientStateListener() {
@ -56,7 +53,8 @@ public class IapGooglePlay implements PurchasesUpdatedListener {
public void onBillingSetupFinished(BillingResult billingResult) { public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingResponseCode.OK) { if (billingResult.getResponseCode() == BillingResponseCode.OK) {
Log.v(TAG, "Setup finished"); Log.v(TAG, "Setup finished");
// TODO BillingClient.queryPurchases() // NOTE: we will not query purchases here. This is done
// when the extension listener is set
} }
else { else {
Log.wtf(TAG, "Setup error: " + billingResult.getDebugMessage()); Log.wtf(TAG, "Setup error: " + billingResult.getDebugMessage());
@ -69,6 +67,12 @@ public class IapGooglePlay implements PurchasesUpdatedListener {
} }
}); });
} }
public void stop() {
Log.d(TAG, "stop()");
billingClient.endConnection();
}
public String toISO8601(final Date date) { public String toISO8601(final Date date) {
String formatted = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(date); String formatted = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(date);
return formatted.substring(0, 22) + ":" + formatted.substring(22); return formatted.substring(0, 22) + ":" + formatted.substring(22);
@ -133,8 +137,7 @@ public class IapGooglePlay implements PurchasesUpdatedListener {
return defoldState; return defoldState;
} }
private int billingResponseCodeToDefoldResponse(int responseCode) private int billingResponseCodeToDefoldResponse(int responseCode) {
{
int defoldResponse; int defoldResponse;
switch(responseCode) { switch(responseCode) {
case BillingResponseCode.BILLING_UNAVAILABLE: case BillingResponseCode.BILLING_UNAVAILABLE:
@ -176,8 +179,9 @@ public class IapGooglePlay implements PurchasesUpdatedListener {
return billingResponseCodeToDefoldResponse(result.getResponseCode()); return billingResponseCodeToDefoldResponse(result.getResponseCode());
} }
// ------------------------------------------- /**
* Query Google Play for purchases done within the app.
*/
private List<Purchase> queryPurchases(final String type) { private List<Purchase> queryPurchases(final String type) {
PurchasesResult result = billingClient.queryPurchases(type); PurchasesResult result = billingClient.queryPurchases(type);
if (result.getBillingResult().getResponseCode() != BillingResponseCode.OK) { if (result.getBillingResult().getResponseCode() != BillingResponseCode.OK) {
@ -187,8 +191,13 @@ public class IapGooglePlay implements PurchasesUpdatedListener {
return result.getPurchasesList(); return result.getPurchasesList();
} }
public void processPendingConsumables(final IPurchaseListener purchaseListener) /**
{ * This method is called either explicitly from Lua or from extension code
* when "set_listener()" is called from Lua.
* The method will query purchases and try to handle them one by one (either
* trying to consume/finish them or simply notify the provided listener).
*/
public void processPendingConsumables(final IPurchaseListener purchaseListener) {
Log.d(TAG, "processPendingConsumables()"); Log.d(TAG, "processPendingConsumables()");
List<Purchase> purchasesList = new ArrayList<Purchase>(); List<Purchase> purchasesList = new ArrayList<Purchase>();
purchasesList.addAll(queryPurchases(SkuType.INAPP)); purchasesList.addAll(queryPurchases(SkuType.INAPP));
@ -198,67 +207,78 @@ public class IapGooglePlay implements PurchasesUpdatedListener {
} }
} }
// ------------------------------------------- /**
* Consume a purchase. This will acknowledge the purchase and make it
private void consumePurchase(final Purchase purchase, final IPurchaseListener purchaseListener) * available to buy again.
{ */
Log.d(TAG, "consumePurchase()"); private void consumePurchase(final String purchaseToken, final ConsumeResponseListener consumeListener) {
Log.d(TAG, "consumePurchase() " + purchaseToken);
ConsumeParams consumeParams = ConsumeParams.newBuilder() ConsumeParams consumeParams = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken()) .setPurchaseToken(purchaseToken)
.build(); .build();
billingClient.consumeAsync(consumeParams, new ConsumeResponseListener() { billingClient.consumeAsync(consumeParams, consumeListener);
}
/**
* Called from Lua. This method will try to consume a purchase.
*/
public void finishTransaction(final String purchaseToken, final IPurchaseListener purchaseListener) {
Log.d(TAG, "finishTransaction() " + purchaseToken);
consumePurchase(purchaseToken, new ConsumeResponseListener() {
@Override @Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) { public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
Log.d(TAG, "consumePurchase() onConsumeResponse " + billingResult.getResponseCode() + " purchaseToken: " + purchaseToken); Log.d(TAG, "finishTransaction() response code " + billingResult.getResponseCode() + " purchaseToken: " + purchaseToken);
// note: we only call the purchase listener if an error happens
if (billingResult.getResponseCode() != BillingResponseCode.OK) { if (billingResult.getResponseCode() != BillingResponseCode.OK) {
Log.e(TAG, "Unable to consume purchase: " + billingResult.getDebugMessage()); Log.e(TAG, "Unable to consume purchase: " + billingResult.getDebugMessage());
purchaseListener.onPurchaseResult(billingResultToDefoldResponse(billingResult), ""); purchaseListener.onPurchaseResult(billingResultToDefoldResponse(billingResult), "");
return;
} }
} }
}); });
} }
public void finishTransaction(final String purchaseToken, final IPurchaseListener purchaseListener) { /**
Log.d(TAG, "finishTransaction() " + purchaseToken); * Handle a purchase. If the extension is configured to automatically
List<Purchase> purchasesList = new ArrayList<Purchase>(); * finish transactions the purchase will be immediately consumed. Otherwise
purchasesList.addAll(queryPurchases(SkuType.INAPP)); * the product will be returned via the listener without being consumed.
purchasesList.addAll(queryPurchases(SkuType.SUBS)); * NOTE: Billing 3.0 requires purchases to be acknowledged within 3 days of
* purchase unless they are consumed.
for(Purchase p : purchasesList) { */
Log.d(TAG, "finishTransaction() purchase: " + p.getOriginalJson());
if (p.getPurchaseToken().equals(purchaseToken)) {
consumePurchase(p, purchaseListener);
return;
}
}
Log.e(TAG, "Unable to find purchase for token: " + purchaseToken);
purchaseListener.onPurchaseResult(IapJNI.BILLING_RESPONSE_RESULT_ERROR, "");
}
// -------------------------------------------
private void handlePurchase(final Purchase purchase, final IPurchaseListener purchaseListener) { private void handlePurchase(final Purchase purchase, final IPurchaseListener purchaseListener) {
if (this.autoFinishTransactions) { if (this.autoFinishTransactions) {
consumePurchase(purchase, purchaseListener); consumePurchase(purchase.getPurchaseToken(), new ConsumeResponseListener() {
@Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
Log.d(TAG, "handlePurchase() response code " + billingResult.getResponseCode() + " purchaseToken: " + purchaseToken);
purchaseListener.onPurchaseResult(billingResultToDefoldResponse(billingResult), convertPurchase(purchase));
}
});
} }
else { else {
purchaseListener.onPurchaseResult(billingResponseCodeToDefoldResponse(BillingResponseCode.OK), convertPurchase(purchase)); purchaseListener.onPurchaseResult(billingResponseCodeToDefoldResponse(BillingResponseCode.OK), convertPurchase(purchase));
} }
} }
/**
* BillingClient listener set in the constructor.
*/
@Override @Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) { public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) { if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) {
for (Purchase purchase : purchases) { for (Purchase purchase : purchases) {
handlePurchase(purchase, this.purchaseListener); handlePurchase(purchase, this.purchaseListener);
} }
} else { }
else {
this.purchaseListener.onPurchaseResult(billingResultToDefoldResponse(billingResult), ""); this.purchaseListener.onPurchaseResult(billingResultToDefoldResponse(billingResult), "");
} }
} }
/**
* Buy a product. This method stores the listener and uses it in the
* onPurchasesUpdated() callback.
*/
private void buyProduct(SkuDetails sku, final IPurchaseListener purchaseListener) { private void buyProduct(SkuDetails sku, final IPurchaseListener purchaseListener) {
this.purchaseListener = purchaseListener; this.purchaseListener = purchaseListener;
@ -273,6 +293,9 @@ public class IapGooglePlay implements PurchasesUpdatedListener {
} }
} }
/**
* Called from Lua.
*/
public void buy(final String product, final IPurchaseListener purchaseListener) { public void buy(final String product, final IPurchaseListener purchaseListener) {
Log.d(TAG, "buy()"); Log.d(TAG, "buy()");
@ -298,10 +321,11 @@ public class IapGooglePlay implements PurchasesUpdatedListener {
} }
} }
// ------------------------------------------- /**
* Get details for a list of products. The products can be a mix of
private void querySkuDetailsAsync(final List<String> skuList, final SkuDetailsResponseListener listener) * in-app products and subscriptions.
{ */
private void querySkuDetailsAsync(final List<String> skuList, final SkuDetailsResponseListener listener) {
SkuDetailsResponseListener detailsListener = new SkuDetailsResponseListener() { SkuDetailsResponseListener detailsListener = new SkuDetailsResponseListener() {
private List<SkuDetails> allSkuDetails = new ArrayList<SkuDetails>(); private List<SkuDetails> allSkuDetails = new ArrayList<SkuDetails>();
private int queries = 2; private int queries = 2;
@ -309,15 +333,14 @@ public class IapGooglePlay implements PurchasesUpdatedListener {
@Override @Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetails) { public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetails) {
// cache skus (cache will be used to speed up buying) // cache skus (cache will be used to speed up buying)
for (SkuDetails sd : skuDetails) for (SkuDetails sd : skuDetails) {
{
IapGooglePlay.this.products.put(sd.getSku(), sd); IapGooglePlay.this.products.put(sd.getSku(), sd);
} }
// // add to list of all sku details
allSkuDetails.addAll(skuDetails); allSkuDetails.addAll(skuDetails);
// we're finished when we have queried for both in-app and subs
queries--; queries--;
if (queries == 0) if (queries == 0) {
{
listener.onSkuDetailsResponse(billingResult, allSkuDetails); listener.onSkuDetailsResponse(billingResult, allSkuDetails);
} }
} }
@ -326,8 +349,10 @@ public class IapGooglePlay implements PurchasesUpdatedListener {
billingClient.querySkuDetailsAsync(SkuDetailsParams.newBuilder().setSkusList(skuList).setType(SkuType.SUBS).build(), detailsListener); billingClient.querySkuDetailsAsync(SkuDetailsParams.newBuilder().setSkusList(skuList).setType(SkuType.SUBS).build(), detailsListener);
} }
public void listItems(final String products, final IListProductsListener productsListener, final long commandPtr) /**
{ * Called from Lua.
*/
public void listItems(final String products, final IListProductsListener productsListener, final long commandPtr) {
Log.d(TAG, "listItems()"); Log.d(TAG, "listItems()");
// create list of skus from comma separated string // create list of skus from comma separated string
@ -343,8 +368,7 @@ public class IapGooglePlay implements PurchasesUpdatedListener {
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetails) { public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetails) {
JSONArray a = new JSONArray(); JSONArray a = new JSONArray();
if (billingResult.getResponseCode() == BillingResponseCode.OK) { if (billingResult.getResponseCode() == BillingResponseCode.OK) {
for (SkuDetails sd : skuDetails) for (SkuDetails sd : skuDetails) {
{
a.put(convertSkuDetails(sd)); a.put(convertSkuDetails(sd));
} }
} }
@ -356,15 +380,7 @@ public class IapGooglePlay implements PurchasesUpdatedListener {
}); });
} }
// ------------------------------------------- public void restore(final IPurchaseListener listener) {
public void stop()
{
Log.d(TAG, "stop()");
}
public void restore(final IPurchaseListener listener)
{
Log.d(TAG, "restore()"); Log.d(TAG, "restore()");
} }
} }

View File

@ -88,8 +88,8 @@ local function buy_listener(self, transaction, error)
gui.set_color(gui.get_node("reset/larrylabel"), vmath.vector4(1,1,1,1)) gui.set_color(gui.get_node("reset/larrylabel"), vmath.vector4(1,1,1,1))
product_items["reset"] = transaction product_items["reset"] = transaction
else else
log("iap.buy() ok") log("iap.buy() ok %s", transaction.ident)
log("iap.finish()") log("iap.finish() %s", transaction.ident)
iap.finish(transaction) iap.finish(transaction)
end end
end end