Skip to content

Commit 7305860

Browse files
Merge pull request #39 from cafebazaar/1.0.0-beta01
Prepare for release 1.0.0-beta01
2 parents 07f7bd9 + de45039 commit 7305860

16 files changed

Lines changed: 927 additions & 215 deletions

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#Fri Sep 27 23:39:31 IRST 2019
1+
#Sat Dec 26 11:51:48 IRST 2020
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
44
zipStoreBase=GRADLE_USER_HOME
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
22
package="ir.cafebazaar.poolakey">
33

4+
<queries>
5+
<package android:name="com.farsitel.bazaar" />
6+
</queries>
7+
48
<uses-permission android:name="com.farsitel.bazaar.permission.PAY_THROUGH_BAZAAR" />
59

10+
<application>
11+
<receiver android:name=".receiver.BillingReceiver">
12+
<intent-filter>
13+
<action android:name="com.farsitel.bazaar.purchase" />
14+
<action android:name="com.farsitel.bazaar.billingSupport" />
15+
<action android:name="com.farsitel.bazaar.consume" />
16+
<action android:name="com.farsitel.bazaar.getPurchase" />
17+
</intent-filter>
18+
</receiver>
19+
</application>
620
</manifest>
Lines changed: 79 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -1,258 +1,155 @@
11
package ir.cafebazaar.poolakey
22

33
import android.app.Activity
4-
import android.content.ComponentName
54
import android.content.Context
6-
import android.content.Intent
7-
import android.content.IntentSender
8-
import android.content.ServiceConnection
9-
import android.os.DeadObjectException
10-
import android.os.IBinder
115
import androidx.fragment.app.Fragment
12-
import com.android.vending.billing.IInAppBillingService
13-
import ir.cafebazaar.poolakey.billing.BillingFunction
14-
import ir.cafebazaar.poolakey.billing.consume.ConsumeFunctionRequest
15-
import ir.cafebazaar.poolakey.billing.purchase.PurchaseFunctionRequest
16-
import ir.cafebazaar.poolakey.billing.query.QueryFunctionRequest
6+
import ir.cafebazaar.poolakey.billing.connection.BillingConnectionCommunicator
7+
import ir.cafebazaar.poolakey.billing.connection.ReceiverBillingConnection
8+
import ir.cafebazaar.poolakey.billing.connection.ServiceBillingConnection
9+
import ir.cafebazaar.poolakey.billing.query.QueryFunction
1710
import ir.cafebazaar.poolakey.callback.ConnectionCallback
1811
import ir.cafebazaar.poolakey.callback.ConsumeCallback
1912
import ir.cafebazaar.poolakey.callback.PurchaseIntentCallback
2013
import ir.cafebazaar.poolakey.callback.PurchaseQueryCallback
2114
import ir.cafebazaar.poolakey.config.PaymentConfiguration
22-
import ir.cafebazaar.poolakey.constant.BazaarIntent
23-
import ir.cafebazaar.poolakey.constant.Billing
24-
import ir.cafebazaar.poolakey.constant.Const
25-
import ir.cafebazaar.poolakey.exception.BazaarNotFoundException
26-
import ir.cafebazaar.poolakey.exception.DisconnectException
27-
import ir.cafebazaar.poolakey.exception.IAPNotSupportedException
28-
import ir.cafebazaar.poolakey.exception.SubsNotSupportedException
2915
import ir.cafebazaar.poolakey.request.PurchaseRequest
30-
import ir.cafebazaar.poolakey.security.Security
3116
import ir.cafebazaar.poolakey.thread.PoolakeyThread
3217

3318
internal class BillingConnection(
3419
private val context: Context,
3520
private val paymentConfiguration: PaymentConfiguration,
3621
private val backgroundThread: PoolakeyThread<Runnable>,
37-
private val purchaseFunction: BillingFunction<PurchaseFunctionRequest>,
38-
private val consumeFunction: BillingFunction<ConsumeFunctionRequest>,
39-
private val queryFunction: BillingFunction<QueryFunctionRequest>
40-
) : ServiceConnection {
22+
private val queryFunction: QueryFunction,
23+
private val mainThread: PoolakeyThread<() -> Unit>
24+
) {
4125

4226
private var callback: ConnectionCallback? = null
4327

44-
private var billingService: IInAppBillingService? = null
28+
private var billingCommunicator: BillingConnectionCommunicator? = null
4529

4630
internal fun startConnection(connectionCallback: ConnectionCallback.() -> Unit): Connection {
4731
callback = ConnectionCallback(disconnect = ::stopConnection).apply(connectionCallback)
48-
Intent(BILLING_SERVICE_ACTION).apply { `package` = Const.BAZAAR_PACKAGE_NAME }
49-
.takeIf(
50-
thisIsTrue = {
51-
context.packageManager.queryIntentServices(it, 0).isNullOrEmpty().not()
52-
},
53-
andIfNot = {
54-
callback?.connectionFailed?.invoke(BazaarNotFoundException())
55-
}
56-
)?.takeIf(
57-
thisIsTrue = {
58-
Security.verifyBazaarIsInstalled(context)
59-
},
60-
andIfNot = {
61-
callback?.connectionFailed?.invoke(BazaarNotFoundException())
62-
}
63-
)?.also {
64-
try {
65-
context.bindService(it, this, Context.BIND_AUTO_CREATE)
66-
} catch (e: SecurityException) {
67-
callback?.connectionFailed?.invoke(e)
68-
}
69-
}
70-
return requireNotNull(callback)
71-
}
7232

73-
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
74-
try {
75-
IInAppBillingService.Stub.asInterface(service)
76-
?.also { billingService = it }
77-
?.takeIf(
78-
thisIsTrue = {
79-
isPurchaseTypeSupported(purchaseType = PurchaseType.IN_APP)
80-
},
81-
andIfNot = {
82-
callback?.connectionFailed?.invoke(IAPNotSupportedException())
83-
}
84-
)
85-
?.takeIf(
86-
thisIsTrue = {
87-
!paymentConfiguration.shouldSupportSubscription || isPurchaseTypeSupported(
88-
purchaseType = PurchaseType.SUBSCRIPTION
89-
)
90-
},
91-
andIfNot = {
92-
callback?.connectionFailed?.invoke(SubsNotSupportedException())
93-
}
94-
)
95-
?.also { callback?.connectionSucceed?.invoke() }
96-
} catch (ignored: DeadObjectException) {
97-
// onServiceDisconnected will get called so no need to do anything
98-
}
99-
}
33+
val serviceCommunicator = ServiceBillingConnection(
34+
context,
35+
mainThread,
36+
backgroundThread,
37+
paymentConfiguration,
38+
queryFunction,
39+
::disconnect
40+
)
10041

101-
private fun isPurchaseTypeSupported(
102-
purchaseType: PurchaseType
103-
): Boolean {
104-
val supportState = billingService?.isBillingSupported(
105-
Billing.IN_APP_BILLING_VERSION,
106-
context.packageName,
107-
purchaseType.type
42+
val receiverConnection = ReceiverBillingConnection(
43+
paymentConfiguration,
44+
queryFunction
10845
)
109-
return supportState == BazaarIntent.RESPONSE_RESULT_OK
110-
}
11146

112-
fun purchase(
113-
activity: Activity,
114-
purchaseRequest: PurchaseRequest,
115-
purchaseType: PurchaseType,
116-
callback: PurchaseIntentCallback.() -> Unit
117-
) {
47+
val canConnect = serviceCommunicator.startConnection(context, requireNotNull(callback))
11848

119-
val intentSenderFire: (IntentSender) -> Unit = { intentSender ->
120-
activity.startIntentSenderForResult(
121-
intentSender,
122-
purchaseRequest.requestCode,
123-
Intent(),
124-
0,
125-
0,
126-
0
49+
billingCommunicator = if (canConnect) {
50+
serviceCommunicator
51+
} else {
52+
receiverConnection.startConnection(
53+
context,
54+
requireNotNull(callback)
12755
)
128-
PurchaseIntentCallback().apply(callback).purchaseFlowBegan.invoke()
129-
}
13056

131-
val intentFire: (Intent) -> Unit = { intent ->
132-
activity.startActivityForResult(
133-
intent,
134-
purchaseRequest.requestCode
135-
)
136-
PurchaseIntentCallback().apply(callback).purchaseFlowBegan.invoke()
57+
receiverConnection
13758
}
138-
139-
purchase(purchaseRequest, purchaseType, callback, intentSenderFire, intentFire)
59+
return requireNotNull(callback)
14060
}
14161

14262
fun purchase(
143-
fragment: Fragment,
63+
activity: Activity,
14464
purchaseRequest: PurchaseRequest,
14565
purchaseType: PurchaseType,
14666
callback: PurchaseIntentCallback.() -> Unit
14767
) {
148-
val intentSenderFire: (IntentSender) -> Unit = { intentSender ->
149-
fragment.startIntentSenderForResult(
150-
intentSender,
151-
purchaseRequest.requestCode,
152-
Intent(),
153-
0,
154-
0,
155-
0,
156-
null
157-
)
158-
PurchaseIntentCallback().apply(callback).purchaseFlowBegan.invoke()
159-
}
160-
161-
val intentFire: (Intent) -> Unit = { intent ->
162-
fragment.startActivityForResult(
163-
intent,
164-
purchaseRequest.requestCode
68+
runOnCommunicator(TAG_PURCHASE) {
69+
requireNotNull(billingCommunicator).purchase(
70+
activity,
71+
purchaseRequest,
72+
purchaseType,
73+
callback
16574
)
166-
PurchaseIntentCallback().apply(callback).purchaseFlowBegan.invoke()
16775
}
168-
169-
purchase(purchaseRequest, purchaseType, callback, intentSenderFire, intentFire)
17076
}
17177

172-
private fun purchase(
78+
fun purchase(
79+
fragment: Fragment,
17380
purchaseRequest: PurchaseRequest,
17481
purchaseType: PurchaseType,
175-
callback: PurchaseIntentCallback.() -> Unit,
176-
fireIntentSender: (IntentSender) -> Unit,
177-
fireIntent: (Intent) -> Unit
178-
) = withService {
179-
purchaseFunction.function(
180-
billingService = this,
181-
request = PurchaseFunctionRequest(
82+
callback: PurchaseIntentCallback.() -> Unit
83+
) {
84+
runOnCommunicator(TAG_PURCHASE) {
85+
requireNotNull(billingCommunicator).purchase(
86+
fragment,
18287
purchaseRequest,
18388
purchaseType,
184-
callback,
185-
fireIntentSender,
186-
fireIntent
89+
callback
18790
)
188-
)
189-
} ifServiceIsDisconnected {
190-
PurchaseIntentCallback().apply(callback).failedToBeginFlow.invoke(DisconnectException())
91+
}
19192
}
19293

19394
fun consume(
19495
purchaseToken: String,
19596
callback: ConsumeCallback.() -> Unit
196-
) = withService(runOnBackground = true) {
197-
consumeFunction.function(
198-
billingService = this,
199-
request = ConsumeFunctionRequest(purchaseToken, callback)
200-
)
201-
} ifServiceIsDisconnected {
202-
ConsumeCallback().apply(callback).consumeFailed.invoke(DisconnectException())
97+
) {
98+
runOnCommunicator(TAG_CONSUME) {
99+
requireNotNull(billingCommunicator).consume(
100+
purchaseToken,
101+
callback
102+
)
103+
}
203104
}
204105

205106
fun queryPurchasedProducts(
206107
purchaseType: PurchaseType,
207108
callback: PurchaseQueryCallback.() -> Unit
208-
) = withService(runOnBackground = true) {
209-
queryFunction.function(
210-
billingService = this,
211-
request = QueryFunctionRequest(purchaseType, callback)
212-
)
213-
} ifServiceIsDisconnected {
214-
PurchaseQueryCallback().apply(callback).queryFailed.invoke(DisconnectException())
109+
) {
110+
runOnCommunicator(TAG_QUERY_PURCHASE_PRODUCT) {
111+
requireNotNull(billingCommunicator).queryPurchasedProducts(
112+
purchaseType,
113+
callback
114+
)
115+
}
215116
}
216117

217118
private fun stopConnection() {
218-
if (billingService != null) {
219-
context.unbindService(this)
119+
runOnCommunicator(TAG_STOP_CONNECTION) {
120+
requireNotNull(billingCommunicator).stopConnection()
220121
disconnect()
221122
}
222123
}
223124

224-
override fun onServiceDisconnected(name: ComponentName?) {
225-
disconnect()
226-
}
227-
228125
private fun disconnect() {
229-
billingService = null
230126
callback?.disconnected?.invoke()
231127
callback = null
232128
backgroundThread.dispose()
129+
billingCommunicator = null
233130
}
234131

235-
private inline fun withService(
236-
runOnBackground: Boolean = false,
237-
crossinline service: IInAppBillingService.() -> Unit
238-
): ConnectionState {
239-
return billingService?.also {
240-
if (runOnBackground) {
241-
backgroundThread.execute(Runnable { service.invoke(it) })
242-
} else {
243-
service.invoke(it)
244-
}
245-
}?.let { ConnectionState.Connected }
246-
?: run { ConnectionState.Disconnected }
132+
private fun runOnCommunicator(
133+
methodName: String,
134+
ifConnected: () -> Unit
135+
) {
136+
if (billingCommunicator == null) {
137+
raiseErrorForCommunicatorNotInitialized(methodName)
138+
} else {
139+
ifConnected.invoke()
140+
}
247141
}
248142

249-
private inline infix fun ConnectionState.ifServiceIsDisconnected(block: () -> Unit) {
250-
if (this is ConnectionState.Disconnected) {
251-
block.invoke()
252-
}
143+
private fun raiseErrorForCommunicatorNotInitialized(methodName: String) {
144+
callback?.connectionFailed?.invoke(
145+
IllegalStateException("You called $methodName but communicator is not initialized yet")
146+
)
253147
}
254148

255149
companion object {
256-
private const val BILLING_SERVICE_ACTION = "ir.cafebazaar.pardakht.InAppBillingService.BIND"
150+
private const val TAG_STOP_CONNECTION = "stopConnection"
151+
private const val TAG_QUERY_PURCHASE_PRODUCT = "queryPurchasedProducts"
152+
private const val TAG_CONSUME = "consume"
153+
private const val TAG_PURCHASE = "purchase"
257154
}
258-
}
155+
}

poolakey/src/main/java/ir/cafebazaar/poolakey/PackageManager.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,13 @@ internal fun getPackageInfo(context: Context, packageName: String): PackageInfo?
88
packageManager.getPackageInfo(packageName, 0)
99
} catch (ignored: Exception) {
1010
null
11+
}
12+
13+
@Suppress("DEPRECATION")
14+
internal fun sdkAwareVersionCode(packageInfo: PackageInfo): Long {
15+
return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
16+
packageInfo.longVersionCode
17+
} else {
18+
packageInfo.versionCode.toLong()
19+
}
1120
}

0 commit comments

Comments
 (0)