|
4 | 4 |
|
5 | 5 | import com.mparticle.MPEvent; |
6 | 6 | import com.mparticle.MParticle; |
| 7 | +import com.mparticle.RoktEvent; |
7 | 8 | import com.mparticle.commerce.CommerceEvent; |
8 | 9 | import com.mparticle.commerce.Impression; |
9 | 10 | import com.mparticle.commerce.Product; |
|
17 | 18 | import com.mparticle.rokt.RoktConfig; |
18 | 19 | import com.mparticle.rokt.CacheConfig; |
19 | 20 |
|
| 21 | +import kotlin.Unit; |
| 22 | +import kotlin.coroutines.Continuation; |
| 23 | +import kotlin.jvm.functions.Function2; |
| 24 | +import kotlinx.coroutines.CoroutineScope; |
| 25 | +import kotlinx.coroutines.CoroutineScopeKt; |
| 26 | +import kotlinx.coroutines.Dispatchers; |
| 27 | +import kotlinx.coroutines.flow.Flow; |
| 28 | +import kotlinx.coroutines.flow.FlowKt; |
| 29 | + |
20 | 30 | import org.apache.cordova.CallbackContext; |
21 | 31 | import org.apache.cordova.CordovaPlugin; |
22 | 32 | import org.apache.cordova.PluginResult; |
|
33 | 43 | import java.util.Iterator; |
34 | 44 | import java.util.List; |
35 | 45 | import java.util.Map; |
| 46 | +import java.util.concurrent.CancellationException; |
36 | 47 |
|
37 | 48 | import static android.R.attr.type; |
38 | 49 |
|
39 | 50 | public class MParticleCordovaPlugin extends CordovaPlugin { |
40 | 51 |
|
41 | 52 | private final static String LOG_TAG = "MParticleCordovaPlugin"; |
42 | 53 |
|
| 54 | + // Active Rokt event subscriptions, keyed by placement identifier. Each holds the |
| 55 | + // CoroutineScope collecting that identifier's event Flow so it can be torn down, |
| 56 | + // preventing leaked scopes, duplicate subscriptions, and delivery through a stale |
| 57 | + // callbackContext after the WebView is destroyed. |
| 58 | + private final Map<String, CoroutineScope> roktEventScopes = new HashMap<>(); |
| 59 | + |
43 | 60 | public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { |
44 | 61 | if (action.equals("logEvent")) { |
45 | 62 | logEvent(args); |
@@ -111,6 +128,9 @@ public boolean execute(String action, final JSONArray args, final CallbackContex |
111 | 128 | } else if (action.equals("handleURLCallback")) { |
112 | 129 | handleURLCallback(callbackContext); |
113 | 130 | return true; |
| 131 | + } else if (action.equals("roktEvents")) { |
| 132 | + roktEvents(args, callbackContext); |
| 133 | + return true; |
114 | 134 | } else { |
115 | 135 | return false; |
116 | 136 | } |
@@ -493,6 +513,107 @@ public void handleURLCallback(final CallbackContext callbackContext) { |
493 | 513 | callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, false)); |
494 | 514 | } |
495 | 515 |
|
| 516 | + public void roktEvents(final JSONArray args, final CallbackContext callbackContext) throws JSONException { |
| 517 | + final String identifier = args.getString(0); |
| 518 | + |
| 519 | + // Tear down any prior subscription for this identifier so repeated calls don't |
| 520 | + // stack duplicate collectors all delivering through different callbacks. |
| 521 | + CoroutineScope previous = roktEventScopes.remove(identifier); |
| 522 | + if (previous != null) { |
| 523 | + CoroutineScopeKt.cancel(previous, (CancellationException) null); |
| 524 | + } |
| 525 | + |
| 526 | + Flow<RoktEvent> events = MParticle.getInstance().Rokt().events(identifier); |
| 527 | + |
| 528 | + Function2<RoktEvent, Continuation<? super Unit>, Object> onEach = |
| 529 | + (event, continuation) -> { |
| 530 | + JSONObject json = jsonFromRoktEvent(event); |
| 531 | + if (json != null) { |
| 532 | + PluginResult result = new PluginResult(PluginResult.Status.OK, json); |
| 533 | + result.setKeepCallback(true); |
| 534 | + callbackContext.sendPluginResult(result); |
| 535 | + } |
| 536 | + return Unit.INSTANCE; |
| 537 | + }; |
| 538 | + |
| 539 | + CoroutineScope scope = CoroutineScopeKt.CoroutineScope(Dispatchers.getDefault()); |
| 540 | + FlowKt.launchIn(FlowKt.onEach(events, onEach), scope); |
| 541 | + roktEventScopes.put(identifier, scope); |
| 542 | + } |
| 543 | + |
| 544 | + @Override |
| 545 | + public void onDestroy() { |
| 546 | + // Cancel every active Rokt event subscription so collectors stop running and |
| 547 | + // never deliver results through a callbackContext tied to a dead WebView. |
| 548 | + for (CoroutineScope scope : roktEventScopes.values()) { |
| 549 | + CoroutineScopeKt.cancel(scope, (CancellationException) null); |
| 550 | + } |
| 551 | + roktEventScopes.clear(); |
| 552 | + super.onDestroy(); |
| 553 | + } |
| 554 | + |
| 555 | + private static JSONObject jsonFromRoktEvent(RoktEvent event) { |
| 556 | + JSONObject json = new JSONObject(); |
| 557 | + try { |
| 558 | + if (event instanceof RoktEvent.ShowLoadingIndicator) { |
| 559 | + json.put("event", "ShowLoadingIndicator"); |
| 560 | + } else if (event instanceof RoktEvent.HideLoadingIndicator) { |
| 561 | + json.put("event", "HideLoadingIndicator"); |
| 562 | + } else if (event instanceof RoktEvent.InitComplete) { |
| 563 | + json.put("event", "InitComplete"); |
| 564 | + json.put("success", ((RoktEvent.InitComplete) event).getSuccess()); |
| 565 | + } else if (event instanceof RoktEvent.PlacementReady) { |
| 566 | + json.put("event", "PlacementReady"); |
| 567 | + json.put("placementId", ((RoktEvent.PlacementReady) event).getPlacementId()); |
| 568 | + } else if (event instanceof RoktEvent.PlacementInteractive) { |
| 569 | + json.put("event", "PlacementInteractive"); |
| 570 | + json.put("placementId", ((RoktEvent.PlacementInteractive) event).getPlacementId()); |
| 571 | + } else if (event instanceof RoktEvent.PlacementClosed) { |
| 572 | + json.put("event", "PlacementClosed"); |
| 573 | + json.put("placementId", ((RoktEvent.PlacementClosed) event).getPlacementId()); |
| 574 | + } else if (event instanceof RoktEvent.PlacementCompleted) { |
| 575 | + json.put("event", "PlacementCompleted"); |
| 576 | + json.put("placementId", ((RoktEvent.PlacementCompleted) event).getPlacementId()); |
| 577 | + } else if (event instanceof RoktEvent.PlacementFailure) { |
| 578 | + json.put("event", "PlacementFailure"); |
| 579 | + String placementId = ((RoktEvent.PlacementFailure) event).getPlacementId(); |
| 580 | + json.put("placementId", placementId != null ? placementId : JSONObject.NULL); |
| 581 | + } else if (event instanceof RoktEvent.OfferEngagement) { |
| 582 | + json.put("event", "OfferEngagement"); |
| 583 | + json.put("placementId", ((RoktEvent.OfferEngagement) event).getPlacementId()); |
| 584 | + } else if (event instanceof RoktEvent.PositiveEngagement) { |
| 585 | + json.put("event", "PositiveEngagement"); |
| 586 | + json.put("placementId", ((RoktEvent.PositiveEngagement) event).getPlacementId()); |
| 587 | + } else if (event instanceof RoktEvent.FirstPositiveEngagement) { |
| 588 | + json.put("event", "FirstPositiveEngagement"); |
| 589 | + json.put("placementId", ((RoktEvent.FirstPositiveEngagement) event).getPlacementId()); |
| 590 | + } else if (event instanceof RoktEvent.OpenUrl) { |
| 591 | + RoktEvent.OpenUrl openUrl = (RoktEvent.OpenUrl) event; |
| 592 | + json.put("event", "OpenUrl"); |
| 593 | + json.put("placementId", openUrl.getPlacementId()); |
| 594 | + json.put("url", openUrl.getUrl()); |
| 595 | + } else if (event instanceof RoktEvent.CartItemInstantPurchase) { |
| 596 | + RoktEvent.CartItemInstantPurchase purchase = (RoktEvent.CartItemInstantPurchase) event; |
| 597 | + json.put("event", "CartItemInstantPurchase"); |
| 598 | + json.put("placementId", purchase.getPlacementId()); |
| 599 | + json.put("cartItemId", purchase.getCartItemId()); |
| 600 | + json.put("catalogItemId", purchase.getCatalogItemId()); |
| 601 | + json.put("currency", purchase.getCurrency()); |
| 602 | + json.put("description", purchase.getDescription()); |
| 603 | + json.put("linkedProductId", purchase.getLinkedProductId()); |
| 604 | + json.put("totalPrice", purchase.getTotalPrice()); |
| 605 | + json.put("quantity", purchase.getQuantity()); |
| 606 | + json.put("unitPrice", purchase.getUnitPrice()); |
| 607 | + } else { |
| 608 | + json.put("event", event.getClass().getSimpleName()); |
| 609 | + } |
| 610 | + } catch (JSONException e) { |
| 611 | + Logger.warning(e, "Failed to serialize RoktEvent"); |
| 612 | + return null; |
| 613 | + } |
| 614 | + return json; |
| 615 | + } |
| 616 | + |
496 | 617 | private static IdentityApiRequest ConvertIdentityAPIRequest(JSONObject map) throws JSONException { |
497 | 618 | IdentityApiRequest.Builder identityRequest = IdentityApiRequest.withEmptyUser(); |
498 | 619 |
|
|
0 commit comments