Add more api calls for billing integration.
This commit is contained in:
parent
26e79db057
commit
478e3a7233
3 changed files with 104 additions and 12 deletions
|
@ -5,12 +5,22 @@
|
|||
|
||||
package org.thoughtcrime.securesms.billing
|
||||
|
||||
import android.app.Activity
|
||||
|
||||
/**
|
||||
* Variant interface for the BillingApi.
|
||||
*/
|
||||
interface GooglePlayBillingApi {
|
||||
fun isApiAvailable(): Boolean = false
|
||||
suspend fun queryProducts() {}
|
||||
suspend fun queryProducts() = Unit
|
||||
|
||||
/**
|
||||
* Queries the user's current purchases. This enqueues a check and will
|
||||
* propagate it to the normal callbacks in the api.
|
||||
*/
|
||||
suspend fun queryPurchases() = Unit
|
||||
|
||||
suspend fun launchBillingFlow(activity: Activity) = Unit
|
||||
|
||||
/**
|
||||
* Empty implementation, to be used when play services are available but
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package org.thoughtcrime.securesms.billing
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import com.android.billingclient.api.BillingClient.BillingResponseCode
|
||||
import com.android.billingclient.api.ProductDetailsResult
|
||||
import com.android.billingclient.api.PurchasesUpdatedListener
|
||||
import org.signal.billing.BillingApi
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
|
@ -29,7 +32,25 @@ private class GooglePlayBillingApiImpl(context: Context) : GooglePlayBillingApi
|
|||
val TAG = Log.tag(GooglePlayBillingApiImpl::class)
|
||||
}
|
||||
|
||||
private val billingApi: BillingApi = BillingApi.getOrCreate(context)
|
||||
private val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases ->
|
||||
when {
|
||||
billingResult.responseCode == BillingResponseCode.OK && purchases != null -> {
|
||||
Log.d(TAG, "purchasesUpdatedListener: ${purchases.size} purchases.")
|
||||
purchases.forEach {
|
||||
// Handle purchases.
|
||||
}
|
||||
}
|
||||
billingResult.responseCode == BillingResponseCode.USER_CANCELED -> {
|
||||
// Handle user cancelled
|
||||
Log.d(TAG, "purchasesUpdatedListener: User cancelled.")
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "purchasesUpdatedListener: No purchases.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val billingApi: BillingApi = BillingApi.getOrCreate(context, purchasesUpdatedListener)
|
||||
|
||||
override fun isApiAvailable(): Boolean = billingApi.areSubscriptionsSupported()
|
||||
|
||||
|
@ -38,4 +59,15 @@ private class GooglePlayBillingApiImpl(context: Context) : GooglePlayBillingApi
|
|||
|
||||
Log.d(TAG, "queryProducts: ${products.billingResult.responseCode}, ${products.billingResult.debugMessage}")
|
||||
}
|
||||
|
||||
override suspend fun queryPurchases() {
|
||||
Log.d(TAG, "queryPurchases")
|
||||
|
||||
val purchaseResult = billingApi.queryPurchases()
|
||||
purchasesUpdatedListener.onPurchasesUpdated(purchaseResult.billingResult, purchaseResult.purchasesList)
|
||||
}
|
||||
|
||||
override suspend fun launchBillingFlow(activity: Activity) {
|
||||
billingApi.launchBillingFlow(activity)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,16 +5,24 @@
|
|||
|
||||
package org.signal.billing
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
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.BillingFlowParams
|
||||
import com.android.billingclient.api.BillingFlowParams.ProductDetailsParams
|
||||
import com.android.billingclient.api.BillingResult
|
||||
import com.android.billingclient.api.PendingPurchasesParams
|
||||
import com.android.billingclient.api.ProductDetails
|
||||
import com.android.billingclient.api.ProductDetailsResult
|
||||
import com.android.billingclient.api.PurchasesResult
|
||||
import com.android.billingclient.api.PurchasesUpdatedListener
|
||||
import com.android.billingclient.api.QueryProductDetailsParams
|
||||
import com.android.billingclient.api.QueryPurchasesParams
|
||||
import com.android.billingclient.api.queryProductDetails
|
||||
import com.android.billingclient.api.queryPurchasesAsync
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -38,7 +46,8 @@ import org.signal.core.util.logging.Log
|
|||
* Care should be taken here to ensure only one instance of this exists at a time.
|
||||
*/
|
||||
class BillingApi private constructor(
|
||||
context: Context
|
||||
context: Context,
|
||||
onPurchaseUpdateListener: PurchasesUpdatedListener
|
||||
) {
|
||||
companion object {
|
||||
private val TAG = Log.tag(BillingApi::class)
|
||||
|
@ -46,8 +55,8 @@ class BillingApi private constructor(
|
|||
private var instance: BillingApi? = null
|
||||
|
||||
@Synchronized
|
||||
fun getOrCreate(context: Context): BillingApi {
|
||||
return instance ?: BillingApi(context).let {
|
||||
fun getOrCreate(context: Context, onPurchaseUpdateListener: PurchasesUpdatedListener): BillingApi {
|
||||
return instance ?: BillingApi(context, onPurchaseUpdateListener).let {
|
||||
instance = it
|
||||
it
|
||||
}
|
||||
|
@ -57,13 +66,8 @@ class BillingApi private constructor(
|
|||
private val connectionState = MutableStateFlow<State>(State.Init)
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.Default)
|
||||
|
||||
private val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases ->
|
||||
Log.d(TAG, "purchasesUpdatedListener: ${billingResult.responseCode}")
|
||||
Log.d(TAG, "purchasesUpdatedListener: Detected ${purchases?.size ?: 0} purchases.")
|
||||
}
|
||||
|
||||
private val billingClient: BillingClient = BillingClient.newBuilder(context)
|
||||
.setListener(purchasesUpdatedListener)
|
||||
.setListener(onPurchaseUpdateListener)
|
||||
.enablePendingPurchases(
|
||||
PendingPurchasesParams.newBuilder()
|
||||
.enableOneTimeProducts()
|
||||
|
@ -88,7 +92,7 @@ class BillingApi private constructor(
|
|||
val productList = listOf(
|
||||
QueryProductDetailsParams.Product.newBuilder()
|
||||
.setProductId("") // TODO [message-backups] where does the product id come from?
|
||||
.setProductType(BillingClient.ProductType.SUBS)
|
||||
.setProductType(ProductType.SUBS)
|
||||
.build()
|
||||
)
|
||||
|
||||
|
@ -103,6 +107,52 @@ class BillingApi private constructor(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun queryPurchases(): PurchasesResult {
|
||||
val param = QueryPurchasesParams.newBuilder()
|
||||
.setProductType(ProductType.SUBS)
|
||||
.build()
|
||||
|
||||
return doOnConnectionReady {
|
||||
billingClient.queryPurchasesAsync(param)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches the Google Play billing flow.
|
||||
* Returns a billing result if we launched the flow, null otherwise.
|
||||
*/
|
||||
suspend fun launchBillingFlow(activity: Activity): BillingResult? {
|
||||
val productDetails = queryProducts().productDetailsList
|
||||
if (productDetails.isNullOrEmpty()) {
|
||||
Log.w(TAG, "No products are available! Cancelling billing flow launch.")
|
||||
return null
|
||||
}
|
||||
|
||||
val subscriptionDetails: ProductDetails = productDetails[0]
|
||||
val offerToken = subscriptionDetails.subscriptionOfferDetails?.firstOrNull()
|
||||
if (offerToken == null) {
|
||||
Log.w(TAG, "No offer tokens available on subscription product! Cancelling billing flow launch.")
|
||||
return null
|
||||
}
|
||||
|
||||
val productDetailParamsList = listOf(
|
||||
ProductDetailsParams.newBuilder()
|
||||
.setProductDetails(subscriptionDetails)
|
||||
.setOfferToken(offerToken.offerToken)
|
||||
.build()
|
||||
)
|
||||
|
||||
val billingFlowParams = BillingFlowParams.newBuilder()
|
||||
.setProductDetailsParamsList(productDetailParamsList)
|
||||
.build()
|
||||
|
||||
return doOnConnectionReady {
|
||||
withContext(Dispatchers.Main) {
|
||||
billingClient.launchBillingFlow(activity, billingFlowParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not subscriptions are supported by a user's device. Lack of subscription support is generally due
|
||||
* to out-of-date Google Play API
|
||||
|
|
Loading…
Add table
Reference in a new issue