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

Evan/feature/placement support #664

Merged
merged 36 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f25d6a8
sets up placement support
Aug 30, 2023
c63104b
sets up update local messages for placement support
Sep 5, 2023
89d0d88
Merge branch 'evan/feature/placement-support' into evan/MOB-6733-upda…
evantk91 Oct 24, 2023
3a5c98a
removes testing function
Oct 24, 2023
173fd04
adds logic to add query parameters when placement ids are passed as a…
Oct 25, 2023
85dd6db
stashed changes
Oct 25, 2023
9f65c94
stashed changes
Oct 26, 2023
fe45314
stashed changes
Oct 27, 2023
8edba2c
adds sync messages unit test
Oct 31, 2023
42075f5
updates reset function for placement support
Oct 31, 2023
a8e12c5
resolves checks
Nov 1, 2023
2294a66
Merge pull request #647 from Iterable/evan/MOB-6733-update-local-stor…
evantk91 Nov 1, 2023
379188f
addresses checks
Nov 1, 2023
be6ef1b
Merge pull request #652 from Iterable/evan/MOB-7179-reset-function-pl…
evantk91 Nov 1, 2023
0275dd5
resolves merge conflicts
Nov 24, 2023
94deaa1
updates unit tests
Nov 24, 2023
420c7c5
updates manager unit tests
Nov 27, 2023
04552e4
adds serialization tests for multiple placements
Nov 27, 2023
f031136
adds public methods for get messages for specific ids
Nov 27, 2023
eb0fb63
Merge branch 'evan/feature/placement-support' into evan/MOB-6731-upda…
Nov 27, 2023
d5f0743
stashed changes
Nov 28, 2023
b81fcae
Merge pull request #665 from Iterable/evan/MOB-7349-placements-deseri…
evantk91 Nov 28, 2023
4b617db
minor edits
Nov 30, 2023
a49c20c
clean up
Nov 30, 2023
27331ce
comments out test placements
Nov 30, 2023
476b8a8
stashed changes
Dec 1, 2023
d5f42f9
resolves checks
Dec 4, 2023
3426a74
minor cleanup
Dec 4, 2023
2b9f5f4
Merge pull request #649 from Iterable/evan/MOB-6731-update-get-messag…
evantk91 Dec 5, 2023
c4864e8
minor cleanup
Dec 5, 2023
2a77ff7
adds unit tests for placement removal
Dec 7, 2023
826ee4a
added comments
Dec 7, 2023
e540a2a
Merge branch 'embedded' into evan/feature/placement-support
Dec 7, 2023
2135e84
updates references
Dec 8, 2023
a3e664c
updates unit tests and adds check if local messages is empty
Dec 11, 2023
101a6c7
adds unit test for update handler with empty local storage
Dec 12, 2023
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
1 change: 1 addition & 0 deletions iterableapi/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ dependencies {
testImplementation 'org.khronos:opengl-api:gl1.1-android-2.1_r1'
testImplementation 'com.squareup.okhttp3:mockwebserver:4.2.2'
testImplementation 'org.skyscreamer:jsonassert:1.5.0'
testImplementation project(':iterableapi')

androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test:rules:1.3.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,25 +241,58 @@ void getInAppMessages(int count, @NonNull IterableHelper.IterableActionHandler o
}

/**
* A package-private method to get a list of Embedded Messages from Iterable;
* Passes the result to the callback.
* To get list of messages as a list of EmbeddedMessages in memory, use
evantk91 marked this conversation as resolved.
Show resolved Hide resolved
* {@link IterableEmbeddedManager#getEmbeddedMessages()} instead
* Gets a list of placements for the list of placement ids passed in from Iterable and
* passes the result to the callback;
* To get list of messages as a list of Embedded Messages in memory, use
* {@link IterableEmbeddedManager#getMessages(long)} instead.
* If no placement ids are passed in, all available messages with corresponding placement id will be returned
*
* @param placementIds array of placement ids - optional
* @param onCallback
*/
void getEmbeddedMessages(@NonNull IterableHelper.IterableActionHandler onCallback) {

public void getEmbeddedMessages(@Nullable Long[] placementIds, @NonNull IterableHelper.IterableActionHandler onCallback) {
if (!checkSDKInitialization()) {
return;
}
apiClient.getEmbeddedMessages(placementIds, onCallback);
}

/**
* Gets a list of placements for the list of placement ids passed in from Iterable and
* passes the result to the success or failure callback;
* To get list of messages as a list of Embedded Messages in memory, use
* {@link IterableEmbeddedManager#getMessages(long)} instead.
* If no placement ids are passed in, all available messages with corresponding placement id will be returned
*
* @param placementIds array of placement ids - optional
* @param onSuccess
* @param onFailure
*/

public void getEmbeddedMessages(@Nullable Long[] placementIds, @NonNull IterableHelper.SuccessHandler onSuccess, @NonNull IterableHelper.FailureHandler onFailure) {
if (!checkSDKInitialization()) {
return;
}
apiClient.getEmbeddedMessages(onCallback);
apiClient.getEmbeddedMessages(placementIds, onSuccess, onFailure);
}

/**
* A package-private method to get a list of Embedded Messages from Iterable;
* Passes the result to the success or failure callback.
* Used by the IterableEmbeddedManager.
*
* To get list of messages as a list of EmbeddedMessages in memory, use
* {@link IterableEmbeddedManager#getMessages(long)} instead
*
* @param onSuccess
* @param onFailure
*/
void getEmbeddedMessages(@NonNull IterableHelper.SuccessHandler onSuccess, @NonNull IterableHelper.FailureHandler onFailure) {
if (!checkSDKInitialization()) {
return;
}
apiClient.getEmbeddedMessages(onSuccess, onFailure);
apiClient.getEmbeddedMessages(null, onSuccess, onFailure);
evantk91 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ public void getInAppMessages(int count, @NonNull IterableHelper.IterableActionHa
}
}

void getEmbeddedMessages(@NonNull IterableHelper.IterableActionHandler onCallback) {
void getEmbeddedMessages(@Nullable Long[] placementIds, @NonNull IterableHelper.IterableActionHandler onCallback) {
JSONObject requestJSON = new JSONObject();

try {
Expand All @@ -226,15 +226,26 @@ void getEmbeddedMessages(@NonNull IterableHelper.IterableActionHandler onCallbac
requestJSON.put(IterableConstants.ITBL_KEY_SDK_VERSION, IterableConstants.ITBL_KEY_SDK_VERSION_NUMBER);
requestJSON.put(IterableConstants.ITBL_SYSTEM_VERSION, Build.VERSION.RELEASE);
requestJSON.put(IterableConstants.KEY_PACKAGE_NAME, authProvider.getContext().getPackageName());
requestJSON.put("placementId", "0");

sendGetRequest(IterableConstants.ENDPOINT_GET_EMBEDDED_MESSAGES, requestJSON, onCallback);
if (placementIds != null) {
StringBuilder pathBuilder = new StringBuilder(IterableConstants.ENDPOINT_GET_EMBEDDED_MESSAGES + "?");

for (Long placementId : placementIds) {
pathBuilder.append("&placementIds=").append(placementId);
}

String path = pathBuilder.toString();
sendGetRequest(path, requestJSON, onCallback);
} else {
sendGetRequest(IterableConstants.ENDPOINT_GET_EMBEDDED_MESSAGES, requestJSON, onCallback);
}

} catch (JSONException e) {
e.printStackTrace();
}
}

void getEmbeddedMessages(@NonNull IterableHelper.SuccessHandler onSuccess, @NonNull IterableHelper.FailureHandler onFailure) {
void getEmbeddedMessages(@Nullable Long[] placementIds, @NonNull IterableHelper.SuccessHandler onSuccess, @NonNull IterableHelper.FailureHandler onFailure) {
JSONObject requestJSON = new JSONObject();

try {
Expand All @@ -243,9 +254,20 @@ void getEmbeddedMessages(@NonNull IterableHelper.SuccessHandler onSuccess, @NonN
requestJSON.put(IterableConstants.ITBL_KEY_SDK_VERSION, IterableConstants.ITBL_KEY_SDK_VERSION_NUMBER);
requestJSON.put(IterableConstants.ITBL_SYSTEM_VERSION, Build.VERSION.RELEASE);
requestJSON.put(IterableConstants.KEY_PACKAGE_NAME, authProvider.getContext().getPackageName());
requestJSON.put("placementId", "0");

sendGetRequest(IterableConstants.ENDPOINT_GET_EMBEDDED_MESSAGES, requestJSON, onSuccess, onFailure);
if (placementIds != null) {
StringBuilder pathBuilder = new StringBuilder(IterableConstants.ENDPOINT_GET_EMBEDDED_MESSAGES + "?");

for (Long placementId : placementIds) {
pathBuilder.append("&placementIds=").append(placementId);
}

String path = pathBuilder.toString();
sendGetRequest(path, requestJSON, onSuccess, onFailure);
} else {
sendGetRequest(IterableConstants.ENDPOINT_GET_EMBEDDED_MESSAGES, requestJSON, onSuccess, onFailure);
}

} catch (JSONException e) {
e.printStackTrace();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ public class IterableEmbeddedManager : IterableActivityMonitor.AppStateCallback
// endregion

// region variables
private var localMessages: List<IterableEmbeddedMessage> = ArrayList()
private var localPlacementMessagesMap = mutableMapOf<Long, List<IterableEmbeddedMessage>>()
private var placementIds = mutableListOf<Long>()

private var updateHandleListeners = mutableListOf<IterableEmbeddedUpdateHandler>()
private var iterableApi: IterableApi
private var context: Context
Expand Down Expand Up @@ -64,13 +66,16 @@ public class IterableEmbeddedManager : IterableActivityMonitor.AppStateCallback
// region public methods

//Gets the list of embedded messages in memory without syncing
fun getMessages(): List<IterableEmbeddedMessage> {
return localMessages
fun getMessages(placementId: Long): List<IterableEmbeddedMessage>? {
return localPlacementMessagesMap[placementId]
}

fun reset() {
val emptyMessages = listOf<IterableEmbeddedMessage>()
updateLocalMessages(emptyMessages)
localPlacementMessagesMap = mutableMapOf()
}

fun getPlacementIds(): List<Long> {
return placementIds
}

//Network call to get the embedded messages
Expand All @@ -80,26 +85,51 @@ public class IterableEmbeddedManager : IterableActivityMonitor.AppStateCallback
IterableApi.sharedInstance.getEmbeddedMessages(SuccessHandler { data ->
IterableLogger.v(TAG, "Got response from network call to get embedded messages")
try {
val remoteMessageList: MutableList<IterableEmbeddedMessage> = ArrayList()
val previousPlacementIds = getPlacementIds()
val currentPlacementIds: MutableList<Long> = mutableListOf()

val placementsArray = data.optJSONArray(IterableConstants.ITERABLE_EMBEDDED_MESSAGE_PLACEMENTS)
if (placementsArray != null) {
//if there are no placements in the payload
//reset the local message storage and trigger a UI update
if(placementsArray.length() == 0) {
reset()
if(previousPlacementIds.isNotEmpty()) {
updateHandleListeners.forEach {
IterableLogger.d(TAG, "Calling updateHandler")
it.onMessagesUpdated()
}
}
} else {
for (i in 0 until placementsArray.length()) {
val placementJson = placementsArray.optJSONObject(i)
val placement = IterableEmbeddedPlacement.fromJSONObject(placementJson)
val placementId = placement.placementId
val messages = placement.messages

currentPlacementIds.add(placementId)
updateLocalMessageMap(placementId, messages)
}
}
}

val placementArray = data.optJSONArray(IterableConstants.ITERABLE_EMBEDDED_MESSAGE_PLACEMENTS)
val placement = placementArray?.getJSONObject(0)
val messagesArray = placement?.optJSONArray(IterableConstants.ITERABLE_EMBEDDED_MESSAGE)
// compare previous placements to the current placement payload
val removedPlacementIds = previousPlacementIds.subtract(currentPlacementIds.toSet())

if (messagesArray != null) {
for (i in 0 until messagesArray.length()) {
val messageJson = messagesArray.optJSONObject(i)
val message = IterableEmbeddedMessage.fromJSONObject(messageJson)
remoteMessageList.add(message)
//if there are placements removed, update the local storage and trigger UI update
if(removedPlacementIds.isNotEmpty()) {
removedPlacementIds.forEach {
localPlacementMessagesMap.remove(it)
}

updateHandleListeners.forEach {
IterableLogger.d(TAG, "Calling updateHandler")
it.onMessagesUpdated()
}
} else {
IterableLogger.e(
TAG,
"Array not found in embedded message response. Probably a parsing failure"
)
}
updateLocalMessages(remoteMessageList)
IterableLogger.v(TAG, "$localMessages")

//store placements from payload for next comparison
placementIds = currentPlacementIds

} catch (e: JSONException) {
IterableLogger.e(TAG, e.toString())
Expand Down Expand Up @@ -157,45 +187,45 @@ public class IterableEmbeddedManager : IterableActivityMonitor.AppStateCallback
}
}

private fun updateLocalMessages(remoteMessageList: List<IterableEmbeddedMessage>) {
private fun updateLocalMessageMap(
placementId: Long,
remoteMessageList: List<IterableEmbeddedMessage>
) {
IterableLogger.printInfo()
var localMessagesChanged = false

// Get local messages in a mutable list
val localMessageList = getMessages().toMutableList()
val localMessageMap = mutableMapOf<String, IterableEmbeddedMessage>()
localMessageList.forEach {
getMessages(placementId)?.toMutableList()?.forEach {
localMessageMap[it.metadata.messageId] = it
}

// Check for new messages and add them to the local list
// Compare the remote list to local list
// if there are new messages, trigger a message update in UI and send out received events
remoteMessageList.forEach {
if (!localMessageMap.containsKey(it.metadata.messageId)) {
localMessagesChanged = true
localMessageList.add(it)
IterableApi.getInstance().trackEmbeddedMessageReceived(it)
}
}

// Check for messages in the local list that are not in the remote list and remove them
// Compare the local list to remote list
// if there are messages to remove, trigger a message update in UI
val remoteMessageMap = mutableMapOf<String, IterableEmbeddedMessage>()
remoteMessageList.forEach {
remoteMessageMap[it.metadata.messageId] = it
}
val messagesToRemove = mutableListOf<IterableEmbeddedMessage>()
localMessageList.forEach {
if (!remoteMessageMap.containsKey(it.metadata.messageId)) {
messagesToRemove.add(it)

//TODO: Make a call to the updateHandler to notify that the message has been removed
//TODO: Make a call to backend if needed
localPlacementMessagesMap[placementId]?.forEach {
if (!remoteMessageMap.containsKey(it.metadata.messageId)) {
localMessagesChanged = true
}
}
localMessageList.removeAll(messagesToRemove)

this.localMessages = localMessageList
// update local message map for placement with remote message list
localPlacementMessagesMap[placementId] = remoteMessageList

//if local messages changed, trigger a message update in UI
if (localMessagesChanged) {
updateHandleListeners.forEach {
IterableLogger.d(TAG, "Calling updateHandler")
Expand Down
Loading
Loading