From 6c71692421aea2ed710360765f2e767ad0d61719 Mon Sep 17 00:00:00 2001 From: baset Date: Thu, 28 Dec 2023 13:13:50 +0330 Subject: [PATCH 1/3] Fix issue of call connectionFailed callback multiple times. --- .../cafebazaar/poolakey/BillingConnection.kt | 27 +++++++----- .../poolakey/ConnectionRequestResult.kt | 6 +++ .../BillingConnectionCommunicator.kt | 3 +- .../connection/ReceiverBillingConnection.kt | 43 ++++++++++--------- .../connection/ServiceBillingConnection.kt | 36 ++++++++-------- 5 files changed, 64 insertions(+), 51 deletions(-) create mode 100644 poolakey/src/main/java/ir/cafebazaar/poolakey/ConnectionRequestResult.kt diff --git a/poolakey/src/main/java/ir/cafebazaar/poolakey/BillingConnection.kt b/poolakey/src/main/java/ir/cafebazaar/poolakey/BillingConnection.kt index e21c2f9..00f7ccd 100644 --- a/poolakey/src/main/java/ir/cafebazaar/poolakey/BillingConnection.kt +++ b/poolakey/src/main/java/ir/cafebazaar/poolakey/BillingConnection.kt @@ -2,8 +2,6 @@ package ir.cafebazaar.poolakey import android.app.Activity import android.content.Context -import android.os.Build -import android.os.Build.VERSION.SDK_INT import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResultRegistry @@ -60,17 +58,24 @@ internal class BillingConnection( queryFunction ) - val canConnect = serviceCommunicator.startConnection(context, requireNotNull(callback)) + val serviceConnectRequestResult = + serviceCommunicator.startConnection(context, requireNotNull(callback)) - billingCommunicator = if (canConnect) { - serviceCommunicator - } else { - receiverConnection.startConnection( - context, - requireNotNull(callback) - ) + billingCommunicator = when { + serviceConnectRequestResult.canConnect -> serviceCommunicator + serviceConnectRequestResult.canUseFallback -> { + val receiverConnectRequestResult = receiverConnection.startConnection( + context, + requireNotNull(callback) + ) + if (receiverConnectRequestResult.canConnect) { + receiverConnection + } else { + null + } + } - receiverConnection + else -> null } return requireNotNull(callback) } diff --git a/poolakey/src/main/java/ir/cafebazaar/poolakey/ConnectionRequestResult.kt b/poolakey/src/main/java/ir/cafebazaar/poolakey/ConnectionRequestResult.kt new file mode 100644 index 0000000..c23f715 --- /dev/null +++ b/poolakey/src/main/java/ir/cafebazaar/poolakey/ConnectionRequestResult.kt @@ -0,0 +1,6 @@ +package ir.cafebazaar.poolakey + +internal data class ConnectionRequestResult( + val canConnect: Boolean, + val canUseFallback: Boolean = true +) diff --git a/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/BillingConnectionCommunicator.kt b/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/BillingConnectionCommunicator.kt index f338dab..9165365 100644 --- a/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/BillingConnectionCommunicator.kt +++ b/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/BillingConnectionCommunicator.kt @@ -1,6 +1,7 @@ package ir.cafebazaar.poolakey.billing.connection import android.content.Context +import ir.cafebazaar.poolakey.ConnectionRequestResult import ir.cafebazaar.poolakey.PurchaseType import ir.cafebazaar.poolakey.PaymentLauncher import ir.cafebazaar.poolakey.billing.skudetail.SkuDetailFunctionRequest @@ -18,7 +19,7 @@ internal interface BillingConnectionCommunicator { fun startConnection( context: Context, callback: ConnectionCallback - ): Boolean + ): ConnectionRequestResult fun consume( purchaseToken: String, diff --git a/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ReceiverBillingConnection.kt b/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ReceiverBillingConnection.kt index 8142af8..7c060a9 100644 --- a/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ReceiverBillingConnection.kt +++ b/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ReceiverBillingConnection.kt @@ -3,6 +3,7 @@ package ir.cafebazaar.poolakey.billing.connection import android.content.Context import android.content.Intent import android.os.Bundle +import ir.cafebazaar.poolakey.ConnectionRequestResult import ir.cafebazaar.poolakey.PurchaseType import ir.cafebazaar.poolakey.PaymentLauncher import ir.cafebazaar.poolakey.billing.Feature @@ -64,33 +65,35 @@ internal class ReceiverBillingConnection( private var purchaseWeakReference: WeakReference? = null - override fun startConnection(context: Context, callback: ConnectionCallback): Boolean { + override fun startConnection( + context: Context, + callback: ConnectionCallback + ): ConnectionRequestResult { connectionCallbackReference = WeakReference(callback) contextReference = WeakReference(context) - if (!Security.verifyBazaarIsInstalled(context)) { - return false - } + if (Security.verifyBazaarIsInstalled(context)) { + bazaarVersionCode = getPackageInfo(context, BAZAAR_PACKAGE_NAME)?.let { + sdkAwareVersionCode(it) + } ?: 0L + + return when { + canConnectWithReceiverComponent() -> { + createReceiverConnection() + registerBroadcast() + isPurchaseTypeSupported() + ConnectionRequestResult(true) + } - bazaarVersionCode = getPackageInfo(context, BAZAAR_PACKAGE_NAME)?.let { - sdkAwareVersionCode(it) - } ?: 0L + bazaarVersionCode > 0 -> { + callback.connectionFailed.invoke(BazaarNotSupportedException()) + ConnectionRequestResult(false) + } - return when { - canConnectWithReceiverComponent() -> { - createReceiverConnection() - registerBroadcast() - isPurchaseTypeSupported() - true - } - bazaarVersionCode > 0 -> { - callback.connectionFailed.invoke(BazaarNotSupportedException()) - false - } - else -> { - false + else -> ConnectionRequestResult(false) } } + return ConnectionRequestResult(canConnect = false, canUseFallback = false) } private fun canConnectWithReceiverComponent(): Boolean { diff --git a/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ServiceBillingConnection.kt b/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ServiceBillingConnection.kt index 89a95fc..5cf37e0 100644 --- a/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ServiceBillingConnection.kt +++ b/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ServiceBillingConnection.kt @@ -6,12 +6,12 @@ import android.content.Intent import android.content.IntentSender import android.content.ServiceConnection import android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS -import android.os.Build import android.os.Bundle import android.os.IBinder import android.os.RemoteException import androidx.activity.result.IntentSenderRequest import com.android.vending.billing.IInAppBillingService +import ir.cafebazaar.poolakey.ConnectionRequestResult import ir.cafebazaar.poolakey.ConnectionState import ir.cafebazaar.poolakey.PurchaseType import ir.cafebazaar.poolakey.PaymentLauncher @@ -65,34 +65,32 @@ internal class ServiceBillingConnection( private var callbackReference: WeakReference? = null private var contextReference: WeakReference? = null - override fun startConnection(context: Context, callback: ConnectionCallback): Boolean { + override fun startConnection( + context: Context, + callback: ConnectionCallback + ): ConnectionRequestResult { callbackReference = WeakReference(callback) contextReference = WeakReference(context) - - return Intent(BILLING_SERVICE_ACTION).apply { - `package` = BAZAAR_PACKAGE_NAME - setClassName(BAZAAR_PACKAGE_NAME, BAZAAR_PAYMENT_SERVICE_CLASS_NAME) - } - .takeIf( + if (Security.verifyBazaarIsInstalled(context)) { + Intent(BILLING_SERVICE_ACTION).apply { + `package` = BAZAAR_PACKAGE_NAME + setClassName(BAZAAR_PACKAGE_NAME, BAZAAR_PAYMENT_SERVICE_CLASS_NAME) + }.takeIf( thisIsTrue = ::isServiceAvailable, andIfNot = { callback.connectionFailed.invoke(BazaarNotFoundException()) - } - )?.takeIf( - thisIsTrue = { - Security.verifyBazaarIsInstalled(context) - }, - andIfNot = { - callback.connectionFailed.invoke(BazaarNotFoundException()) + return ConnectionRequestResult(canConnect = false) } )?.let { - try { - context.bindService(it, this, Context.BIND_AUTO_CREATE) + return try { + ConnectionRequestResult(context.bindService(it, this, Context.BIND_AUTO_CREATE)) } catch (e: SecurityException) { callback.connectionFailed.invoke(e) - false + ConnectionRequestResult(false) } - } ?: false + } + } + return ConnectionRequestResult(canConnect = false, canUseFallback = false) } override fun onServiceConnected(name: ComponentName?, service: IBinder?) { From e04bfd8cfc0331cb586edb7d609f36051156829b Mon Sep 17 00:00:00 2001 From: baset Date: Thu, 28 Dec 2023 13:15:43 +0330 Subject: [PATCH 2/3] Convert sdk int check functions to top level functions. --- .../java/ir/cafebazaar/poolakey/BillingConnection.kt | 2 +- .../main/java/ir/cafebazaar/poolakey/PackageManager.kt | 9 +++++++-- .../billing/connection/ServiceBillingConnection.kt | 3 ++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/poolakey/src/main/java/ir/cafebazaar/poolakey/BillingConnection.kt b/poolakey/src/main/java/ir/cafebazaar/poolakey/BillingConnection.kt index 00f7ccd..b253cc2 100644 --- a/poolakey/src/main/java/ir/cafebazaar/poolakey/BillingConnection.kt +++ b/poolakey/src/main/java/ir/cafebazaar/poolakey/BillingConnection.kt @@ -90,7 +90,7 @@ internal class BillingConnection( onActivityResult(it, purchaseCallback) }.build() - purchaseRequest.cutoutModeIsShortEdges = if (SDK_INT >= Build.VERSION_CODES.P) { + purchaseRequest.cutoutModeIsShortEdges = if (isSdkPieAndUp()) { (context as? Activity) ?.window ?.attributes diff --git a/poolakey/src/main/java/ir/cafebazaar/poolakey/PackageManager.kt b/poolakey/src/main/java/ir/cafebazaar/poolakey/PackageManager.kt index 79a2533..4bce3b5 100644 --- a/poolakey/src/main/java/ir/cafebazaar/poolakey/PackageManager.kt +++ b/poolakey/src/main/java/ir/cafebazaar/poolakey/PackageManager.kt @@ -2,6 +2,7 @@ package ir.cafebazaar.poolakey import android.content.Context import android.content.pm.PackageInfo +import android.os.Build internal fun getPackageInfo(context: Context, packageName: String): PackageInfo? = try { val packageManager = context.packageManager @@ -12,9 +13,13 @@ internal fun getPackageInfo(context: Context, packageName: String): PackageInfo? @Suppress("DEPRECATION") internal fun sdkAwareVersionCode(packageInfo: PackageInfo): Long { - return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { + return if (isSdkPieAndUp()) { packageInfo.longVersionCode } else { packageInfo.versionCode.toLong() } -} \ No newline at end of file +} + +internal fun isSdkNougatAndUp() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N + +internal fun isSdkPieAndUp() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P \ No newline at end of file diff --git a/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ServiceBillingConnection.kt b/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ServiceBillingConnection.kt index 5cf37e0..ed85443 100644 --- a/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ServiceBillingConnection.kt +++ b/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ServiceBillingConnection.kt @@ -40,6 +40,7 @@ import ir.cafebazaar.poolakey.exception.BazaarNotFoundException import ir.cafebazaar.poolakey.exception.DisconnectException import ir.cafebazaar.poolakey.exception.IAPNotSupportedException import ir.cafebazaar.poolakey.exception.SubsNotSupportedException +import ir.cafebazaar.poolakey.isSdkNougatAndUp import ir.cafebazaar.poolakey.request.PurchaseRequest import ir.cafebazaar.poolakey.security.Security import ir.cafebazaar.poolakey.takeIf @@ -286,7 +287,7 @@ internal class ServiceBillingConnection( } private fun isServiceAvailableInDeepSleep(intent: Intent): Boolean { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && + return isSdkNougatAndUp() && context.packageManager .queryIntentServices(intent, MATCH_DISABLED_COMPONENTS) .isNotEmpty() From 4df41013ffab1fca5fd6b83873dddb1400984f32 Mon Sep 17 00:00:00 2001 From: baset Date: Thu, 28 Dec 2023 13:37:40 +0330 Subject: [PATCH 3/3] Fix mistake of call callbacks. --- .../poolakey/billing/connection/ReceiverBillingConnection.kt | 4 +++- .../poolakey/billing/connection/ServiceBillingConnection.kt | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ReceiverBillingConnection.kt b/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ReceiverBillingConnection.kt index 7c060a9..7ed486e 100644 --- a/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ReceiverBillingConnection.kt +++ b/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ReceiverBillingConnection.kt @@ -28,6 +28,7 @@ import ir.cafebazaar.poolakey.constant.BazaarIntent import ir.cafebazaar.poolakey.constant.BazaarIntent.REQUEST_SKU_DETAILS_LIST import ir.cafebazaar.poolakey.constant.Billing import ir.cafebazaar.poolakey.constant.Const.BAZAAR_PACKAGE_NAME +import ir.cafebazaar.poolakey.exception.BazaarNotFoundException import ir.cafebazaar.poolakey.exception.BazaarNotSupportedException import ir.cafebazaar.poolakey.exception.ConsumeFailedException import ir.cafebazaar.poolakey.exception.DisconnectException @@ -87,12 +88,13 @@ internal class ReceiverBillingConnection( bazaarVersionCode > 0 -> { callback.connectionFailed.invoke(BazaarNotSupportedException()) - ConnectionRequestResult(false) + ConnectionRequestResult(canConnect = false, canUseFallback = false) } else -> ConnectionRequestResult(false) } } + callback.connectionFailed.invoke(BazaarNotFoundException()) return ConnectionRequestResult(canConnect = false, canUseFallback = false) } diff --git a/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ServiceBillingConnection.kt b/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ServiceBillingConnection.kt index ed85443..b6d531f 100644 --- a/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ServiceBillingConnection.kt +++ b/poolakey/src/main/java/ir/cafebazaar/poolakey/billing/connection/ServiceBillingConnection.kt @@ -80,7 +80,7 @@ internal class ServiceBillingConnection( thisIsTrue = ::isServiceAvailable, andIfNot = { callback.connectionFailed.invoke(BazaarNotFoundException()) - return ConnectionRequestResult(canConnect = false) + return ConnectionRequestResult(canConnect = false, canUseFallback = false) } )?.let { return try { @@ -91,6 +91,7 @@ internal class ServiceBillingConnection( } } } + callback.connectionFailed.invoke(BazaarNotFoundException()) return ConnectionRequestResult(canConnect = false, canUseFallback = false) }