Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for Amazon purchase dialog not showing up #552

Merged
merged 42 commits into from
Jul 1, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
3de0b41
adds ProxyAmazonBillingActivity
vegaro Jun 24, 2022
a11a295
remove finish()
vegaro Jun 24, 2022
619967e
implementation using broadcast receiver
vegaro Jun 27, 2022
4b6faea
fix for status bar
vegaro Jun 27, 2022
0e93706
fix AmazonBillingTest
vegaro Jun 29, 2022
e8c5a3e
fixes more tests
vegaro Jun 29, 2022
5aa5306
savedInstanceState
vegaro Jun 29, 2022
7d9a3fa
refactor to pass purchasingserviceprovider so we can test
vegaro Jun 29, 2022
51c425b
more refactor
vegaro Jun 30, 2022
94d85a6
more tests
vegaro Jun 30, 2022
e1d7863
fix PurchaseHandlerTest
vegaro Jun 30, 2022
7792135
missing dependencies
vegaro Jun 30, 2022
bb5cfeb
Merge branch 'main' into amazon-proxy-billing-activity
vegaro Jun 30, 2022
6bef4de
adds missing dependency
vegaro Jun 30, 2022
03cbd27
adds VisibleForTesting
vegaro Jun 30, 2022
72659e3
removes explicit exported and theme in AndroidManifest
vegaro Jun 30, 2022
4cde5a3
Move createRequestIdResultReceiver to startProxyActivity
vegaro Jun 30, 2022
8a32ed4
remove nullability of context and intent in onreceive
vegaro Jun 30, 2022
dfdc9a4
fix test
vegaro Jun 30, 2022
90f9631
replace other instances of verifyActivityIsStartedAndFakeRequestId an…
vegaro Jun 30, 2022
885b343
extracts creation of intent
vegaro Jun 30, 2022
4fc2c9c
downgrades annotation to 1.3.0 because it's failing compilation
vegaro Jun 30, 2022
177c964
extracts EXTRAS_REQUEST_ID as constant
vegaro Jun 30, 2022
bafef41
moves intent filter creation to ProxyAmazonBillingActivityBroadcastRe…
vegaro Jun 30, 2022
2516c89
remove onReceiveCalled test
vegaro Jun 30, 2022
9bd8e0f
create ProxyAmazonBillingHelper
vegaro Jun 30, 2022
72672ac
move newPurchaseFinishedIntent to ProxyAmazonBillingActivityBroadcast…
vegaro Jun 30, 2022
2cbd49c
creates WeakReference of Activity
vegaro Jun 30, 2022
28ee907
rename to ProxyAmazonBillingDelegate and moves logic there
vegaro Jun 30, 2022
0b147e4
add error logging
vegaro Jun 30, 2022
3cf3048
makes packagename an argument
vegaro Jul 1, 2022
3d03404
companion object moved to top
vegaro Jul 1, 2022
3ed4185
makes proxyAmazonBillingDelegate visiblefortesting
vegaro Jul 1, 2022
22a4aa3
changes activity test to only test creation of delegate
vegaro Jul 1, 2022
9a17cb2
ProxyAmazonBillingActivityTest fixed
vegaro Jul 1, 2022
11dad87
ProxyAmazonBillingDelegateTest
vegaro Jul 1, 2022
bfed36f
fixes test
vegaro Jul 1, 2022
a9f6872
registers broadcast in activity context instead of activity
vegaro Jul 1, 2022
f8bb051
Remove clearAllMocks
vegaro Jul 1, 2022
252d825
move to package and another clearAllMocks
vegaro Jul 1, 2022
0872ebb
adds theme back because it broke the transparency
vegaro Jul 1, 2022
f641be4
Merge branch 'main' into amazon-proxy-billing-activity
vegaro Jul 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions feature/amazon/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@
package="com.revenuecat.purchases.amazon">

<application>
<receiver android:name = "com.amazon.device.iap.ResponseReceiver"
android:permission = "com.amazon.inapp.purchasing.Permission.NOTIFY"
android:exported = "true">
<receiver
android:name="com.amazon.device.iap.ResponseReceiver"
android:exported="true"
android:permission="com.amazon.inapp.purchasing.Permission.NOTIFY">
<intent-filter>
<action android:name = "com.amazon.inapp.purchasing.NOTIFY" />
<action android:name="com.amazon.inapp.purchasing.NOTIFY" />
</intent-filter>
</receiver>
<activity
android:name=".ProxyAmazonBillingActivity"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:exported="false"
vegaro marked this conversation as resolved.
Show resolved Hide resolved
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
vegaro marked this conversation as resolved.
Show resolved Hide resolved
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,19 @@ internal class AmazonBilling constructor(
executeRequestOnUIThread { connectionError ->
if (connectionError == null) {
purchaseHandler.purchase(
activity,
appUserID,
storeProduct,
presentedOfferingIdentifier,
onPurchaseCompleted = {
purchaseHandler.onPurchaseCompleted(activity)
tonidero marked this conversation as resolved.
Show resolved Hide resolved
},
onSuccess = { receipt, userData ->
handleReceipt(receipt, userData, storeProduct, presentedOfferingIdentifier)
},
onError = ::onPurchaseError
onError = {
onPurchaseError(it)
}
)
} else {
onPurchaseError(connectionError)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package com.revenuecat.purchases.amazon

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.ResultReceiver
import com.amazon.device.iap.PurchasingListener
import com.amazon.device.iap.PurchasingService
import com.amazon.device.iap.model.FulfillmentResult
import com.amazon.device.iap.model.RequestId

const val PROXY_AMAZON_BILLING_ACTIVITY_REQUEST_CODE = 192342
tonidero marked this conversation as resolved.
Show resolved Hide resolved
class DefaultPurchasingServiceProvider : PurchasingServiceProvider {

override fun registerListener(
Expand All @@ -20,9 +24,18 @@ class DefaultPurchasingServiceProvider : PurchasingServiceProvider {
}

override fun purchase(
sku: String
): RequestId {
return PurchasingService.purchase(sku)
activity: Activity,
sku: String,
resultReceiver: ResultReceiver
) {
val intent = Intent(activity, ProxyAmazonBillingActivity::class.java)
intent.putExtra("result_receiver", resultReceiver)
intent.putExtra("sku", sku)
activity.startActivityForResult(intent, PROXY_AMAZON_BILLING_ACTIVITY_REQUEST_CODE)
}

override fun onPurchaseCompleted(activity: Activity) {
activity.finishActivity(PROXY_AMAZON_BILLING_ACTIVITY_REQUEST_CODE)
}

override fun getProductData(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.revenuecat.purchases.amazon

import android.app.Activity
import android.os.Bundle
import android.os.ResultReceiver
import com.amazon.device.iap.PurchasingService

class ProxyAmazonBillingActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val sku = intent.getStringExtra("sku")
val resultReceiver = intent.getParcelableExtra("result_receiver") as? ResultReceiver

val requestId = PurchasingService.purchase(sku)

val bundle = Bundle().apply {
putParcelable("request_id", requestId)
}
resultReceiver?.send(0, bundle)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.revenuecat.purchases.amazon

import android.app.Activity
import android.content.Context
import android.os.ResultReceiver
import com.amazon.device.iap.PurchasingListener
import com.amazon.device.iap.model.FulfillmentResult
import com.amazon.device.iap.model.RequestId
Expand All @@ -10,7 +12,8 @@ interface PurchasingServiceProvider {
fun registerListener(context: Context, listener: PurchasingListener)
fun getProductData(skus: Set<String>): RequestId
fun getUserData(): RequestId
fun purchase(sku: String): RequestId
fun purchase(activity: Activity, sku: String, resultReceiver: ResultReceiver)
fun getPurchaseUpdates(reset: Boolean): RequestId
fun notifyFulfillment(receiptId: String, fulfillmentResult: FulfillmentResult)
fun onPurchaseCompleted(activity: Activity)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.revenuecat.purchases.amazon.handler

import android.app.Activity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.ResultReceiver
import com.amazon.device.iap.model.PurchaseResponse
import com.amazon.device.iap.model.Receipt
import com.amazon.device.iap.model.RequestId
Expand All @@ -24,30 +29,50 @@ class PurchaseHandler(
private val presentedOfferingsByProductIdentifier = mutableMapOf<String, String?>()
private val purchaseCallbacks =
mutableMapOf<RequestId, Pair<(Receipt, UserData) -> Unit, (PurchasesError) -> Unit>>()
private val purchaseCompletedCallbacks = mutableMapOf<RequestId, () -> Unit>()

override fun purchase(
activity: Activity,
appUserID: String,
storeProduct: StoreProduct,
presentedOfferingIdentifier: String?,
onPurchaseCompleted: () -> Unit,
onSuccess: (Receipt, UserData) -> Unit,
onError: (PurchasesError) -> Unit
) {
log(LogIntent.PURCHASE, PurchaseStrings.PURCHASING_PRODUCT.format(storeProduct.sku))

val requestId = purchasingServiceProvider.purchase(storeProduct.sku)
synchronized(this@PurchaseHandler) {
purchaseCallbacks[requestId] = onSuccess to onError
productTypes[storeProduct.sku] = storeProduct.type
presentedOfferingsByProductIdentifier[storeProduct.sku] = presentedOfferingIdentifier
val resultReceiver = object : ResultReceiver(Handler(Looper.getMainLooper())) {
override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
synchronized(this@PurchaseHandler) {
val requestId = resultData?.get("request_id") as? RequestId
if (requestId != null) {
purchaseCompletedCallbacks[requestId] = onPurchaseCompleted
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this suggests that we support having multiple purchase requests initiated at the same time, but considering we are using the same PROXY_AMAZON_BILLING_ACTIVITY_REQUEST_CODE, we only really support one (and I think that's fine). So probably we could refactor this to allow only 1 purchase to be initiated at the same time? If we do that, we won't need to be passing a callback and we can call purchasingServiceProvider.onPurchaseCompleted(activity) on the onPurchaseResponse. We would still need to hold a reference to the activity here (same as we are doing now, inside the callback), we can make it a WeakReference... (not a fan of this either...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true... We actually error out before getting to this point indicating there's another purchase in progress

purchaseCallbacks[requestId] = onSuccess to onError
productTypes[storeProduct.sku] = storeProduct.type
presentedOfferingsByProductIdentifier[storeProduct.sku] = presentedOfferingIdentifier
} else {
errorLog("No RequestId coming from ProxyAmazonBillingActivity")
}
}
}
}
purchasingServiceProvider.purchase(activity, storeProduct.sku, resultReceiver)
}

override fun onPurchaseCompleted(activity: Activity) {
purchasingServiceProvider.onPurchaseCompleted(activity)
}

override fun onPurchaseResponse(response: PurchaseResponse) {
val requestId = response.requestId
val purchaseCompletedCallback = synchronized(this) { purchaseCompletedCallbacks.remove(requestId) }
purchaseCompletedCallback?.invoke()

// Amazon is catching all exceptions and swallowing them so we have to catch ourselves and log
try {
log(LogIntent.DEBUG, AmazonStrings.PURCHASE_REQUEST_FINISHED.format(response.toJSON().toString(1)))

val requestId = response.requestId
val callbacks = synchronized(this) { purchaseCallbacks.remove(requestId) }

callbacks?.let { (onSuccess, onError) ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.revenuecat.purchases.amazon.listener

import android.app.Activity
import com.amazon.device.iap.PurchasingListener
import com.amazon.device.iap.model.ProductDataResponse
import com.amazon.device.iap.model.PurchaseUpdatesResponse
Expand All @@ -23,11 +24,16 @@ interface PurchaseResponseListener : PurchasingListener {
/* intentionally ignored. Use PurchaseUpdatesResponseListener instead */
}

@SuppressWarnings("LongParameterList")
fun purchase(
activity: Activity,
appUserID: String,
storeProduct: StoreProduct,
presentedOfferingIdentifier: String?,
onPurchaseCompleted: () -> Unit,
onSuccess: (Receipt, UserData) -> Unit,
onError: (PurchasesError) -> Unit
)

fun onPurchaseCompleted(activity: Activity)
}