From b374b1208dbd241ee0400625c3ea5fd1da417aab Mon Sep 17 00:00:00 2001 From: Akshay Ayyanchira Date: Wed, 14 Jun 2023 13:24:37 -0700 Subject: [PATCH] [MOB 5730] Add callbacks to reading/removing in-app messages (#557) (#583) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [MOB 5730] Add callbacks to reading/removing in-app messages (#557) * add callback for setRead/removeMessage * modify test for setRead and added test for removeMessage * fixes * Update build.gradle * removed resultcallbackhandler * Update IterableInAppManager.java * fixes --------- Co-authored-by: Akshay Ayyanchira Co-authored-by: Hardik Mashru * Fixing and adding test method --------- Co-authored-by: devcsomnicg <129495456+devcsomnicg@users.noreply.github.com> Co-authored-by: Hardik Mashru Co-authored-by: “Akshay <“ayyanchira.akshay@gmail.com”> --- .../ui/inbox/IterableInboxFragment.java | 4 +- .../com/iterable/iterableapi/IterableApi.java | 40 ++++++++- .../iterableapi/IterableApiClient.java | 4 +- ...IterableInAppFragmentHTMLNotification.java | 2 +- .../iterableapi/IterableInAppManager.java | 20 +++-- .../iterableapi/IterableInboxTest.java | 88 ++++++++++++++++++- 6 files changed, 143 insertions(+), 15 deletions(-) diff --git a/iterableapi-ui/src/main/java/com/iterable/iterableapi/ui/inbox/IterableInboxFragment.java b/iterableapi-ui/src/main/java/com/iterable/iterableapi/ui/inbox/IterableInboxFragment.java index b57e8e8d1..7a0eb4fb3 100644 --- a/iterableapi-ui/src/main/java/com/iterable/iterableapi/ui/inbox/IterableInboxFragment.java +++ b/iterableapi-ui/src/main/java/com/iterable/iterableapi/ui/inbox/IterableInboxFragment.java @@ -244,7 +244,7 @@ public void onInboxUpdated() { @Override public void onListItemTapped(@NonNull IterableInAppMessage message) { - IterableApi.getInstance().getInAppManager().setRead(message, true); + IterableApi.getInstance().getInAppManager().setRead(message, true, null); if (inboxMode == InboxMode.ACTIVITY) { startActivity(new Intent(getContext(), IterableInboxMessageActivity.class).putExtra(IterableInboxMessageActivity.ARG_MESSAGE_ID, message.getMessageId())); @@ -255,7 +255,7 @@ public void onListItemTapped(@NonNull IterableInAppMessage message) { @Override public void onListItemDeleted(@NonNull IterableInAppMessage message, @NonNull IterableInAppDeleteActionType source) { - IterableApi.getInstance().getInAppManager().removeMessage(message, source, IterableInAppLocation.INBOX); + IterableApi.getInstance().getInAppManager().removeMessage(message, source, IterableInAppLocation.INBOX, null, null); } @Override diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java index d793b9621..5aa76d727 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApi.java @@ -725,7 +725,26 @@ public void inAppConsume(@NonNull String messageId) { IterableLogger.e(TAG, "inAppConsume: message is null"); return; } - inAppConsume(message, null, null); + inAppConsume(message, null, null, null, null); + IterableLogger.printInfo(); + } + + /** + * Consumes an InApp message. + * @param messageId + * @param successHandler The callback which returns `success`. + * @param failureHandler The callback which returns `failure`. + */ + public void inAppConsume(@NonNull String messageId, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + IterableInAppMessage message = getInAppManager().getMessageById(messageId); + if (message == null) { + IterableLogger.e(TAG, "inAppConsume: message is null"); + if (failureHandler != null) { + failureHandler.onFailure("inAppConsume: message is null", null); + } + return; + } + inAppConsume(message, null, null, successHandler, failureHandler); IterableLogger.printInfo(); } @@ -742,8 +761,25 @@ public void inAppConsume(@NonNull IterableInAppMessage message, @Nullable Iterab if (!checkSDKInitialization()) { return; } + apiClient.inAppConsume(message, source, clickLocation, inboxSessionId, null, null); + } - apiClient.inAppConsume(message, source, clickLocation, inboxSessionId); + /** + * Tracks InApp delete. + * This method from informs Iterable about inApp messages deleted with additional paramters. + * Call this method from places where inApp deletion are invoked by user. The messages can be swiped to delete or can be deleted using the link to delete button. + * + * @param message message object + * @param source An enum describing how the in App delete was triggered + * @param clickLocation The module in which the action happened + * @param successHandler The callback which returns `success`. + * @param failureHandler The callback which returns `failure`. + */ + public void inAppConsume(@NonNull IterableInAppMessage message, @Nullable IterableInAppDeleteActionType source, @Nullable IterableInAppLocation clickLocation, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { + if (!checkSDKInitialization()) { + return; + } + apiClient.inAppConsume(message, source, clickLocation, inboxSessionId, successHandler, failureHandler); } /** diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java index 0a3d9914f..e190c0aa0 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableApiClient.java @@ -317,7 +317,7 @@ void trackInAppDelivery(@NonNull IterableInAppMessage message) { } } - public void inAppConsume(@NonNull IterableInAppMessage message, @Nullable IterableInAppDeleteActionType source, @Nullable IterableInAppLocation clickLocation, @Nullable String inboxSessionId) { + public void inAppConsume(@NonNull IterableInAppMessage message, @Nullable IterableInAppDeleteActionType source, @Nullable IterableInAppLocation clickLocation, @Nullable String inboxSessionId, @Nullable final IterableHelper.SuccessHandler successHandler, @Nullable final IterableHelper.FailureHandler failureHandler) { JSONObject requestJSON = new JSONObject(); try { @@ -336,7 +336,7 @@ public void inAppConsume(@NonNull IterableInAppMessage message, @Nullable Iterab addInboxSessionID(requestJSON, inboxSessionId); } - sendPostRequest(IterableConstants.ENDPOINT_INAPP_CONSUME, requestJSON); + sendPostRequest(IterableConstants.ENDPOINT_INAPP_CONSUME, requestJSON, successHandler, failureHandler); } catch (JSONException e) { e.printStackTrace(); } diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java index ba11b3c2e..d5ca9655a 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppFragmentHTMLNotification.java @@ -417,7 +417,7 @@ private void processMessageRemoval() { } if (message.isMarkedForDeletion() && !message.isConsumed()) { - IterableApi.sharedInstance.getInAppManager().removeMessage(message); + IterableApi.sharedInstance.getInAppManager().removeMessage(message, null, null); } } diff --git a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java index 8fc294497..05c292a6d 100644 --- a/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java +++ b/iterableapi/src/main/java/com/iterable/iterableapi/IterableInAppManager.java @@ -132,9 +132,13 @@ public synchronized int getUnreadInboxMessagesCount() { * Set the read flag on an inbox message * @param message Inbox message object retrieved from {@link IterableInAppManager#getInboxMessages()} * @param read Read state flag. true = read, false = unread + * @param successHandler The callback which returns `success`. */ - public synchronized void setRead(@NonNull IterableInAppMessage message, boolean read) { + public synchronized void setRead(@NonNull IterableInAppMessage message, boolean read, @Nullable IterableHelper.SuccessHandler successHandler) { message.setRead(read); + if (successHandler != null) { + successHandler.onSuccess(new JSONObject()); // passing blank json object here as onSuccess is @Nonnull + } notifyOnChange(); } @@ -239,7 +243,7 @@ public void execute(Uri url) { scheduleProcessing(); } })) { - setRead(message, true); + setRead(message, true, null); if (consume) { message.markForDeletion(true); } @@ -249,17 +253,19 @@ public void execute(Uri url) { /** * Remove message from the list * @param message The message to be removed + * @param successHandler The callback which returns `success`. + * @param failureHandler The callback which returns `failure`. */ - public synchronized void removeMessage(@NonNull IterableInAppMessage message) { + public synchronized void removeMessage(@NonNull IterableInAppMessage message, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { message.setConsumed(true); - api.inAppConsume(message.getMessageId()); + api.inAppConsume(message.getMessageId(), successHandler, failureHandler); notifyOnChange(); } - public synchronized void removeMessage(@NonNull IterableInAppMessage message, @NonNull IterableInAppDeleteActionType source, @NonNull IterableInAppLocation clickLocation) { + public synchronized void removeMessage(@NonNull IterableInAppMessage message, @NonNull IterableInAppDeleteActionType source, @NonNull IterableInAppLocation clickLocation, @Nullable IterableHelper.SuccessHandler successHandler, @Nullable IterableHelper.FailureHandler failureHandler) { IterableLogger.printInfo(); message.setConsumed(true); - api.inAppConsume(message, source, clickLocation); + api.inAppConsume(message, source, clickLocation, successHandler, failureHandler); notifyOnChange(); } @@ -432,7 +438,7 @@ private boolean canShowInAppAfterPrevious() { private void handleIterableCustomAction(String actionName, IterableInAppMessage message) { if (IterableConstants.ITERABLE_IN_APP_ACTION_DELETE.equals(actionName)) { - removeMessage(message, IterableInAppDeleteActionType.DELETE_BUTTON, IterableInAppLocation.IN_APP); + removeMessage(message, IterableInAppDeleteActionType.DELETE_BUTTON, IterableInAppLocation.IN_APP, null, null); } } diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInboxTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInboxTest.java index 3e324208c..1697c381d 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInboxTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInboxTest.java @@ -2,8 +2,12 @@ import android.app.Activity; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.iterable.iterableapi.unit.PathBasedQueueDispatcher; +import org.json.JSONObject; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -11,6 +15,8 @@ import java.io.IOException; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; @@ -78,18 +84,98 @@ public void testInboxMessageOrdering() throws Exception { assertEquals("message4", inboxMessages.get(1).getMessageId()); } + @Test + public void testRemoveMessageSuccessCallbackOnSuccessfulResponse() throws Exception { + final CountDownLatch signal = new CountDownLatch(1); + dispatcher.enqueueResponse("/inApp/getMessages", new MockResponse().setBody(IterableTestUtils.getResourceString("inapp_payload_inbox_multiple.json"))); + final IterableInAppManager inAppManager = IterableApi.getInstance().getInAppManager(); + inAppManager.syncInApp(); + shadowOf(getMainLooper()).idle(); + List inboxMessages = inAppManager.getInboxMessages(); + assertEquals(2, inboxMessages.size()); + assertEquals(1, inAppManager.getUnreadInboxMessagesCount()); + + final JSONObject responseData = new JSONObject("{\"key\":\"value\"}"); + dispatcher.enqueueResponse("/events/inAppConsume", new MockResponse().setResponseCode(200).setBody(responseData.toString())); + + inAppManager.removeMessage(inboxMessages.get(0), new IterableHelper.SuccessHandler() { + @Override + public void onSuccess(@NonNull JSONObject data) { + signal.countDown(); + } + }, new IterableHelper.FailureHandler() { + @Override + public void onFailure(@NonNull String reason, @Nullable JSONObject data) { + assertFalse(true); + } + }); + shadowOf(getMainLooper()).idle(); + assertTrue("Message remove success callback called", signal.await(1, TimeUnit.SECONDS)); + } + + @Test + public void testRemoveMessageFailureCallbackOnFailedResponse() throws Exception { + final CountDownLatch signal = new CountDownLatch(1); + dispatcher.enqueueResponse("/inApp/getMessages", new MockResponse().setBody(IterableTestUtils.getResourceString("inapp_payload_inbox_multiple.json"))); + final IterableInAppManager inAppManager = IterableApi.getInstance().getInAppManager(); + inAppManager.syncInApp(); + shadowOf(getMainLooper()).idle(); + List inboxMessages = inAppManager.getInboxMessages(); + assertEquals(2, inboxMessages.size()); + assertEquals(1, inAppManager.getUnreadInboxMessagesCount()); + + final JSONObject responseData = new JSONObject("{\"key\":\"value\"}"); + dispatcher.enqueueResponse("/events/inAppConsume", new MockResponse().setResponseCode(500).setBody(responseData.toString())); + + inAppManager.removeMessage(inboxMessages.get(0), new IterableHelper.SuccessHandler() { + @Override + public void onSuccess(@NonNull JSONObject data) { + assertFalse(true); + } + }, new IterableHelper.FailureHandler() { + @Override + public void onFailure(@NonNull String reason, @Nullable JSONObject data) { + signal.countDown(); + } + }); + shadowOf(getMainLooper()).idle(); + assertTrue("Message remove failure callback called", signal.await(1, TimeUnit.SECONDS)); + } + @Test public void testSetRead() throws Exception { + // Set up mock response dispatcher.enqueueResponse("/inApp/getMessages", new MockResponse().setBody(IterableTestUtils.getResourceString("inapp_payload_inbox_multiple.json"))); + + // Initialize in-app manager and wait for messages to be synced IterableInAppManager inAppManager = IterableApi.getInstance().getInAppManager(); inAppManager.syncInApp(); shadowOf(getMainLooper()).idle(); + + // Get inbox messages List inboxMessages = inAppManager.getInboxMessages(); + + // Verify initial state assertEquals(1, inAppManager.getUnreadInboxMessagesCount()); assertEquals(2, inboxMessages.size()); assertFalse(inboxMessages.get(0).isRead()); assertTrue(inboxMessages.get(1).isRead()); - inAppManager.setRead(inboxMessages.get(0), true); + + // Set first message as read with a callback + final boolean[] callbackCalled = { false }; + inAppManager.setRead(inboxMessages.get(0), true, new IterableHelper.SuccessHandler() { + @Override + public void onSuccess(@NonNull JSONObject data) { + callbackCalled[0] = true; + assertTrue(callbackCalled[0]); + } + }); + + // Wait for callback to be called + shadowOf(getMainLooper()).idle(); + + // Verify that callback was called and that message is marked as read + assertTrue(callbackCalled[0]); assertEquals(0, inAppManager.getUnreadInboxMessagesCount()); assertEquals(2, inAppManager.getInboxMessages().size()); assertTrue(inboxMessages.get(0).isRead());