diff --git a/play-billing-library/app/build.gradle b/play-billing-library/app/build.gradle new file mode 100644 index 000000000..293a86d84 --- /dev/null +++ b/play-billing-library/app/build.gradle @@ -0,0 +1,75 @@ +/* Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +apply plugin: 'com.android.application' +apply plugin: 'com.google.android.gms.strict-version-matcher-plugin' +// OSS Plugin +apply plugin: 'com.google.android.gms.oss-licenses-plugin' + + +android { + compileSdk 36 + namespace="com.google.play.billing.samples.managedcatalogue" + + defaultConfig { + applicationId "com.google.play.billing.samples.managedcatalogue" + minSdk 23 + targetSdk 36 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + all { + proguardFiles 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + coreLibraryDesugaringEnabled true + } + +} + + + +dependencies { + + // OSS Plugin + implementation 'com.google.android.gms:play-services-oss-licenses:17.1.0' + + // Play Billing Library + implementation "com.android.billingclient:billing:8.1.0" + + implementation 'androidx.appcompat:appcompat:1.7.0' + // Align kotlin versions + implementation(platform("org.jetbrains.kotlin:kotlin-bom:2.1.10")) + implementation 'com.google.android.material:material:1.13.0' + implementation 'androidx.constraintlayout:constraintlayout:2.2.1' + implementation 'com.google.guava:guava:31.1-android' + + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.mockito:mockito-core:5.15.2' + testImplementation 'org.mockito:mockito-android:5.15.2' + androidTestImplementation 'androidx.test.ext:junit:1.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5' +} diff --git a/play-billing-library/app/src/main/AndroidManifest.xml b/play-billing-library/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..90cdc429c --- /dev/null +++ b/play-billing-library/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + diff --git a/play-billing-library/app/src/main/ic_launcher-playstore.png b/play-billing-library/app/src/main/ic_launcher-playstore.png new file mode 100644 index 000000000..69fa8c0be Binary files /dev/null and b/play-billing-library/app/src/main/ic_launcher-playstore.png differ diff --git a/play-billing-library/app/src/main/java/com/google/play/billing/samples/managedcatalogue/BillingClientWrapper.java b/play-billing-library/app/src/main/java/com/google/play/billing/samples/managedcatalogue/BillingClientWrapper.java new file mode 100644 index 000000000..9c507bf71 --- /dev/null +++ b/play-billing-library/app/src/main/java/com/google/play/billing/samples/managedcatalogue/BillingClientWrapper.java @@ -0,0 +1,365 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.play.billing.samples.managedcatalogue; + + +import android.app.Activity; +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.billingclient.api.AcknowledgePurchaseParams; +import com.android.billingclient.api.AcknowledgePurchaseResponseListener; +import com.android.billingclient.api.BillingClient; +import com.android.billingclient.api.BillingClient.BillingResponseCode; +import com.android.billingclient.api.BillingClient.ProductType; +import com.android.billingclient.api.BillingClientStateListener; +import com.android.billingclient.api.BillingConfig; +import com.android.billingclient.api.BillingConfigResponseListener; +import com.android.billingclient.api.BillingFlowParams; +import com.android.billingclient.api.BillingFlowParams.ProductDetailsParams; +import com.android.billingclient.api.BillingFlowParams.SubscriptionUpdateParams; +import com.android.billingclient.api.BillingResult; +import com.android.billingclient.api.ConsumeParams; +import com.android.billingclient.api.ConsumeResponseListener; +import com.android.billingclient.api.GetBillingConfigParams; +import com.android.billingclient.api.InAppMessageParams; +import com.android.billingclient.api.InAppMessageResult; +import com.android.billingclient.api.InAppMessageResponseListener; +import com.android.billingclient.api.PendingPurchasesParams; +import com.android.billingclient.api.ProductDetails; +import com.android.billingclient.api.ProductDetailsResponseListener; +import com.android.billingclient.api.Purchase; +import com.android.billingclient.api.PurchasesUpdatedListener; +import com.android.billingclient.api.QueryProductDetailsParams; +import com.android.billingclient.api.QueryProductDetailsParams.Product; +import com.google.common.collect.ImmutableList; + +import java.util.ArrayList; +import java.util.List; + +/** + * A wrapper for the Google Play Billing Library that handles all the billing logic. + */ +class BillingClientWrapper { + + private final Context context; + private final Activity activity; + + public BillingClientWrapper(Context context, Activity activity) { + this.context = context; + this.activity = activity; + } + + List productDetailsList; + ProductDetails productDetails; + public void initializeBillingClient() { + // [START android_playbilling_initialize_java] + PurchasesUpdatedListener purchasesUpdatedListener = + (billingResult, purchases) -> { + // To be implemented in a later section. + }; + + BillingClient billingClient = + BillingClient.newBuilder(context) + .setListener(purchasesUpdatedListener) + // Configure other settings. + .build(); + // [END android_playbilling_initialize_java] + } + + public void startConnection() { + BillingClient billingClient = BillingClient.newBuilder(context) + .setListener((billingResult, list) -> {}) + .build(); + // [START android_playbilling_startconnection_java] + billingClient.startConnection( + new BillingClientStateListener() { + @Override + public void onBillingSetupFinished(BillingResult billingResult) { + if (billingResult.getResponseCode() == BillingResponseCode.OK) { + // The BillingClient is ready. You can query purchases here. + // It's a good practice to query products after the connection is established. + queryProductDetails(); + } + } + + @Override + public void onBillingServiceDisconnected() { + // Try to restart the connection on the next request to + // Google Play by calling the startConnection() method. + } + }); + // [END android_playbilling_startconnection_java] + } + + public void enableAutoReconnect() { + PurchasesUpdatedListener listener = (billingResult, list) -> {}; + // [START android_playbilling_enableautoreconnect_java] + BillingClient billingClient = + BillingClient.newBuilder(context) + .setListener(listener) + .enablePendingPurchases(PendingPurchasesParams.newBuilder() + .enableOneTimeProducts() + .build()) + .build(); + // [END android_playbilling_enableautoreconnect_java] + } + + public void queryProductDetails() { + BillingClient billingClient = BillingClient.newBuilder(context) + .setListener((billingResult, list) -> {}) + .build(); + // [START android_playbilling_queryproductdetails_java] + QueryProductDetailsParams queryProductDetailsParams = + QueryProductDetailsParams.newBuilder() + .setProductList( + ImmutableList.of( + Product.newBuilder() + .setProductId("product_id_example") + .setProductType(ProductType.SUBS) + .build())) + .build(); + + billingClient.queryProductDetailsAsync( + queryProductDetailsParams, + (billingResult, productDetailsList) -> { + if (billingResult.getResponseCode() == BillingResponseCode.OK && productDetailsList != null) { + this.productDetailsList = productDetailsList.getProductDetailsList(); + // Now that the list is populated, you can use it. + // For example, find a specific product. + if (!productDetailsList.getProductDetailsList().isEmpty()) { + this.productDetails = productDetailsList.getProductDetailsList().get(0); + // Any methods that require productDetails should be called from here. + } + } + }); + // [END android_playbilling_queryproductdetails_java] + } + + public void onPurchasesUpdated() { + // [START android_playbilling_onpurchasesupdated_java] + new PurchasesUpdatedListener() { + @Override + public void onPurchasesUpdated( + @NonNull BillingResult billingResult, @Nullable List purchases) { + if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) { + for (Purchase purchase : purchases) { + // Process the purchase as described in the next section. + } + } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) { + // Handle an error caused by a user canceling the purchase flow. + } else { + // Handle any other error codes. + } + } + }; + // [END android_playbilling_onpurchasesupdated_java] + } + + public void consumeProduct(Purchase purchase) { + BillingClient billingClient = BillingClient.newBuilder(context) + .setListener((billingResult, list) -> {}) + .build(); + // [START android_playbilling_consumeproduct_java] + ConsumeParams consumeParams = + ConsumeParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build(); + + ConsumeResponseListener listener = + (billingResult, purchaseToken) -> { + if (billingResult.getResponseCode() == BillingResponseCode.OK) { + // Handle the success of the consume operation. + } + }; + + billingClient.consumeAsync(consumeParams, listener); + // [END android_playbilling_consumeproduct_java] + } + + public void acknowledgePurchase(Purchase purchase) { + // [START android_playbilling_acknowledge_java] + BillingClient client = BillingClient.newBuilder(context) + .setListener((billingResult, list) -> {}) + .build(); + AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = + (billingResult) -> {}; + + AcknowledgePurchaseParams acknowledgePurchaseParams = + AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build(); + client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener); + // [END android_playbilling_acknowledge_java] + } + + public void handlePurchaseRecap() { + // [START android_playbilling_handlepurchaserecap_java] + final class HandlePurchase { + void handlePurchase(Purchase purchase) { + // Purchase retrieved from BillingClient#queryPurchasesAsync or your + // onPurchasesUpdated. + + // Step 1: Send the purchase to your secure backend to verify the purchase + // following + // https://developer.android.com/google/play/billing/security#verify + + // Step 2: Update your entitlement storage with the purchase. If purchase is + // in PENDING state then ensure the entitlement is marked as pending and the + // user does not receive benefits yet. It is recommended that this step is + // done on your secure backend and can combine in the API call to your + // backend in step 1. + + // Step 3: Notify the user using appropriate messaging (delaying + // notification if needed as discussed above). + + // Step 4: Notify Google the purchase was processed using the steps + // discussed in the processing purchases section. + } + } + // [END android_playbilling_handlepurchaserecap_java] + } + + public void getBillingConfigAsync() { + BillingClient billingClient = BillingClient.newBuilder(context) + .setListener((billingResult, list) -> {}) + .build(); + // [START android_playbilling_getbillingconfig_java] + // Use the default GetBillingConfigParams. + GetBillingConfigParams getBillingConfigParams = GetBillingConfigParams.newBuilder().build(); + billingClient.getBillingConfigAsync( + getBillingConfigParams, + (billingResult, billingConfig) -> { + if (billingResult.getResponseCode() == BillingResponseCode.OK && billingConfig != null) { + String countryCode = billingConfig.getCountryCode(); + } else { + // TODO: Handle errors + } + }); + // [END android_playbilling_getbillingconfig_java] + } + + public void changeSubscriptionPlan() { + if (productDetails == null) { + // Can't launch the flow if product details aren't loaded yet. + // You could initiate a query here or show an error message. + return; + } + String purchaseTokenOfExistingSubscription = "purchase_token"; + // [START android_playbilling_subscription_replace_java] + BillingClient billingClient = BillingClient.newBuilder(context) + .setListener((billingResult, list) -> {}) + .build(); + + int replacementModeForBasePlan = + SubscriptionUpdateParams.ReplacementMode.CHARGE_FULL_PRICE; + int replacementModeForAddon = + SubscriptionUpdateParams.ReplacementMode.CHARGE_FULL_PRICE; + // ProductDetails obtained from queryProductDetailsAsync(). + ProductDetailsParams productDetails1 = + ProductDetailsParams.newBuilder() + .setProductDetails(productDetails) + .build(); + ProductDetailsParams productDetails2 = + ProductDetailsParams.newBuilder() + .setProductDetails(productDetails) + .build(); + ProductDetailsParams productDetails3 = ProductDetailsParams.newBuilder().build(); + + ArrayList newProductDetailsList = new ArrayList<>(); + newProductDetailsList.add(productDetails1); + newProductDetailsList.add(productDetails2); + newProductDetailsList.add(productDetails3); + + BillingFlowParams billingFlowParams = + BillingFlowParams.newBuilder() + .setSubscriptionUpdateParams( + SubscriptionUpdateParams.newBuilder() + .setOldPurchaseToken(purchaseTokenOfExistingSubscription) + .build()) + .setProductDetailsParamsList(newProductDetailsList) + .build(); + + billingClient.launchBillingFlow(activity, billingFlowParams); + // [END android_playbilling_subscription_replace_java] + } + + public void changeSubscriptionPlanDeprecated() { + if (productDetails == null || productDetails.getSubscriptionOfferDetails() == null || productDetails.getSubscriptionOfferDetails().isEmpty()) { + // Can't launch the flow if product details or offers aren't loaded yet. + return; + } + + BillingClient billingClient = BillingClient.newBuilder(context) + .setListener((billingResult, list) -> {}) + .build(); + int selectedOfferIndex = 0; + // [START android_playbilling_subscription_update_deprecated_java] + String offerToken = + productDetails.getSubscriptionOfferDetails().get(selectedOfferIndex).getOfferToken(); + + BillingFlowParams billingFlowParams = + BillingFlowParams.newBuilder() + .setProductDetailsParamsList( + ImmutableList.of( + ProductDetailsParams.newBuilder() + // fetched via queryProductDetailsAsync + .setProductDetails(productDetails) + // offerToken can be found in + // ProductDetails=>SubscriptionOfferDetails + .setOfferToken(offerToken) + .build())) + .setSubscriptionUpdateParams( + SubscriptionUpdateParams.newBuilder() + // purchaseToken can be found in Purchase#getPurchaseToken + .setOldPurchaseToken("old_purchase_token") + .setSubscriptionReplacementMode( + SubscriptionUpdateParams.ReplacementMode.CHARGE_FULL_PRICE) + .build()) + .build(); + + BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams); + // ... + // [END android_playbilling_subscription_update_deprecated_java] + } + + public void inAppMessaging() { + BillingClient billingClient = BillingClient.newBuilder(context) + .setListener((billingResult, list) -> {}) + .build(); + // [START android_playbilling_inappmessaging_java] + InAppMessageParams inAppMessageParams = + InAppMessageParams.newBuilder() + .addInAppMessageCategoryToShow( + InAppMessageParams.InAppMessageCategoryId.TRANSACTIONAL) + .build(); + + billingClient.showInAppMessages( + activity, + inAppMessageParams, + inAppMessageResult -> { + if (inAppMessageResult.getResponseCode() + == InAppMessageResult.InAppMessageResponseCode.NO_ACTION_NEEDED) { + // The flow has finished and there is no action needed from developers. + } else if (inAppMessageResult.getResponseCode() + == InAppMessageResult.InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED) { + // The subscription status changed. For example, a subscription + // has been recovered from a suspend state. Developers should + // expect the purchase token to be returned with this response + // code and use it with the Google Play Developer API. + } + }); + // [END android_playbilling_inappmessaging_java] + } +} diff --git a/play-billing-library/app/src/main/java/com/google/play/billing/samples/managedcatalogue/billing/BillingServiceClient.java b/play-billing-library/app/src/main/java/com/google/play/billing/samples/managedcatalogue/billing/BillingServiceClient.java new file mode 100644 index 000000000..f81321da0 --- /dev/null +++ b/play-billing-library/app/src/main/java/com/google/play/billing/samples/managedcatalogue/billing/BillingServiceClient.java @@ -0,0 +1,160 @@ +/* Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.play.billing.samples.managedcatalogue.billing; + +import android.app.Activity; +import android.util.Log; +import androidx.annotation.VisibleForTesting; +import androidx.appcompat.app.AppCompatActivity; +import com.android.billingclient.api.BillingClient; +import com.android.billingclient.api.BillingClient.BillingResponseCode; +import com.android.billingclient.api.BillingClientStateListener; +import com.android.billingclient.api.BillingFlowParams; +import com.android.billingclient.api.BillingResult; +import com.android.billingclient.api.PendingPurchasesParams; +import com.android.billingclient.api.ProductDetails; +import com.android.billingclient.api.ProductDetailsResponseListener; +import com.android.billingclient.api.QueryProductDetailsParams; +import com.android.billingclient.api.QueryProductDetailsParams.Product; +import com.android.billingclient.api.QueryProductDetailsResult; +import com.google.common.collect.ImmutableList; +import java.util.List; + +/** + * Manages interactions with the Google Play Billing Library for handling pre-orders. + * + *

This class encapsulates the setup of the {@link BillingClient}, manages the connection + * lifecycle (including retries), queries product details, and notifies a listener. + */ +public class BillingServiceClient { + + private static final String TAG = "BillingServiceClient"; + private BillingClient billingClient; + private final AppCompatActivity activity; + private final BillingServiceClientListener listener; + + public BillingServiceClient(AppCompatActivity activity, BillingServiceClientListener listener) { + this.activity = activity; + this.listener = listener; + billingClient = createBillingClient(); + } + + // Constructor for testing + @VisibleForTesting + BillingServiceClient( + AppCompatActivity activity, + BillingServiceClientListener listener, + BillingClient billingClient) { + this.activity = activity; + this.listener = listener; + this.billingClient = billingClient; + } + + /** + * Starts the billing connection with Google Play. This method should be called exactly once + * before any other methods in this class. + * + * @param productList The list of products to query for after the connection is established. + */ + public void startBillingConnection(List productList) { + billingClient.startConnection( + new BillingClientStateListener() { + @Override + public void onBillingSetupFinished(BillingResult billingResult) { + if (billingResult.getResponseCode() == BillingResponseCode.OK) { + Log.d(TAG, "Billing Client Connection Successful"); + queryProductDetails(productList); + } else { + Log.e(TAG, "Billing Client Connection Failed: " + billingResult.getDebugMessage()); + listener.onBillingSetupFailed(billingResult); // Propagate the error to the listener to show a message to the user. + } + } + + @Override + public void onBillingServiceDisconnected() { + Log.e(TAG, "Billing Client Connection Lost"); + listener.onBillingError("Billing Connection Lost"); + } + }); + } + + /** + * Launches the billing flow for the product with the given offer token. + * + * @param activity The activity instance from which the billing flow will be launched. + * @param productDetails The product details of the product to purchase. + * @param offerToken The offer token of the product to purchase. + * @return The result of the billing flow. + */ + public void launchPurchase(Activity activity, ProductDetails productDetails, String offerToken) { + ImmutableList productDetailsParamsList = + ImmutableList.of( + BillingFlowParams.ProductDetailsParams.newBuilder() + .setProductDetails(productDetails) + .setOfferToken(offerToken) + .build()); + BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() + .setProductDetailsParamsList(productDetailsParamsList) + .build(); + billingClient.launchBillingFlow(activity, billingFlowParams); +} + + /** + * Ends the billing connection with Google Play. This method should be called when the app is + * closed. + */ + public void endBillingConnection() { + if (billingClient != null && billingClient.isReady()) { + billingClient.endConnection(); + billingClient = null; + } + } + + private BillingClient createBillingClient() { + return BillingClient.newBuilder(activity) + .enablePendingPurchases(PendingPurchasesParams.newBuilder().enableOneTimeProducts().build()) + // For one-time products, add a listener to acknowledge the purchases. This will notify + // Google the purchase was processed. + // For client-only apps, use billingClient.acknowledgePurchase(). + // If you have a secure backend, you must acknowledge purchases on your server using the + // server-side API. + // See https://developer.android.com/google/play/billing/security#acknowledge + .setListener((billingResult, purchases) -> {}) + .enableAutoServiceReconnection() + .build(); + } + + private void queryProductDetails(List productList) { + QueryProductDetailsParams queryProductDetailsParams = + QueryProductDetailsParams.newBuilder().setProductList(productList).build(); + + billingClient.queryProductDetailsAsync( + queryProductDetailsParams, + new ProductDetailsResponseListener() { + @Override + public void onProductDetailsResponse( + BillingResult billingResult, QueryProductDetailsResult productDetailsResponse) { + if (billingResult.getResponseCode() == BillingResponseCode.OK) { + List productDetailsList = + productDetailsResponse.getProductDetailsList(); + listener.onProductDetailsResponse(productDetailsList); + } else { + Log.e(TAG, "QueryProductDetailsAsync Failed: " + billingResult.getDebugMessage()); + listener.onBillingError("Query Products Failed: " + billingResult.getResponseCode()); + } + } + }); + } +} diff --git a/play-billing-library/app/src/main/java/com/google/play/billing/samples/managedcatalogue/billing/BillingServiceClientListener.java b/play-billing-library/app/src/main/java/com/google/play/billing/samples/managedcatalogue/billing/BillingServiceClientListener.java new file mode 100644 index 000000000..8c2285847 --- /dev/null +++ b/play-billing-library/app/src/main/java/com/google/play/billing/samples/managedcatalogue/billing/BillingServiceClientListener.java @@ -0,0 +1,28 @@ +/* Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.play.billing.samples.managedcatalogue.billing; + +import com.android.billingclient.api.BillingResult; +import com.android.billingclient.api.ProductDetails; +import java.util.List; + +/** Listener interface for handling responses from the BillingServiceClient. */ +public interface BillingServiceClientListener { + void onProductDetailsResponse(List productDetailsList); + + void onBillingSetupFailed(BillingResult billingResult); + + void onBillingError(String errorMsg); +} diff --git a/play-billing-library/build.gradle b/play-billing-library/build.gradle new file mode 100644 index 000000000..a602bdf80 --- /dev/null +++ b/play-billing-library/build.gradle @@ -0,0 +1,45 @@ +/* Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:8.9.1" + + classpath 'com.google.android.gms:strict-version-matcher-plugin:1.2.1' + + // OSS plugin + classpath 'com.google.android.gms:oss-licenses-plugin:0.10.6' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/play-billing-library/gradle.properties b/play-billing-library/gradle.properties new file mode 100644 index 000000000..2a19ff5b6 --- /dev/null +++ b/play-billing-library/gradle.properties @@ -0,0 +1,20 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true + diff --git a/play-billing-library/gradle/wrapper/gradle-wrapper.jar b/play-billing-library/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..1b33c55ba Binary files /dev/null and b/play-billing-library/gradle/wrapper/gradle-wrapper.jar differ diff --git a/play-billing-library/gradle/wrapper/gradle-wrapper.properties b/play-billing-library/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..3106b43b8 --- /dev/null +++ b/play-billing-library/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,8 @@ +#Fri Aug 14 10:59:44 CDT 2020 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists