I referred to the Android documentation to implement an in-app purchase to remove ads. https://developer.android.com/google/play/billing/integrate
This is my code:
public static final String AD_REMOVAL_SKU = "remove_ads";
private void LoadBillingInfo() {
PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
@Override
public void onPurchasesUpdated(@NonNull BillingResult billingResult, List<Purchase> purchases) {
// To be implemented in a later section.
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
for (Purchase purchase : purchases) {
if (purchase.getSku().equals(AD_REMOVAL_SKU)) {
//Acknowledge the payment
AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = new AcknowledgePurchaseResponseListener() {
@Override
public void onAcknowledgePurchaseResponse(@NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
areAdsRemoved = true;
sharedPreferences.edit().putBoolean(AD_REMOVAL_SKU, areAdsRemoved).apply();
DestroyBannerAd();
Log.i("LOG", "Congrats! You are Ad free!");
Toast.makeText(MainActivity.this, "Congrats! You are Ad free!", Toast.LENGTH_SHORT).show();
}
}
};
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
if (!purchase.isAcknowledged()) {
AcknowledgePurchaseParams acknowledgePurchaseParams =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
billingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
}
}
}
}
}
}
};
billingClient = BillingClient.newBuilder(this)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build();
StartBillingConnection();
}
private void StartBillingConnection() {
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The BillingClient is ready. You can query purchases here.
if (HasNetwork()) {
isBillingClientReady = true;
Toast.makeText(MainActivity.this, "onBillingSetupFinished", Toast.LENGTH_SHORT).show();
Log.i("LOG", "onBillingSetupFinished");
Log.i("LOG", "Response code: " + billingResult.getResponseCode());
}
}
}
@Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
isBillingClientReady = false;
Toast.makeText(MainActivity.this, "onBillingServiceDisconnected", Toast.LENGTH_SHORT).show();
Log.i("LOG", "onBillingServiceDisconnected");
if (HasNetwork()) {
StartBillingConnection();
}
}
});
}
private void MakePayment() {
final SkuDetails[] skuDetail = new SkuDetails[1];
Log.i("LOG", "1");
List<String> skuList = new ArrayList<>();
Log.i("LOG", "2");
skuList.add(AD_REMOVAL_SKU);
Log.i("LOG", "3");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
Log.i("LOG", "4");
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
Log.i("LOG", "5");
billingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(@NonNull BillingResult billingResult, List<SkuDetails> skuDetailsList) {
Log.i("LOG", "6");
// Process the result.
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& skuDetailsList != null) {
Log.i("LOG", "7");
for (Object skuDetailsObject : skuDetailsList) {
Log.i("LOG", "8");
SkuDetails skuDetails = (SkuDetails) skuDetailsObject;
Log.i("LOG", "9");
String sku = skuDetails.getSku();
Log.i("LOG", "10");
if (AD_REMOVAL_SKU.equals(sku)) {
Toast.makeText(MainActivity.this, "Right Inside", Toast.LENGTH_SHORT).show();
Log.i("LOG", "Right Inside");
skuDetail[0] = skuDetails;
}
}
}
}
});
Toast.makeText(this, skuDetail[0].getSku() + " " + skuDetail[0].getTitle(), Toast.LENGTH_LONG).show();
Log.i("LOG", skuDetail[0].getSku() + " " + skuDetail[0].getTitle());
// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetail[0])
.build();
billingClient.launchBillingFlow(this, billingFlowParams);
}
I call this onClick:
if (!areAdsRemoved) {
if (isBillingClientReady) {
MakePayment();
} else {
Log.i("LOG", "Sorry! You are unable to make the payment due to network connectivity issues");
Toast.makeText(this,
"Sorry! You are unable to make the payment due to network connectivity issues",
Toast.LENGTH_LONG).show();
LoadBillingInfo();
}
}
But, when I click the button to remove ads, the app crashes.
So, I put Log statements everywhere. And I found out that Log.i("LOG", "5");
is the last printed Log statement.
When the app crashes I get this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String b.a.a.a.j.a()' on a null object reference
at b.d.b.b.a0.a.a(:17)
at a.b.o.i.g.e()
at a.b.o.i.g.s(:1)
at b.d.b.b.z.h$a.onClick()
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22433)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6126)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
I am unable to find a solution for this problem...
Has anyone faced this issue? Please Help! 🙏🏼
skuDetail[0]
is null. Move the code that dereferences it to the async callback where that data is available