diff --git a/build.gradle b/build.gradle
index b98c6f41..f7b7f89c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -22,16 +22,7 @@ ext {
mm_compileSdkVersion = 33
mm_targetSdkVersion = 33
mm_buildToolsVersion = "33.0.0"
- mm_androidSdkVersion = "7.3.1"
-}
-
-allprojects {
- repositories {
- google()
- mavenCentral()
- // huawei maven
- maven { url 'https://developer.huawei.com/repo/' }
- }
+ mm_androidSdkVersion = "7.4.2"
}
//It's required to be applied here, to have `subprojects.publish` task available
@@ -62,6 +53,7 @@ task build {}
build.dependsOn tasks.getByPath('infobip-mobile-messaging-huawei-chat-sdk:build')
//build.dependsOn tasks.getByPath('infobip-mobile-messaging-huawei-geo-sdk:build')
+build.dependsOn tasks.getByPath('infobip-mobile-messaging-huawei-inbox-sdk:build')
build.dependsOn tasks.getByPath('infobip-mobile-messaging-huawei-cryptor-migration:build')
build.dependsOn tasks.getByPath(':infobip-mobile-messaging-huawei-sdk:build')
diff --git a/infobip-mobile-messaging-huawei-chat-sdk/build.gradle b/infobip-mobile-messaging-huawei-chat-sdk/build.gradle
index 47952dc4..95d2f870 100644
--- a/infobip-mobile-messaging-huawei-chat-sdk/build.gradle
+++ b/infobip-mobile-messaging-huawei-chat-sdk/build.gradle
@@ -1,7 +1,7 @@
buildscript {
repositories {
- mavenCentral()
google()
+ mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.2'
diff --git a/infobip-mobile-messaging-huawei-demo/build.gradle b/infobip-mobile-messaging-huawei-demo/build.gradle
index 222a96a1..e7954acc 100644
--- a/infobip-mobile-messaging-huawei-demo/build.gradle
+++ b/infobip-mobile-messaging-huawei-demo/build.gradle
@@ -96,6 +96,10 @@ android {
// geofencing {
// dimension "default"
// }
+ inbox {
+ dimension "default"
+ }
+
}
}
@@ -104,14 +108,15 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
implementation project(':infobip-mobile-messaging-huawei-chat-sdk')
-
// current version of SDK doesn't support geo
// implementation project(':infobip-mobile-messaging-huawei-geo-sdk')
- implementation ("com.infobip:infobip-mobile-messaging-android-resources:${mm_androidSdkVersion}@aar")
implementation project(':infobip-mobile-messaging-huawei-sdk')
+ implementation project(':infobip-mobile-messaging-huawei-inbox-sdk')
implementation "com.infobip:infobip-mobile-messaging-api-java:${mm_androidSdkVersion}"
+ implementation ("com.infobip:infobip-mobile-messaging-android-resources:${mm_androidSdkVersion}@aar")
+ // push kit
implementation 'com.huawei.hms:push:6.3.0.302'
implementation 'androidx.appcompat:appcompat:1.3.1'
diff --git a/infobip-mobile-messaging-huawei-demo/src/inbox/AndroidManifest.xml b/infobip-mobile-messaging-huawei-demo/src/inbox/AndroidManifest.xml
new file mode 100644
index 00000000..7da5d862
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-demo/src/inbox/AndroidManifest.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/infobip-mobile-messaging-huawei-demo/src/inbox/java/org/infobip/mobile/messaging/demo/Constants.java b/infobip-mobile-messaging-huawei-demo/src/inbox/java/org/infobip/mobile/messaging/demo/Constants.java
new file mode 100644
index 00000000..28f1670e
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-demo/src/inbox/java/org/infobip/mobile/messaging/demo/Constants.java
@@ -0,0 +1,8 @@
+package org.infobip.mobile.messaging.demo;
+
+public class Constants {
+ public static final String BUNDLE_KEY_DEMO_INBOX = "DEMO_INBOX";
+ public static final String BUNDLE_KEY_EXTERNAL_USER_ID = "DEMO_EXTERNAL_USER_ID";
+ public static final String SHARED_PREF_KEY = "InfobipDemoPrefs";
+ public static final String SHARED_PREF_INBOX_VALUE = "INFOBIP_DEMO_INBOX_VALUE";
+}
diff --git a/infobip-mobile-messaging-huawei-demo/src/inbox/java/org/infobip/mobile/messaging/demo/CustomAdapter.java b/infobip-mobile-messaging-huawei-demo/src/inbox/java/org/infobip/mobile/messaging/demo/CustomAdapter.java
new file mode 100644
index 00000000..b22098b3
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-demo/src/inbox/java/org/infobip/mobile/messaging/demo/CustomAdapter.java
@@ -0,0 +1,83 @@
+package org.infobip.mobile.messaging.demo;
+
+import android.graphics.Typeface;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.infobip.mobile.messaging.inbox.InboxMessage;
+
+import java.util.List;
+
+public class CustomAdapter extends RecyclerView.Adapter {
+ private List messageTexts;
+ private OnMessageListener onMessageListener;
+
+ public CustomAdapter(List inboxMessages, OnMessageListener onMessageListener) {
+ this.messageTexts = inboxMessages;
+ this.onMessageListener = onMessageListener;
+ }
+
+ public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
+ private final TextView messageText;
+ private final TextView messageTopic;
+
+ OnMessageListener onMessageListener;
+
+ public ViewHolder(View view, OnMessageListener onMessageListener) {
+ super(view);
+ this.onMessageListener = onMessageListener;
+ view.setOnClickListener(this);
+
+ messageText = view.findViewById(R.id.tv_message_details);
+ messageTopic = view.findViewById(R.id.tv_message_topic);
+ }
+
+ @Override
+ public void onClick(View view) {
+ onMessageListener.onMessageClick(getAdapterPosition());
+ }
+
+ public TextView getMessageTextView() {
+ return messageText;
+ }
+
+ public TextView getMessageTopicView() {
+ return messageTopic;
+ }
+ }
+
+ @Override
+ @NonNull
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.message_details, parent, false);
+
+ return new ViewHolder(v, onMessageListener);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ InboxMessage message = messageTexts.get(position);
+ String text = "Text: " + message.getBody();
+ String topic = "Topic: " + message.getTopic();
+
+ holder.getMessageTextView().setText(text);
+ holder.getMessageTopicView().setText(topic);
+ holder.getMessageTextView()
+ .setTypeface(null, message.isSeen() ? Typeface.NORMAL : Typeface.BOLD);
+ }
+
+ @Override
+ public int getItemCount() {
+ return messageTexts.size();
+ }
+
+ public interface OnMessageListener {
+ void onMessageClick(int position);
+ }
+}
\ No newline at end of file
diff --git a/infobip-mobile-messaging-huawei-demo/src/inbox/java/org/infobip/mobile/messaging/demo/InboxActivity.java b/infobip-mobile-messaging-huawei-demo/src/inbox/java/org/infobip/mobile/messaging/demo/InboxActivity.java
new file mode 100644
index 00000000..ff64f0ae
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-demo/src/inbox/java/org/infobip/mobile/messaging/demo/InboxActivity.java
@@ -0,0 +1,198 @@
+package org.infobip.mobile.messaging.demo;
+
+import android.annotation.SuppressLint;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
+import com.google.android.material.tabs.TabLayout;
+
+import org.infobip.mobile.messaging.MobileMessaging;
+import org.infobip.mobile.messaging.inbox.Inbox;
+import org.infobip.mobile.messaging.inbox.InboxBundleMapper;
+import org.infobip.mobile.messaging.inbox.InboxMessage;
+import org.infobip.mobile.messaging.inbox.MobileInbox;
+import org.infobip.mobile.messaging.inbox.MobileInboxFilterOptions;
+import org.infobip.mobile.messaging.mobileapi.MobileMessagingError;
+import org.infobip.mobile.messaging.mobileapi.Result;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class InboxActivity extends AppCompatActivity implements CustomAdapter.OnMessageListener {
+
+ private SwipeRefreshLayout swipeLayout;
+ private TextView counts;
+ private Button btnBack;
+ protected RecyclerView mRecyclerView;
+ protected RecyclerView.LayoutManager mLayoutManager;
+ protected CustomAdapter mAdapter;
+
+ private Inbox inbox;
+ private List inboxMessages;
+ private MobileInbox mobileInbox;
+ private String externalUserId;
+ private String topic;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_inbox);
+
+ mRecyclerView = findViewById(R.id.recyclerView);
+ counts = findViewById(R.id.tv_counts);
+ swipeLayout = findViewById(R.id.swipeContainer);
+ swipeLayout.setOnRefreshListener(fetchInboxOnSwipeListener());
+
+ btnBack = findViewById(R.id.button_second);
+ btnBack.setOnClickListener(onBackPressedListener());
+
+ TabLayout tabs = findViewById(R.id.tabs);
+
+ mobileInbox = MobileInbox.getInstance(this);
+ topic = null;
+
+ Intent intent = getIntent();
+ if (intent != null) {
+ externalUserId = intent.getStringExtra(Constants.BUNDLE_KEY_EXTERNAL_USER_ID);
+ inbox = Inbox.createFrom(intent.getBundleExtra(Constants.BUNDLE_KEY_DEMO_INBOX));
+ }
+
+ inboxMessages = new ArrayList<>();
+ if (inbox != null && inbox.getCountTotal() > 0) {
+ inboxMessages.addAll(inbox.getMessages());
+ updateCounterText();
+ } else {
+ counts.setText(R.string.inbox_empty);
+ }
+
+ mAdapter = new CustomAdapter(inboxMessages, this);
+
+ mLayoutManager = new LinearLayoutManager(this);
+ mRecyclerView.setAdapter(mAdapter);
+ mRecyclerView.setLayoutManager(mLayoutManager);
+ mRecyclerView.scrollToPosition(0);
+
+ tabs.addTab(tabs.newTab().setText(R.string.tb_all));
+ tabs.addTab(tabs.newTab().setText(R.string.tb_promo));
+ tabs.addTab(tabs.newTab().setText(R.string.tb_notifications));
+ tabs.addOnTabSelectedListener(tabSelectedListener());
+ }
+
+ @Override
+ public void onMessageClick(int i) {
+ String messageId = inboxMessages.get(i).getMessageId();
+ if (inboxMessages.get(i).isSeen()) {
+ return;
+ }
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setMessage(R.string.mark_as_seen)
+ .setPositiveButton("YES", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ inboxMessages.get(i).setSeen();
+ mobileInbox.setSeen(externalUserId, new String[]{messageId}, new MobileMessaging.ResultListener() {
+ @SuppressLint("NotifyDataSetChanged")
+ @Override
+ public void onResult(Result result) {
+ if (result.isSuccess()) {
+ updateCounterText();
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+ });
+ }
+ }).setNegativeButton("NO", (dialog, which) -> {});
+ builder.show();
+ }
+
+ private void updateCounterText() {
+ String countsText = String.format("%s messages, %s unread", inbox.getCountTotal(), inbox.getCountUnread());
+ counts.setText(countsText);
+ }
+
+ private TabLayout.OnTabSelectedListener tabSelectedListener() {
+ return new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ Map topicNameMap = new HashMap<>();
+ topicNameMap.put(0, null);
+ topicNameMap.put(1, "promo");
+ topicNameMap.put(2, "notifications");
+
+ if (inbox != null && inbox.getCountTotal() > 0) {
+ topic = topicNameMap.get(tab.getPosition());
+ updateInboxList();
+ }
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+
+ }
+ };
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private void updateInboxList() {
+ inboxMessages.clear();
+ for (InboxMessage message : inbox.getMessages()) {
+ if (topic == null || message.getTopic().equalsIgnoreCase(topic)) {
+ inboxMessages.add(message);
+ }
+ }
+ mAdapter.notifyDataSetChanged();
+ }
+
+ private SwipeRefreshLayout.OnRefreshListener fetchInboxOnSwipeListener() {
+ MobileInboxFilterOptions filterOptions = new MobileInboxFilterOptions(null, null, topic, null);
+ return () -> mobileInbox.fetchInbox(externalUserId, filterOptions, new MobileMessaging.ResultListener() {
+ @Override
+ public void onResult(Result result) {
+ if (result.isSuccess()) {
+ inbox = result.getData();
+ if (inbox.getCountTotal() > 0) {
+ updateInboxList();
+ updateCounterText();
+ } else {
+ Toast.makeText(InboxActivity.this, R.string.inbox_empty, Toast.LENGTH_SHORT).show();
+ }
+ } else {
+ Toast.makeText(InboxActivity.this, R.string.cannot_update_inbox, Toast.LENGTH_SHORT).show();
+ }
+ swipeLayout.setRefreshing(false);
+ }
+ });
+ }
+
+ private View.OnClickListener onBackPressedListener() {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Bundle arguments = new Bundle();
+ arguments.putBundle(Constants.BUNDLE_KEY_DEMO_INBOX, InboxBundleMapper.inboxToBundle(inbox));
+
+ Intent intent = new Intent(InboxActivity.this, MainActivity.class);
+ intent.putExtras(arguments);
+ InboxActivity.this.startActivity(intent);
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/infobip-mobile-messaging-huawei-demo/src/inbox/java/org/infobip/mobile/messaging/demo/MainActivity.java b/infobip-mobile-messaging-huawei-demo/src/inbox/java/org/infobip/mobile/messaging/demo/MainActivity.java
new file mode 100644
index 00000000..e930f095
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-demo/src/inbox/java/org/infobip/mobile/messaging/demo/MainActivity.java
@@ -0,0 +1,254 @@
+package org.infobip.mobile.messaging.demo;
+
+import static org.infobip.mobile.messaging.util.StringUtils.isBlank;
+import static org.infobip.mobile.messaging.util.StringUtils.isNotBlank;
+
+import android.annotation.SuppressLint;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import com.google.android.material.badge.BadgeDrawable;
+import com.google.android.material.badge.BadgeUtils;
+
+import org.infobip.mobile.messaging.Event;
+import org.infobip.mobile.messaging.MobileMessaging;
+import org.infobip.mobile.messaging.SuccessPending;
+import org.infobip.mobile.messaging.User;
+import org.infobip.mobile.messaging.UserIdentity;
+import org.infobip.mobile.messaging.inbox.Inbox;
+import org.infobip.mobile.messaging.inbox.InboxBundleMapper;
+import org.infobip.mobile.messaging.inbox.MobileInbox;
+import org.infobip.mobile.messaging.inbox.MobileInboxEvent;
+import org.infobip.mobile.messaging.mobileapi.MobileMessagingError;
+import org.infobip.mobile.messaging.mobileapi.Result;
+
+public class MainActivity extends AppCompatActivity {
+
+ private EditText etExtUsrId;
+ private Button btnPersonalize;
+ private Button btnDepersonalize;
+ private BadgeDrawable badgeDrawable;
+ private Button btnToInbox;
+
+ private MobileMessaging mobileMessaging;
+ private SharedPreferences sharedPref;
+ private String externalUserId;
+ private Inbox inbox;
+ private Boolean receiversRegistered = false;
+
+ private final BroadcastReceiver sdkBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateControls();
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_main);
+ setSupportActionBar(this.findViewById(R.id.toolbar));
+
+ btnToInbox = findViewById(R.id.btn_to_inbox);
+ etExtUsrId = findViewById(R.id.etExtUserId);
+ btnPersonalize = findViewById(R.id.btn_personalize);
+ btnDepersonalize = findViewById(R.id.btn_depersonalize);
+
+ registerReceivers();
+ mobileMessaging = MobileMessaging.getInstance(this);
+
+ // Setting onClick listeners for buttons
+ btnPersonalize.setOnClickListener(this::onPersonalizeButtonPressed);
+ btnDepersonalize.setOnClickListener(this::onDepersonalizeButtonPressed);
+ btnToInbox.setOnClickListener(onToInboxButtonPressed());
+
+ // Setting the badge
+ badgeDrawable = BadgeDrawable.create(this);
+ btnToInbox.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @SuppressLint({"UnsafeOptInUsageError", "UnsafeExperimentalUsageError"})
+ @Override
+ public void onGlobalLayout() {
+ badgeDrawable.setNumber((inbox == null) ? 0 : inbox.getCountUnread());
+ badgeDrawable.setHorizontalOffset(30);
+ badgeDrawable.setVerticalOffset(20);
+ BadgeUtils.attachBadgeDrawable(badgeDrawable, btnToInbox, null);
+ btnToInbox.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+ });
+
+ sharedPref = getSharedPreferences(Constants.SHARED_PREF_KEY, Context.MODE_PRIVATE);
+
+ // Checking if there is inbox in passed arguments from second activity
+ Intent intent = getIntent();
+ if (intent != null) {
+ Bundle inboxValue = intent.getBundleExtra(Constants.BUNDLE_KEY_DEMO_INBOX);
+ if (inboxValue != null) {
+ inbox = Inbox.createFrom(inboxValue);
+ saveInboxToSharedPref();
+ } else {
+ // Checking if there is a saved inbox in preferences
+ getInboxFromSharedPref();
+ }
+ }
+
+ // Populating text field with external user id if present
+ if (isUserPersonalizedWithExternalUserId()) {
+ externalUserId = mobileMessaging.getUser().getExternalUserId();
+ etExtUsrId.setText(externalUserId);
+ } else {
+ etExtUsrId.setHint(R.string.extuserid_hint);
+ }
+
+ updateControls();
+ }
+
+ private void getInboxFromSharedPref() {
+ if (isUserPersonalizedWithExternalUserId()) {
+ String sharedPrefString = sharedPref.getString(Constants.SHARED_PREF_INBOX_VALUE, "");
+ if (isNotBlank(sharedPrefString)) {
+ inbox = new Inbox().fromString(sharedPrefString);
+ }
+ }
+ }
+
+ private void saveInboxToSharedPref() {
+ SharedPreferences.Editor sharedPrefEditor = sharedPref.edit()
+ .putString(Constants.SHARED_PREF_INBOX_VALUE, inbox.toString());
+ sharedPrefEditor.commit();
+ }
+
+ // Personalizing by external user id provided, then fetching inbox
+ private void onPersonalizeButtonPressed(View view) {
+ UserIdentity userIdentity = new UserIdentity();
+ String extUserId = etExtUsrId.getText().toString();
+ if (isBlank(extUserId)) {
+ Toast.makeText(MainActivity.this, R.string.cannot_personalize, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ userIdentity.setExternalUserId(extUserId);
+ mobileMessaging.personalize(userIdentity, null, new MobileMessaging.ResultListener() {
+ @Override
+ public void onResult(Result result) {
+ externalUserId = extUserId;
+ Toast.makeText(MainActivity.this, R.string.personalized_success, Toast.LENGTH_SHORT).show();
+ prepareInbox();
+ }
+ });
+ }
+
+ public void onDepersonalizeButtonPressed(View view) {
+ //Cleaning up inbox because new user might log in
+ inbox = new Inbox();
+ externalUserId = null;
+ saveInboxToSharedPref();
+ mobileMessaging.depersonalize(new MobileMessaging.ResultListener() {
+ @Override
+ public void onResult(Result result) {
+ Toast.makeText(MainActivity.this, R.string.depersonalized_success, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ private View.OnClickListener onToInboxButtonPressed() {
+ return view -> {
+ // Passing external user id and inbox as arguments to the second activity
+ Bundle arguments = new Bundle();
+ arguments.putString(Constants.BUNDLE_KEY_EXTERNAL_USER_ID, externalUserId);
+ arguments.putBundle(Constants.BUNDLE_KEY_DEMO_INBOX, InboxBundleMapper.inboxToBundle(inbox));
+
+ Intent intent = new Intent(MainActivity.this, InboxActivity.class);
+ intent.putExtras(arguments);
+ MainActivity.this.startActivity(intent);
+ };
+ }
+
+ private boolean isUserPersonalizedWithExternalUserId() {
+ if (mobileMessaging.getUser() != null)
+ return isNotBlank(mobileMessaging.getUser().getExternalUserId());
+ return false;
+ }
+
+ private boolean isPushRegistrationAvailable() {
+ return mobileMessaging.getInstallation() != null;
+ }
+
+ private void prepareInbox() {
+ MobileInbox.getInstance(this).fetchInbox(mobileMessaging.getUser().getExternalUserId(), null, new MobileMessaging.ResultListener() {
+ @Override
+ public void onResult(Result result) {
+ if (result.isSuccess()) {
+ inbox = result.getData();
+ saveInboxToSharedPref();
+ updateBadge();
+ }
+ }
+ });
+ }
+
+ // Changing buttons & text field availability
+ private void updateControls() {
+ updateBadge();
+ etExtUsrId.setEnabled(isPushRegistrationAvailable() && !isUserPersonalizedWithExternalUserId());
+ btnPersonalize.setEnabled(!isUserPersonalizedWithExternalUserId());
+ btnDepersonalize.setEnabled(isUserPersonalizedWithExternalUserId());
+ btnToInbox.setEnabled(isUserPersonalizedWithExternalUserId());
+ }
+
+ // Showing count of unread inbox messages
+ private void updateBadge() {
+ badgeDrawable.setNumber((inbox == null) ? 0 : inbox.getCountUnread());
+ }
+
+ @Override
+ public void onDestroy() {
+ unregisterReceivers();
+ super.onDestroy();
+ }
+
+ @Override
+ public void onPause() {
+ unregisterReceivers();
+ super.onPause();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ registerReceivers();
+ }
+
+ private void registerReceivers() {
+ if (!receiversRegistered) {
+ LocalBroadcastManager
+ .getInstance(this)
+ .registerReceiver(sdkBroadcastReceiver, new IntentFilter() {{
+ addAction(Event.PERSONALIZED.getKey());
+ addAction(Event.DEPERSONALIZED.getKey());
+ addAction(MobileInboxEvent.INBOX_MESSAGES_FETCHED.getKey());
+ }});
+ }
+ this.receiversRegistered = true;
+ }
+
+ private void unregisterReceivers() {
+ if (receiversRegistered) {
+ LocalBroadcastManager
+ .getInstance(this)
+ .unregisterReceiver(sdkBroadcastReceiver);
+ }
+ this.receiversRegistered = false;
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-demo/src/inbox/res/layout/activity_inbox.xml b/infobip-mobile-messaging-huawei-demo/src/inbox/res/layout/activity_inbox.xml
new file mode 100644
index 00000000..997788bc
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-demo/src/inbox/res/layout/activity_inbox.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/infobip-mobile-messaging-huawei-demo/src/inbox/res/layout/activity_main.xml b/infobip-mobile-messaging-huawei-demo/src/inbox/res/layout/activity_main.xml
new file mode 100644
index 00000000..8cda3a92
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-demo/src/inbox/res/layout/activity_main.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/infobip-mobile-messaging-huawei-demo/src/inbox/res/layout/message_details.xml b/infobip-mobile-messaging-huawei-demo/src/inbox/res/layout/message_details.xml
new file mode 100644
index 00000000..3de343c2
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-demo/src/inbox/res/layout/message_details.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/infobip-mobile-messaging-huawei-demo/src/inbox/res/values/strings.xml b/infobip-mobile-messaging-huawei-demo/src/inbox/res/values/strings.xml
new file mode 100644
index 00000000..fa9c91fd
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-demo/src/inbox/res/values/strings.xml
@@ -0,0 +1,21 @@
+
+ MainActivity
+ To Inbox
+ Back
+ Button
+ Personalize
+
+ External User ID
+ Personalize
+ Depersonalize
+
+ Inbox is empty
+ all
+ promo
+ notifications
+ Do you want to mark message as seen?
+ Cannot update inbox
+ Cannot personalize: externalUserId is empty
+ Personalized successfully
+ Depersonalized successfully
+
\ No newline at end of file
diff --git a/infobip-mobile-messaging-huawei-demo/src/inbox/res/values/styles.xml b/infobip-mobile-messaging-huawei-demo/src/inbox/res/values/styles.xml
new file mode 100644
index 00000000..3efdcb47
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-demo/src/inbox/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/infobip-mobile-messaging-huawei-demo/src/inbox/res/values/themes.xml b/infobip-mobile-messaging-huawei-demo/src/inbox/res/values/themes.xml
new file mode 100644
index 00000000..b66099c1
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-demo/src/inbox/res/values/themes.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/infobip-mobile-messaging-huawei-geo-sdk/src/main/java/org/infobip/mobile/messaging/geo/mapper/GeoBundleMapper.java b/infobip-mobile-messaging-huawei-geo-sdk/src/main/java/org/infobip/mobile/messaging/geo/mapper/GeoBundleMapper.java
index 38b4bd70..7a7c5c4f 100644
--- a/infobip-mobile-messaging-huawei-geo-sdk/src/main/java/org/infobip/mobile/messaging/geo/mapper/GeoBundleMapper.java
+++ b/infobip-mobile-messaging-huawei-geo-sdk/src/main/java/org/infobip/mobile/messaging/geo/mapper/GeoBundleMapper.java
@@ -14,7 +14,7 @@
public class GeoBundleMapper extends BundleMapper {
private static final String BUNDLED_GEO_TAG = GeoBundleMapper.class.getName() + ".geo";
- private static final String BUNDLED_GEO_MESSAGE_TAG = GeoBundleMapper.class.getName() + ".geoMessage";
+ private static final String BUNDLED_GEO_MESSAGE_TAG = GeoBundleMapper.class.getName() + ".geo.message";
private static final String BUNDLED_GEO_REPORTS_TAG = GeoBundleMapper.class.getName() + ".geo.report";
/**
@@ -29,7 +29,7 @@ public static Geo geoFromBundle(@NonNull Bundle bundle) {
}
/**
- * Serializes geo object into bundle
+ * Serializes geo message object into bundle
*
* @param geoMessage object to serialize
* @return bundle with geo contents
@@ -40,10 +40,10 @@ public static Bundle geoMessageToBundle(@NonNull GeoMessage geoMessage) {
}
/**
- * Serializes geo object into bundle
+ * De-serializes geo message object from bundle
*
* @param bundle where to load data from
- * @return bundle with geo contents
+ * @return new geo message object
*/
@Nullable
public static GeoMessage geoMessageFromBundle(@NonNull Bundle bundle) {
diff --git a/infobip-mobile-messaging-huawei-geo-sdk/src/main/java/org/infobip/mobile/messaging/geo/mapper/GeoDataMapper.java b/infobip-mobile-messaging-huawei-geo-sdk/src/main/java/org/infobip/mobile/messaging/geo/mapper/GeoDataMapper.java
index c4927861..7636e033 100644
--- a/infobip-mobile-messaging-huawei-geo-sdk/src/main/java/org/infobip/mobile/messaging/geo/mapper/GeoDataMapper.java
+++ b/infobip-mobile-messaging-huawei-geo-sdk/src/main/java/org/infobip/mobile/messaging/geo/mapper/GeoDataMapper.java
@@ -22,7 +22,7 @@ public static Geo geoFromInternalData(String internalDataJson) {
}
/**
- * Serializes json data to Geo object
+ * Serializes Geo object to json data
*
* @param geo geo object to serialize
* @return String - geo data as json string
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/.gitignore b/infobip-mobile-messaging-huawei-inbox-sdk/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/build.gradle b/infobip-mobile-messaging-huawei-inbox-sdk/build.gradle
new file mode 100644
index 00000000..b9efd7e6
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/build.gradle
@@ -0,0 +1,125 @@
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ maven { url 'https://developer.huawei.com/repo/' }
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.2.2'
+ }
+}
+
+plugins {
+ id 'com.android.library'
+ id 'maven-publish'
+ id 'idea'
+}
+
+android {
+ compileSdkVersion mm_compileSdkVersion
+ buildToolsVersion mm_buildToolsVersion
+
+ defaultConfig {
+ minSdkVersion mm_minSdkVersion
+ targetSdkVersion mm_targetSdkVersion
+ versionCode 1
+ versionName project.version
+
+ // since AGP 4.1 "VERSION_NAME" is not included anymore to BuildConfig
+ // https://issuetracker.google.com/issues/154275579
+ buildConfigField "String", "VERSION_NAME", "\"$versionName\""
+ testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
+ consumerProguardFiles 'proguard-rules.pro'
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+}
+
+dependencies {
+ implementation project(':infobip-mobile-messaging-huawei-sdk')
+ implementation "com.infobip:infobip-mobile-messaging-api-java:${mm_androidSdkVersion}"
+
+ androidTestImplementation project(':infobip-mobile-messaging-huawei-test')
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation "junit:junit:4.12"
+ androidTestImplementation "org.mockito:mockito-core:1.10.19"
+ androidTestImplementation 'com.nanohttpd:nanohttpd:2.1.0'
+ androidTestImplementation('org.skyscreamer:jsonassert:1.5.0') {
+ exclude group: "org.json", module: "json"
+ }
+
+ testImplementation project(':infobip-mobile-messaging-huawei-test')
+}
+
+android.libraryVariants.all { variant ->
+ def name = variant.buildType.name
+ def jar = project.tasks.create(name: "jar${name.capitalize()}", type: Jar) {
+ Task javaCompileTask
+ if (variant.hasProperty('javaCompileProvider')) {
+ // Android 3.3.0+
+ javaCompileTask = variant.javaCompileProvider.get()
+ } else {
+ javaCompileTask = variant.javaCompile
+ }
+
+ dependsOn javaCompileTask
+ from javaCompileTask.destinationDir
+
+ manifest {
+ attributes(
+ "Bundle-Name": 'parse-android',
+ "Bundle-Version": project.version
+ )
+ }
+
+ exclude '**/R.class'
+ exclude '**/R\$*.class'
+ exclude '**/Manifest.class'
+ exclude '**/Manifest\$*.class'
+ exclude '**/BuildConfig.class'
+ }
+
+ artifacts {
+ archives jar
+ }
+}
+
+task sourcesJar(type: Jar) {
+ from android.sourceSets.main.java.srcDirs
+ archiveClassifier.set('sources')
+}
+
+task javadoc(type: Javadoc) {
+ failOnError false
+ source = android.sourceSets.main.java.srcDirs
+ classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
+}
+
+task javadocJar(type: Jar, dependsOn: javadoc) {
+ archiveClassifier.set('javadoc')
+ from javadoc.destinationDir
+}
+
+artifacts {
+ archives javadocJar
+ archives sourcesJar
+}
+
+ext {
+ PUBLISH_ARTIFACT_ID = 'infobip-mobile-messaging-huawei-inbox-sdk'
+}
+apply from: "${rootProject.projectDir}/publish-mavencentral.gradle"
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/proguard-rules.pro b/infobip-mobile-messaging-huawei-inbox-sdk/proguard-rules.pro
new file mode 100644
index 00000000..73da8ea5
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+
+-keep class org.infobip.mobile.messaging.inbox.Inbox { *; }
+-keep class org.infobip.mobile.messaging.inbox.InboxData { *; }
+-keep class org.infobip.mobile.messaging.inbox.InboxMessage { *; }
\ No newline at end of file
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileInboxBroadcasterTest.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileInboxBroadcasterTest.java
new file mode 100644
index 00000000..dbcfabd1
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileInboxBroadcasterTest.java
@@ -0,0 +1,63 @@
+package org.infobip.mobile.messaging.inbox;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Intent;
+
+import org.infobip.mobile.messaging.BroadcastParameter;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.Collections;
+
+public class MobileInboxBroadcasterTest extends MobileMessagingTestCase {
+
+ private MobileInboxBroadcaster mobileInboxBroadcaster;
+ private ArgumentCaptor intentArgumentCaptor;
+
+ private String givenMessageId = "someMessageId";
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mobileInboxBroadcaster = new MobileInboxBroadcasterImpl(contextMock);
+ intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ }
+
+ @Test
+ public void should_send_inbox_fetched_event() {
+ // Given
+ Inbox inbox = createInbox(1, 1, Collections.singletonList(createMessage(givenMessageId)));
+
+ // When
+ mobileInboxBroadcaster.inboxFetched(inbox);
+
+ // Then
+ Mockito.verify(contextMock, Mockito.times(1)).sendBroadcast(intentArgumentCaptor.capture());
+
+ Intent intent = intentArgumentCaptor.getValue();
+ assertEquals(MobileInboxEvent.INBOX_MESSAGES_FETCHED.getKey(), intent.getAction());
+
+ Inbox result = Inbox.createFrom(intent.getExtras());
+ assertEquals(1, result.getCountTotal());
+ assertEquals(1, result.getCountUnread());
+ assertEquals(givenMessageId, result.getMessages().get(0).getMessageId());
+ }
+
+ @Test
+ public void should_send_seen_reports_event() {
+ // When
+ mobileInboxBroadcaster.seenReported(givenMessageId);
+
+ // Then
+ Mockito.verify(contextMock, Mockito.times(1)).sendBroadcast(intentArgumentCaptor.capture());
+
+ Intent intent = intentArgumentCaptor.getValue();
+ assertEquals(MobileInboxEvent.INBOX_SEEN_REPORTED.getKey(), intent.getAction());
+
+ String someId = intent.getExtras().toString();
+ assertEquals(givenMessageId, intent.getStringArrayExtra(BroadcastParameter.EXTRA_INBOX_SEEN_IDS)[0]);
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileInboxSynchronizerTest.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileInboxSynchronizerTest.java
new file mode 100644
index 00000000..0b3025de
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileInboxSynchronizerTest.java
@@ -0,0 +1,114 @@
+package org.infobip.mobile.messaging.inbox;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentCaptor.forClass;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import org.infobip.mobile.messaging.MobileMessaging;
+import org.infobip.mobile.messaging.api.inbox.FetchInboxResponse;
+import org.infobip.mobile.messaging.api.inbox.MobileApiInbox;
+import org.infobip.mobile.messaging.api.messages.MessageResponse;
+import org.infobip.mobile.messaging.platform.AndroidBroadcaster;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.sql.Date;
+import java.util.Collections;
+
+public class MobileInboxSynchronizerTest extends MobileMessagingTestCase {
+
+ private MobileInboxSynchronizer mobileInboxSynchronizer;
+ private AndroidBroadcaster androidBroadcaster;
+ private MobileApiInbox mobileApiInbox;
+ private MobileMessaging.ResultListener inboxResultListener = mock(MobileMessaging.ResultListener.class);
+ private ArgumentCaptor dataCaptor;
+
+ private String givenToken = "someToken";
+ private String givenExternalUserId = "someExtUID";
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ androidBroadcaster = mock(AndroidBroadcaster.class);
+ mobileApiInbox = mock(MobileApiInbox.class);
+ dataCaptor = forClass(Inbox.class);
+
+ mobileInboxSynchronizer = new MobileInboxSynchronizer(
+ context,
+ mobileMessagingCore,
+ androidBroadcaster,
+ inboxBroadcaster,
+ mobileApiInbox
+ );
+
+ given(mobileApiInbox.fetchInbox(any(), any(), any(), any(), any(), any()))
+ .willReturn(new FetchInboxResponse(1, 1, Collections.singletonList(new MessageResponse())));
+ }
+
+ @Test
+ public void should_call_api_with_bearer() {
+ mobileInboxSynchronizer.fetchInbox(givenToken, givenExternalUserId, null, inboxResultListener);
+ String resultToken = "Bearer " + givenToken;
+
+ verify(mobileApiInbox, after(300).times(1)).fetchInbox(givenExternalUserId, resultToken, null, null, null, null);
+ }
+
+ @Test
+ public void should_call_api_with_appcode() {
+ mobileInboxSynchronizer.fetchInbox(null, givenExternalUserId, null, inboxResultListener);
+
+ String resultToken = "App " + mobileMessagingCore.getApplicationCode();
+
+ verify(mobileApiInbox, after(300).times(1)).fetchInbox(givenExternalUserId, resultToken, null, null, null, null);
+ }
+
+ @Test
+ public void should_call_api_with_only_required_filterOptions() {
+ MobileInboxFilterOptions filterOptions = new MobileInboxFilterOptions(null, null, "sometopic", 15);
+
+ mobileInboxSynchronizer.fetchInbox(null, givenExternalUserId, filterOptions, inboxResultListener);
+
+ String resultToken = "App " + mobileMessagingCore.getApplicationCode();
+
+ verify(mobileApiInbox, after(300).times(1)).fetchInbox(givenExternalUserId, resultToken, null, null, "sometopic", 15);
+ }
+
+ @Test
+ public void should_call_api_with_filterOptions() {
+ Date dateFrom = new Date(1640984400000L);
+ Date dateTo = new Date(1654462800000L);
+
+ MobileInboxFilterOptions filterOptions = new MobileInboxFilterOptions(dateFrom, dateTo, "sometopic", 15);
+
+ mobileInboxSynchronizer.fetchInbox(null, givenExternalUserId, filterOptions, inboxResultListener);
+
+ String resultToken = "App " + mobileMessagingCore.getApplicationCode();
+
+ verify(mobileApiInbox, after(300).times(1)).fetchInbox(givenExternalUserId, resultToken, "1640984400000", "1654462800000", "sometopic", 15);
+
+ verify(inboxBroadcaster, after(300).atLeastOnce()).inboxFetched(dataCaptor.capture());
+ Inbox returnedInbox = dataCaptor.getValue();
+ assertEquals(1, returnedInbox.getMessages().size());
+ assertEquals(1, returnedInbox.getCountUnread());
+ assertEquals(1, returnedInbox.getCountTotal());
+ }
+
+ @Test
+ public void should_call_api_and_work_with_null_messages_response() {
+ given(mobileApiInbox.fetchInbox(any(), any(), any(), any(), any(), any()))
+ .willReturn(new FetchInboxResponse(0, 0, null));
+
+ MobileInboxFilterOptions filterOptions = new MobileInboxFilterOptions(null, null, "sometopic", 15);
+
+ mobileInboxSynchronizer.fetchInbox(null, givenExternalUserId, filterOptions, inboxResultListener);
+
+ verify(inboxBroadcaster, after(300).atLeastOnce()).inboxFetched(dataCaptor.capture());
+ Inbox returnedInbox = dataCaptor.getValue();
+ assertNull(returnedInbox.getMessages());
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileInboxTest.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileInboxTest.java
new file mode 100644
index 00000000..20f3d2a3
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileInboxTest.java
@@ -0,0 +1,96 @@
+package org.infobip.mobile.messaging.inbox;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+
+import org.infobip.mobile.messaging.MobileMessaging;
+import org.infobip.mobile.messaging.platform.AndroidBroadcaster;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class MobileInboxTest extends MobileMessagingTestCase {
+
+ private MobileInboxSynchronizer mobileInboxSynchronizer;
+ private InboxSeenStatusReporter inboxSeenStatusReporter;
+ private AndroidBroadcaster androidBroadcaster;
+ private MobileInboxImpl mobileInboxImpl;
+ private MobileMessaging.ResultListener inboxResultListener = mock(MobileMessaging.ResultListener.class);
+ private MobileMessaging.ResultListener seenResultListener = mock(MobileMessaging.ResultListener.class);
+
+
+ private String givenToken = "someToken";
+ private String givenExternalUserId = "someExtUID";
+ private String givenTopic = "someTopic";
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ androidBroadcaster = mock(AndroidBroadcaster.class);
+ mobileInboxSynchronizer = mock(MobileInboxSynchronizer.class);
+ inboxSeenStatusReporter = mock(InboxSeenStatusReporter.class);
+
+ mobileInboxImpl = new MobileInboxImpl(context,
+ androidBroadcaster,
+ inboxBroadcaster,
+ mobileApiResourceProvider,
+ mobileInboxSynchronizer,
+ inboxSeenStatusReporter
+ );
+ }
+
+ @Test
+ public void fetchInbox_should_be_called_with_null_token() {
+ MobileInboxFilterOptions filterOptions = filterOptions();
+
+ mobileInboxImpl.fetchInbox(givenExternalUserId, filterOptions, inboxResultListener);
+
+ Mockito.verify(mobileInboxSynchronizer, times(1)).fetchInbox(null, givenExternalUserId, filterOptions, inboxResultListener);
+ }
+
+ @Test
+ public void fetchInbox_should_be_called_with_provided_token() {
+ MobileInboxFilterOptions filterOptions = filterOptions();
+
+ mobileInboxImpl.fetchInbox(givenToken, givenExternalUserId, filterOptions, inboxResultListener);
+
+ Mockito.verify(mobileInboxSynchronizer, times(1)).fetchInbox(givenToken, givenExternalUserId, filterOptions, inboxResultListener);
+ }
+
+ @Test
+ public void fetchInbox_should_not_be_called_when_externalUserID_is_missing() {
+ MobileInboxFilterOptions filterOptions = filterOptions();
+
+ mobileInboxImpl.fetchInbox(null, filterOptions, inboxResultListener);
+
+ Mockito.verify(mobileInboxSynchronizer, times(0)).fetchInbox(any(), any(), any(), any());
+ }
+
+ @Test
+ public void fetchInbox_should_be_called_without_filter_options() {
+ mobileInboxImpl.fetchInbox(givenExternalUserId, null, inboxResultListener);
+
+ Mockito.verify(mobileInboxSynchronizer, times(1)).fetchInbox(null, givenExternalUserId, null, inboxResultListener);
+ }
+
+ @Test
+ public void setSeen_should_not_be_called_when_externalUserID_is_missing() {
+ mobileInboxImpl.setSeen(null, new String[]{"someMessageId1"}, seenResultListener);
+
+ Mockito.verify(inboxSeenStatusReporter, times(0)).reportSeen(any(), any(), any());
+ }
+
+ @Test
+ public void setSeen_should_not_be_called_when_messageIDs_are_missing() {
+ mobileInboxImpl.setSeen(givenExternalUserId, new String[]{}, seenResultListener);
+
+ Mockito.verify(inboxSeenStatusReporter, times(0)).reportSeen(any(), any(), any());
+ }
+
+ private MobileInboxFilterOptions filterOptions() {
+ return new MobileInboxFilterOptions(
+ null, null, givenTopic, 15
+ );
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileMessagingTestCase.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileMessagingTestCase.java
new file mode 100644
index 00000000..94ce9698
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileMessagingTestCase.java
@@ -0,0 +1,195 @@
+package org.infobip.mobile.messaging.inbox;
+
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+
+import com.google.firebase.FirebaseOptions;
+
+import org.infobip.mobile.messaging.Message;
+import org.infobip.mobile.messaging.MobileMessaging;
+import org.infobip.mobile.messaging.MobileMessagingCore;
+import org.infobip.mobile.messaging.MobileMessagingProperty;
+import org.infobip.mobile.messaging.android.MobileMessagingBaseTestCase;
+import org.infobip.mobile.messaging.api.appinstance.MobileApiAppInstance;
+import org.infobip.mobile.messaging.api.messages.MobileApiMessages;
+import org.infobip.mobile.messaging.cloud.firebase.FirebaseAppProvider;
+import org.infobip.mobile.messaging.logging.MobileMessagingLogger;
+import org.infobip.mobile.messaging.mobileapi.MobileApiResourceProvider;
+import org.infobip.mobile.messaging.notification.NotificationHandler;
+import org.infobip.mobile.messaging.platform.Broadcaster;
+import org.infobip.mobile.messaging.platform.Time;
+import org.infobip.mobile.messaging.platform.TimeProvider;
+import org.infobip.mobile.messaging.util.PreferenceHelper;
+import org.junit.After;
+import org.junit.Before;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public abstract class MobileMessagingTestCase extends MobileMessagingBaseTestCase {
+
+ protected MobileInboxBroadcaster inboxBroadcaster;
+ protected Broadcaster coreBroadcaster;
+ protected TestTimeProvider time;
+ protected MobileMessagingCore mobileMessagingCore;
+ protected MobileMessaging mobileMessaging;
+ protected NotificationHandler notificationHandler;
+
+ protected MobileApiResourceProvider mobileApiResourceProvider;
+ protected MobileApiMessages mobileApiMessages;
+ protected MobileApiAppInstance mobileApiAppInstance;
+ protected FirebaseAppProvider firebaseAppProvider;
+
+ protected static class TestTimeProvider implements TimeProvider {
+
+ long delta = 0;
+ boolean overwritten = false;
+
+ public void forward(long time, TimeUnit unit) {
+ delta += unit.toMillis(time);
+ }
+
+ public void backward(long time, TimeUnit unit) {
+ delta -= unit.toMillis(time);
+ }
+
+ public void reset() {
+ overwritten = false;
+ delta = 0;
+ }
+
+ public void set(long time) {
+ overwritten = true;
+ delta = time;
+ }
+
+ @Override
+ public long now() {
+ if (overwritten) {
+ return delta;
+ } else {
+ return System.currentTimeMillis() + delta;
+ }
+ }
+ }
+
+ @SuppressLint("ApplySharedPref")
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ PreferenceHelper.getDefaultMMSharedPreferences(context).edit().clear().commit();
+
+ PreferenceHelper.saveString(context, MobileMessagingProperty.API_URI, "http://127.0.0.1:" + debugServer.getListeningPort() + "/");
+ PreferenceHelper.saveString(context, MobileMessagingProperty.APPLICATION_CODE, "TestApplicationCode");
+ PreferenceHelper.saveString(context, MobileMessagingProperty.INFOBIP_REGISTRATION_ID, "TestDeviceInstanceId");
+ PreferenceHelper.saveString(context, MobileMessagingProperty.CLOUD_TOKEN, "TestRegistrationId");
+ PreferenceHelper.saveBoolean(context, MobileMessagingProperty.CLOUD_TOKEN_REPORTED, true);
+
+ MobileMessagingLogger.enforce();
+
+ time = new TestTimeProvider();
+ Time.reset(time);
+
+ notificationHandler = mock(NotificationHandler.class);
+ coreBroadcaster = mock(Broadcaster.class);
+ mobileApiMessages = mock(MobileApiMessages.class);
+ mobileApiAppInstance = mock(MobileApiAppInstance.class);
+ mobileApiResourceProvider = mock(MobileApiResourceProvider.class);
+ given(mobileApiResourceProvider.getMobileApiMessages(any(Context.class))).willReturn(mobileApiMessages);
+ given(mobileApiResourceProvider.getMobileApiAppInstance(any(Context.class))).willReturn(mobileApiAppInstance);
+
+ firebaseAppProvider = new FirebaseAppProvider(context);
+ FirebaseOptions firebaseOptions = new FirebaseOptions.Builder().setProjectId("project_id").setApiKey("api_key").setApplicationId("application_id").build();
+ firebaseAppProvider.setFirebaseOptions(firebaseOptions);
+
+ mobileMessagingCore = MobileMessagingTestable.create(context, coreBroadcaster, mobileApiResourceProvider, firebaseAppProvider);
+ mobileMessaging = mobileMessagingCore;
+
+ inboxBroadcaster = mock(MobileInboxBroadcaster.class);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ time.reset();
+ }
+
+ /**
+ * Generates messages with provided id, topic and seen flag
+ *
+ * @param messageId message id for a message
+ * @param topic inbox topic of a message
+ * @param isSeen is message seen
+ * @return new message
+ */
+ protected static InboxMessage createMessage(String messageId, String topic, boolean isSeen) {
+ return createMessage(messageId, new InboxData(topic, isSeen));
+ }
+
+ /**
+ * Generates messages with provided id
+ *
+ * @param messageId message id for a message
+ * @return new message
+ */
+ protected static InboxMessage createMessage(String messageId) {
+ return createMessage(messageId, new InboxData("defaultTopic", false));
+ }
+
+ /**
+ * Generates messages with provided ids and inbox data object
+ *
+ * @param messageId message id for a message
+ * @param inboxData inbox data object for a message
+ * @return new message
+ */
+ protected static InboxMessage createMessage(String messageId, InboxData inboxData) {
+ Message message = new Message();
+ message.setMessageId(messageId);
+ message.setSentTimestamp(0);
+ message.setBody("some text");
+
+ boolean isInbox = inboxData != null && inboxData.getTopic() != null;
+ if (isInbox) {
+ message.setInternalData(InboxDataMapper.inboxDataToInternalData(inboxData));
+ }
+
+ return InboxMessage.createFrom(message, inboxData);
+ }
+
+ /**
+ * Generates inbox data with provided topic
+ *
+ * @param topic inbox topic of a message
+ * @return new inbox data
+ */
+ protected static InboxData createInboxData(String topic) {
+ return createInboxData(topic, false);
+ }
+
+ /**
+ * Generates new inbox data with provided topic and seen
+ *
+ * @param topic inbox topic of a message
+ * @param isSeen is message seen
+ * @return new inbox data
+ */
+ protected static InboxData createInboxData(String topic, boolean isSeen) {
+ return new InboxData(topic, isSeen);
+ }
+
+ protected static Inbox createInbox(int countTotal, int countUnread, List messages) {
+ Inbox inbox = new Inbox();
+ inbox.setCountTotal(countTotal);
+ inbox.setCountUnread(countUnread);
+ inbox.setMessages(messages);
+ return inbox;
+ }
+
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileMessagingTestable.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileMessagingTestable.java
new file mode 100644
index 00000000..7e4b559b
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/androidTest/java/org/infobip/mobile/messaging/inbox/MobileMessagingTestable.java
@@ -0,0 +1,27 @@
+package org.infobip.mobile.messaging.inbox;
+
+import android.content.Context;
+
+import org.infobip.mobile.messaging.MobileMessagingCore;
+import org.infobip.mobile.messaging.cloud.firebase.FirebaseAppProvider;
+import org.infobip.mobile.messaging.mobileapi.MobileApiResourceProvider;
+import org.infobip.mobile.messaging.platform.Broadcaster;
+import org.infobip.mobile.messaging.platform.Platform;
+import org.infobip.mobile.messaging.util.ModuleLoader;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class MobileMessagingTestable extends MobileMessagingCore {
+
+ private MobileMessagingTestable(Context context, Broadcaster broadcaster, ExecutorService executorService, FirebaseAppProvider firebaseAppProvider) {
+ super(context, broadcaster, executorService, new ModuleLoader(context), firebaseAppProvider);
+ }
+
+ public static MobileMessagingTestable create(Context context, Broadcaster broadcaster, MobileApiResourceProvider mobileApiResourceProvider, FirebaseAppProvider firebaseAppProvider) {
+ MobileMessagingTestable instance = new MobileMessagingTestable(context, broadcaster, Executors.newSingleThreadExecutor(), firebaseAppProvider);
+ Platform.reset(instance);
+ MobileMessagingCore.mobileApiResourceProvider = mobileApiResourceProvider;
+ return instance;
+ }
+}
\ No newline at end of file
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/AndroidManifest.xml b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..c934c52d
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/AndroidManifest.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/Inbox.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/Inbox.java
new file mode 100644
index 00000000..0993e44f
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/Inbox.java
@@ -0,0 +1,67 @@
+package org.infobip.mobile.messaging.inbox;
+
+import android.os.Bundle;
+
+import org.infobip.mobile.messaging.MobileMessaging;
+import org.infobip.mobile.messaging.api.support.http.serialization.JsonSerializer;
+
+import java.util.List;
+
+/**
+ * The class encapsulates user inbox data.
+ */
+public class Inbox {
+ private static final JsonSerializer serializer = new JsonSerializer(false);
+
+ private int countTotal;
+
+ private int countUnread;
+
+ private List messages;
+
+ /**
+ * Total number of messages available in the Inbox. Maximum is limited to 100 messages.
+ */
+ public int getCountTotal() {
+ return countTotal;
+ }
+
+ public void setCountTotal(int countTotal) {
+ this.countTotal = countTotal;
+ }
+
+ /**
+ * Number of messages that not yet marked as seen/read. See {@link MobileInbox#setSeen(String, String[], MobileMessaging.ResultListener)}.
+ */
+ public int getCountUnread() {
+ return countUnread;
+ }
+
+ public void setCountUnread(int countUnread) {
+ this.countUnread = countUnread;
+ }
+
+ /**
+ * Array of inbox messages ordered by message send date-time.
+ */
+ public List getMessages() {
+ return messages;
+ }
+
+ void setMessages(List messages) {
+ this.messages = messages;
+ }
+
+ public static Inbox createFrom(Bundle bundle) {
+ return InboxBundleMapper.inboxFromBundle(bundle);
+ }
+
+ @Override
+ public String toString() {
+ return serializer.serialize(this);
+ }
+
+ public Inbox fromString(String inboxDataString) {
+ return serializer.deserialize(inboxDataString, Inbox.class);
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxBundleMapper.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxBundleMapper.java
new file mode 100644
index 00000000..f264c4a9
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxBundleMapper.java
@@ -0,0 +1,35 @@
+package org.infobip.mobile.messaging.inbox;
+
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+
+import org.infobip.mobile.messaging.dal.bundle.BundleMapper;
+
+public class InboxBundleMapper extends BundleMapper {
+
+ private static final String BUNDLED_INBOX_TAG = InboxBundleMapper.class.getName() + ".inbox";
+ private static final String BUNDLED_INBOX_MESSAGE_TAG = InboxBundleMapper.class.getName() + ".inbox.message";
+
+ /**
+ * De-serializes inbox from bundle
+ *
+ * @param bundle where to load data from
+ * @return new inbox object
+ */
+ @NonNull
+ public static Inbox inboxFromBundle(@NonNull Bundle bundle) {
+ return objectFromBundle(bundle, BUNDLED_INBOX_TAG, Inbox.class);
+ }
+
+ /**
+ * Serializes inbox message to bundle
+ *
+ * @param inbox object to serialize
+ * @return bundle with inbox contents
+ */
+ @NonNull
+ public static Bundle inboxToBundle(@NonNull Inbox inbox) {
+ return objectToBundle(inbox, BUNDLED_INBOX_TAG);
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxData.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxData.java
new file mode 100644
index 00000000..1ca4dee4
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxData.java
@@ -0,0 +1,34 @@
+package org.infobip.mobile.messaging.inbox;
+
+import org.infobip.mobile.messaging.dal.json.InternalDataMapper;
+
+public class InboxData extends InternalDataMapper.InternalData {
+
+ private Inbox inbox;
+
+ public InboxData(Inbox inbox) { this.inbox = inbox; }
+
+ public InboxData(String topic, boolean isSeen) {
+ this.inbox = new Inbox(topic, isSeen);
+ }
+
+ public String getTopic() { return getInbox().getTopic(); }
+
+ public boolean isSeen() { return getInbox().isSeen(); }
+
+ protected Inbox getInbox() { return inbox; }
+
+ static class Inbox {
+ private final String topic;
+ private final boolean seen;
+
+ public Inbox(String topic, boolean seen) {
+ this.topic = topic;
+ this.seen = seen;
+ }
+
+ public String getTopic() { return this.topic; }
+
+ public boolean isSeen() { return this.seen; }
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxDataMapper.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxDataMapper.java
new file mode 100644
index 00000000..174f5f49
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxDataMapper.java
@@ -0,0 +1,35 @@
+package org.infobip.mobile.messaging.inbox;
+
+import androidx.annotation.Nullable;
+
+import org.infobip.mobile.messaging.api.support.http.serialization.JsonSerializer;
+
+/**
+ * Used to parse Inbox stringified json from internalData
+ */
+public final class InboxDataMapper {
+
+ private static final JsonSerializer serializer = new JsonSerializer(false);
+
+ /**
+ * Serializes json data to InboxData object
+ *
+ * @param internalDataJson inboxData to deserialize
+ * @return InboxData object from json data
+ */
+ @Nullable
+ public static InboxData inboxDataFromInternalData(String internalDataJson) {
+ return internalDataJson != null ? serializer.deserialize(internalDataJson, InboxData.class) : null;
+ }
+
+ /**
+ * Serializes InboxData object to json data
+ *
+ * @param inbox inboxData object to serialize
+ * @return String - inboxData as json string
+ */
+ @Nullable
+ public static String inboxDataToInternalData(InboxData inbox) {
+ return inbox != null ? serializer.serialize(inbox) : null;
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxMapper.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxMapper.java
new file mode 100644
index 00000000..3614f9c7
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxMapper.java
@@ -0,0 +1,72 @@
+package org.infobip.mobile.messaging.inbox;
+
+import androidx.annotation.NonNull;
+
+import org.infobip.mobile.messaging.Message;
+import org.infobip.mobile.messaging.api.inbox.FetchInboxResponse;
+import org.infobip.mobile.messaging.api.messages.MessageResponse;
+import org.infobip.mobile.messaging.dal.json.InternalDataMapper;
+import org.infobip.mobile.messaging.platform.Time;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class InboxMapper {
+ public static Inbox fromBackend(@NonNull FetchInboxResponse fetchInboxResponse) {
+ Inbox inbox = new Inbox();
+ inbox.setCountTotal(fetchInboxResponse.getCountTotal());
+ inbox.setCountUnread(fetchInboxResponse.getCountUnread());
+ if (fetchInboxResponse.getMessages() != null) {
+ List inboxMessages = new ArrayList<>(fetchInboxResponse.getMessages().size());
+ for (MessageResponse messageResponse : fetchInboxResponse.getMessages()) {
+ inboxMessages.add(responseToMessage(messageResponse));
+ }
+ inbox.setMessages(inboxMessages);
+ }
+ return inbox;
+ }
+
+ private static InboxMessage responseToMessage(MessageResponse response) {
+ JSONObject customPayload = null;
+ try {
+ customPayload = response.getCustomPayload() != null ? new JSONObject(response.getCustomPayload()) : null;
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ final String internalData = response.getInternalData();
+ InboxMessage message = InboxMessage.createFrom(new Message(
+ response.getMessageId(),
+ response.getTitle(),
+ response.getBody(),
+ response.getSound(),
+ !"false".equals(response.getVibrate()),
+ null,
+ "true".equals(response.getSilent()),
+ response.getCategory(),
+ null,
+ Time.now(),
+ 0,
+ InternalDataMapper.getInternalDataSendDateTime(internalData),
+ customPayload,
+ internalData,
+ null,
+ Message.Status.UNKNOWN,
+ null,
+ InternalDataMapper.getInternalDataContentUrl(internalData),
+ InternalDataMapper.getInternalDataInAppStyle(internalData),
+ InternalDataMapper.getInternalDataInAppExpiryDateTime(internalData),
+ InternalDataMapper.getInternalDataWebViewUrl(internalData),
+ InternalDataMapper.getInternalDataBrowserUrl(internalData),
+ InternalDataMapper.getInternalDataMessageType(internalData),
+ InternalDataMapper.getInternalDataDeeplinkUri(internalData),
+ InternalDataMapper.getInternalDataInAppOpenTitle(internalData),
+ InternalDataMapper.getInternalDataInAppDismissTitle(internalData)),
+ InboxDataMapper.inboxDataFromInternalData(internalData));
+
+ InternalDataMapper.updateMessageWithInternalData(message, internalData);
+ return message;
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxMessage.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxMessage.java
new file mode 100644
index 00000000..22a9a88b
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxMessage.java
@@ -0,0 +1,47 @@
+package org.infobip.mobile.messaging.inbox;
+
+import org.infobip.mobile.messaging.Message;
+import org.json.JSONObject;
+
+public class InboxMessage extends Message {
+
+ private InboxData inboxData;
+
+ public static InboxMessage createFrom(Message message, InboxData inboxData) {
+ return new InboxMessage(message.getMessageId(), message.getTitle(), message.getBody(), message.getSound(), message.isVibrate(), message.getIcon(),
+ message.isSilent(), message.getCategory(), message.getFrom(), message.getReceivedTimestamp(), message.getSeenTimestamp(), message.getSentTimestamp(),
+ message.getCustomPayload(), message.getInternalData(), message.getDestination(), message.getStatus(), message.getStatusMessage(), message.getContentUrl(),
+ inboxData, message.getInAppStyle(), message.getInAppExpiryTimestamp(), message.getWebViewUrl(), message.getBrowserUrl(), message.getMessageType(), message.getDeeplink(),
+ message.getInAppOpenTitle(), message.getInAppDismissTitle());
+ }
+
+ static Message toMessage(InboxMessage inboxMessage) {
+ return new Message(inboxMessage.getMessageId(), inboxMessage.getTitle(), inboxMessage.getBody(), inboxMessage.getSound(), inboxMessage.isVibrate(), inboxMessage.getIcon(),
+ inboxMessage.isSilent(), inboxMessage.getCategory(), inboxMessage.getFrom(), inboxMessage.getReceivedTimestamp(), inboxMessage.getSeenTimestamp(), inboxMessage.getSentTimestamp(),
+ inboxMessage.getCustomPayload(), inboxMessage.getInternalData(), inboxMessage.getDestination(), inboxMessage.getStatus(), inboxMessage.getStatusMessage(), inboxMessage.getContentUrl(),
+ inboxMessage.getInAppStyle(), inboxMessage.getInAppExpiryTimestamp(), inboxMessage.getWebViewUrl(), inboxMessage.getBrowserUrl(), inboxMessage.getMessageType(), inboxMessage.getDeeplink(),
+ inboxMessage.getInAppOpenTitle(), inboxMessage.getInAppDismissTitle());
+ }
+
+ private InboxMessage(String messageId, String title, String body, String sound, boolean vibrate, String icon, boolean silent, String category,
+ String from, long receivedTimestamp, long seenTimestamp, long sentTimestamp, JSONObject customPayload, String internalData,
+ String destination, Status status, String statusMessage, String contentUrl, InboxData inboxData, InAppStyle inAppStyle, long expiryTime,
+ String webViewUrl, String browserUrl, String messageType, String deeplink, String inAppOpenTitle, String inAppDismissTitle) {
+ super(messageId, title, body, sound, vibrate, icon, silent, category, from, receivedTimestamp, seenTimestamp, sentTimestamp, customPayload,
+ internalData, destination, status, statusMessage, contentUrl, inAppStyle, expiryTime, webViewUrl, browserUrl, messageType, deeplink,
+ inAppOpenTitle, inAppDismissTitle);
+ this.inboxData = inboxData;
+ }
+
+ public String getTopic() { return getInboxData().getTopic(); }
+
+ void setInboxData(InboxData inboxData) { this.inboxData = inboxData; }
+
+ InboxData getInboxData() { return this.inboxData; }
+
+ public boolean isSeen() { return getInboxData().isSeen(); }
+
+ public void setSeen() {
+ setInboxData(new InboxData(getInboxData().getTopic(), true));
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxSeenMessagesMapper.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxSeenMessagesMapper.java
new file mode 100644
index 00000000..0f54819f
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxSeenMessagesMapper.java
@@ -0,0 +1,23 @@
+package org.infobip.mobile.messaging.inbox;
+
+import org.infobip.mobile.messaging.api.inbox.InboxSeenMessages;
+import org.infobip.mobile.messaging.mobileapi.seen.SeenMessagesMapper;
+import org.infobip.mobile.messaging.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class InboxSeenMessagesMapper extends InboxSeenMessages {
+
+ static InboxSeenMessages fromMessageIds(String externalUserId, String[] messageIds) {
+ List messages = new ArrayList<>();
+ for (String seenMessage : messageIds) {
+ String[] messageIdWithTimestamp = seenMessage.split(StringUtils.COMMA_WITH_SPACE);
+ String messageId = messageIdWithTimestamp[0];
+ String seenTimestampString = messageIdWithTimestamp[1];
+
+ messages.add(new Message(messageId, SeenMessagesMapper.countDeltaInSeconds(seenTimestampString)));
+ }
+ return new InboxSeenMessages(externalUserId, messages.toArray(new Message[0]));
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxSeenStatusReporter.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxSeenStatusReporter.java
new file mode 100644
index 00000000..eb4db801
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/InboxSeenStatusReporter.java
@@ -0,0 +1,91 @@
+package org.infobip.mobile.messaging.inbox;
+
+import android.content.Context;
+
+import org.infobip.mobile.messaging.MobileMessaging;
+import org.infobip.mobile.messaging.MobileMessagingCore;
+import org.infobip.mobile.messaging.api.inbox.InboxSeenMessages;
+import org.infobip.mobile.messaging.api.inbox.MobileApiInbox;
+import org.infobip.mobile.messaging.logging.MobileMessagingLogger;
+import org.infobip.mobile.messaging.mobileapi.InternalSdkError;
+import org.infobip.mobile.messaging.mobileapi.MobileMessagingError;
+import org.infobip.mobile.messaging.mobileapi.Result;
+import org.infobip.mobile.messaging.mobileapi.common.MRetryPolicy;
+import org.infobip.mobile.messaging.mobileapi.common.MRetryableTask;
+import org.infobip.mobile.messaging.mobileapi.common.RetryPolicyProvider;
+import org.infobip.mobile.messaging.mobileapi.common.exceptions.BackendInvalidParameterException;
+import org.infobip.mobile.messaging.platform.AndroidBroadcaster;
+import org.infobip.mobile.messaging.util.StringUtils;
+
+public class InboxSeenStatusReporter {
+
+ private final Context context;
+ private final MobileMessagingCore mobileMessagingCore;
+ private final AndroidBroadcaster coreBroadcaster;
+ private final MobileInboxBroadcaster broadcaster;
+ private final MobileApiInbox mobileApiInbox;
+ private final MRetryPolicy retryPolicy;
+
+ public InboxSeenStatusReporter(
+ Context context,
+ MobileMessagingCore mobileMessagingCore,
+ AndroidBroadcaster coreBroadcaster,
+ MobileInboxBroadcaster broadcaster,
+ MobileApiInbox mobileApiInbox
+ ) {
+
+ this.context = context;
+ this.mobileMessagingCore = mobileMessagingCore;
+ this.coreBroadcaster = coreBroadcaster;
+ this.broadcaster = broadcaster;
+ this.mobileApiInbox = mobileApiInbox;
+ this.retryPolicy = new RetryPolicyProvider(context).DEFAULT();
+ }
+
+ public void reportSeen(MobileMessaging.ResultListener listener, String externalUserId, String... messageIDs ) {
+
+ if (StringUtils.isBlank(mobileMessagingCore.getPushRegistrationId())) {
+ MobileMessagingLogger.w("[INBOX] Can't report inbox seen status without valid registration");
+ if (listener != null) {
+ listener.onResult(new Result<>(messageIDs, InternalSdkError.NO_VALID_REGISTRATION.getError()));
+ }
+ return;
+ }
+
+ new MRetryableTask() {
+ @Override
+ public String[] run(Void[] voids) {
+ InboxSeenMessages seenMessages = InboxSeenMessagesMapper.fromMessageIds(externalUserId, messageIDs);
+ MobileMessagingLogger.v("[INBOX] SEEN >>>", seenMessages);
+ mobileApiInbox.reportSeen(seenMessages);
+ MobileMessagingLogger.v("[INBOX] SEEN DONE <<<");
+ return messageIDs;
+ }
+
+ @Override
+ public void after(String[] messageIdsWithTimestamp) {
+ broadcaster.seenReported(messageIdsWithTimestamp);
+ if (listener != null) {
+ listener.onResult(new Result<>(messageIdsWithTimestamp));
+ }
+ }
+
+ @Override
+ public void error(Throwable error) {
+ MobileMessagingLogger.e("[INBOX] Error reporting seen status!", error);
+ MobileMessagingError mobileMessagingError = MobileMessagingError.createFrom(error);
+
+ if (error instanceof BackendInvalidParameterException) {
+ mobileMessagingCore.handleNoRegistrationError(mobileMessagingError);
+ }
+
+ coreBroadcaster.error(mobileMessagingError);
+ if (listener != null) {
+ listener.onResult(new Result<>(mobileMessagingError));
+ }
+ }
+ }
+ .retryWith(retryPolicy)
+ .execute();
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInbox.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInbox.java
new file mode 100644
index 00000000..30c8a787
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInbox.java
@@ -0,0 +1,57 @@
+package org.infobip.mobile.messaging.inbox;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import org.infobip.mobile.messaging.MobileMessaging;
+
+/**
+ * Main interface for inbox
+ */
+public abstract class MobileInbox {
+
+ /**
+ * Returns instance of inbox api
+ *
+ * @param context android context
+ * @return instance of inbox api
+ */
+ public synchronized static MobileInbox getInstance(Context context) {
+ return MobileInboxImpl.getInstance(context);
+ }
+
+ /**
+ * Asynchronously fetches inbox data for authorised user.
+ *
+ * @param token access token (JWT in a strictly predefined format) required for current user to have access to the Inbox messages.
+ * @param externalUserId External User ID is meant to be an ID of a user in an external (non-Infobip) service.
+ * @param filterOptions filtering options applied to messages list in response. Nullable, will return default number of messages
+ * @param messageResultListener listener to report the result on
+ * @see MobileMessaging.ResultListener
+ */
+ public abstract void fetchInbox(String token, @NonNull String externalUserId, MobileInboxFilterOptions filterOptions, MobileMessaging.ResultListener messageResultListener);
+
+ /**
+ * Asynchronously fetches inbox data for authorised user. Uses application Code for authorization.
+ *
+ * This version of API uses Application Code (or API key) based authorization. This is not the most secure way of authorization because it is heavily dependent on how secure is your Application Code stored on a device.
+ * If the security is a crucial aspect, consider using the alternative `fetchInbox` API that is based on access token authorization.
+ * @param externalUserId External User ID is meant to be an ID of a user in an external (non-Infobip) service.
+ * @param filterOptions filtering options applied to messages list in response. Nullable, will return default number of messages
+ * @param messageResultListener listener to report the result on
+ * @see MobileMessaging.ResultListener
+ */
+
+ public abstract void fetchInbox(@NonNull String externalUserId, MobileInboxFilterOptions filterOptions, MobileMessaging.ResultListener messageResultListener);
+
+ /**
+ * Asynchronously marks inbox messages as seen
+ *
+ * @param externalUserId External User ID is meant to be an ID of a user in an external (non-Infobip) service.
+ * @param messageIds array of inbox messages identifiers that need to be marked as seen.
+ * @param listener listener to report the result on
+ * @see MobileMessaging.ResultListener
+ */
+ public abstract void setSeen(@NonNull String externalUserId, @NonNull String[] messageIds, MobileMessaging.ResultListener listener);
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxBroadcaster.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxBroadcaster.java
new file mode 100644
index 00000000..26ff4565
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxBroadcaster.java
@@ -0,0 +1,17 @@
+package org.infobip.mobile.messaging.inbox;
+
+public interface MobileInboxBroadcaster {
+ /**
+ * Sends broadcast that Inbox messages are fetched
+ *
+ * @param inbox inbox object received
+ */
+ void inboxFetched(Inbox inbox);
+
+ /**
+ * Sends broadcast with message ids which were reported as seen by the library
+ *
+ * @param messageIDs ids of inbox messages marked as seen
+ */
+ void seenReported(String... messageIDs);
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxBroadcasterImpl.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxBroadcasterImpl.java
new file mode 100644
index 00000000..fc8ed600
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxBroadcasterImpl.java
@@ -0,0 +1,56 @@
+package org.infobip.mobile.messaging.inbox;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import org.infobip.mobile.messaging.BroadcastParameter;
+import org.infobip.mobile.messaging.logging.MobileMessagingLogger;
+
+public class MobileInboxBroadcasterImpl implements MobileInboxBroadcaster {
+ private final Context context;
+
+ public MobileInboxBroadcasterImpl(@NonNull Context context) {
+ this.context = context;
+ }
+
+ @Override
+ public void inboxFetched(Inbox inbox) {
+ send(prepare(MobileInboxEvent.INBOX_MESSAGES_FETCHED)
+ .putExtras(InboxBundleMapper.inboxToBundle(inbox)));
+ }
+
+ @Override
+ public void seenReported(@NonNull String... messageIds) {
+ if (messageIds.length == 0) {
+ return;
+ }
+
+ Intent seenReportsSent = prepare(MobileInboxEvent.INBOX_SEEN_REPORTED);
+ Bundle extras = new Bundle();
+ extras.putStringArray(BroadcastParameter.EXTRA_INBOX_SEEN_IDS, messageIds);
+ seenReportsSent.putExtras(extras);
+ send(seenReportsSent);
+ }
+
+ private void send(Intent intent) {
+ try {
+ context.sendBroadcast(intent);
+ LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
+ } catch (Exception ex) {
+ MobileMessagingLogger.e("Failed to send broadcast for action " + intent.getAction() + " due to exception " + ex.getMessage());
+ }
+ }
+
+ private Intent prepare(MobileInboxEvent event) {
+ return prepare(event.getKey());
+ }
+
+ private Intent prepare(String event) {
+ return new Intent(event)
+ .setPackage(context.getPackageName());
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxEvent.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxEvent.java
new file mode 100644
index 00000000..b7cdbc3c
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxEvent.java
@@ -0,0 +1,17 @@
+package org.infobip.mobile.messaging.inbox;
+
+public enum MobileInboxEvent {
+ /**
+ * It is triggered when Inbox messages fetched
+ */
+ INBOX_MESSAGES_FETCHED("org.infobip.mobile.messaging.inbox.INBOX_MESSAGES_FETCHED"),
+ INBOX_COUNT_UNREAD("org.infobip.mobile.messaging.inbox.INBOX_COUNT_UNREAD"),
+ INBOX_COUNT_TOTAL("org.infobip.mobile.messaging.inbox.INBOX_COUNT_TOTAL"),
+ INBOX_SEEN_REPORTED("org.infobip.mobile.messaging.inbox.INBOX_SEEN_REPORTED");
+
+ private final String key;
+
+ MobileInboxEvent(String key) { this.key = key; }
+
+ public String getKey() { return key; }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxFilterOptions.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxFilterOptions.java
new file mode 100644
index 00000000..bb1c98da
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxFilterOptions.java
@@ -0,0 +1,66 @@
+package org.infobip.mobile.messaging.inbox;
+
+import java.util.Date;
+
+/**
+ * Class with filtering options for fetching inbox
+ */
+
+public class MobileInboxFilterOptions {
+ /**
+ * fromDateTime - date time from which Inbox will be fetched
+ */
+ private Date fromDateTime;
+ /**
+ * toDateTime - date time to which Inbox will be fetched
+ */
+ private Date toDateTime;
+ /**
+ * topic - messages' topic to be fetched
+ */
+ private String topic;
+ /**
+ * limit - number of messages to be fetched
+ */
+ private Integer limit;
+
+ public MobileInboxFilterOptions(Date fromDateTime,
+ Date toDateTime,
+ String topic,
+ Integer limit) {
+ this.fromDateTime = fromDateTime;
+ this.toDateTime = toDateTime;
+ this.topic = topic;
+ this.limit = limit;
+ }
+
+ public Date getFromDateTime() {
+ return fromDateTime;
+ }
+
+ public void setFromDateTime(Date fromDateTime) {
+ this.fromDateTime = fromDateTime;
+ }
+
+ public Date getToDateTime() {
+ return toDateTime;
+ }
+
+ public void setToDateTime(Date toDateTime) {
+ this.toDateTime = toDateTime;
+ }
+
+ public String getTopic() { return topic; }
+
+ public void setTopic(String topic) {
+ this.topic = topic;
+ }
+
+ public Integer getLimit() {
+ return limit;
+ }
+
+ public void setLimit(Integer limit) {
+ this.limit = limit;
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxImpl.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxImpl.java
new file mode 100644
index 00000000..f13275a6
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxImpl.java
@@ -0,0 +1,193 @@
+package org.infobip.mobile.messaging.inbox;
+
+import static org.infobip.mobile.messaging.api.support.util.StringUtils.isBlank;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import org.infobip.mobile.messaging.Message;
+import org.infobip.mobile.messaging.MessageHandlerModule;
+import org.infobip.mobile.messaging.MobileMessaging;
+import org.infobip.mobile.messaging.MobileMessagingCore;
+import org.infobip.mobile.messaging.logging.MobileMessagingLogger;
+import org.infobip.mobile.messaging.mobileapi.MobileApiResourceProvider;
+import org.infobip.mobile.messaging.platform.AndroidBroadcaster;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class MobileInboxImpl extends MobileInbox implements MessageHandlerModule {
+ @SuppressLint("StaticFieldLeak")
+ private static MobileInboxImpl instance;
+
+ private Context context;
+ private AndroidBroadcaster coreBroadcaster;
+ private MobileInboxBroadcaster mobileInboxBroadcaster;
+ private MobileApiResourceProvider mobileApiResourceProvider;
+ private MobileInboxSynchronizer mobileInboxSynchronizer;
+ private InboxSeenStatusReporter inboxSeenStatusReporter;
+
+ public static MobileInboxImpl getInstance(Context context) {
+ if (instance == null) {
+ instance = MobileMessagingCore.getInstance(context).getMessageHandlerModule(MobileInboxImpl.class);
+ }
+ return instance;
+ }
+
+ public MobileInboxImpl() {
+ }
+
+ public MobileInboxImpl(Context context, AndroidBroadcaster coreBroadcaster,
+ MobileInboxBroadcaster mobileInboxBroadcaster,
+ MobileApiResourceProvider mobileApiResourceProvider,
+ MobileInboxSynchronizer mobileInboxSynchronizer,
+ InboxSeenStatusReporter inboxSeenStatusReporter) {
+ this.context = context;
+ this.coreBroadcaster = coreBroadcaster;
+ this.mobileInboxBroadcaster = mobileInboxBroadcaster;
+ this.mobileApiResourceProvider = mobileApiResourceProvider;
+ this.mobileInboxSynchronizer = mobileInboxSynchronizer;
+ this.inboxSeenStatusReporter = inboxSeenStatusReporter;
+ }
+
+ @Override
+ public void fetchInbox(@NonNull String token, @NonNull String externalUserId, MobileInboxFilterOptions filterOptions, MobileMessaging.ResultListener messageResultListener) {
+ if (isBlank(token) || isBlank(externalUserId)) {
+ MobileMessagingLogger.e("[Inbox] One or more required parameters is empty. Check token and externalUserId");
+ return;
+ }
+ mobileInboxSynchronizer().fetchInbox(token, externalUserId, filterOptions, messageResultListener);
+ }
+
+ @Override
+ public void fetchInbox(@NonNull String externalUserId, MobileInboxFilterOptions filterOptions, MobileMessaging.ResultListener messageResultListener) {
+ if (isBlank(externalUserId)) {
+ MobileMessagingLogger.e("[Inbox] externalUserId was empty");
+ return;
+ }
+ mobileInboxSynchronizer().fetchInbox(null, externalUserId, filterOptions, messageResultListener);
+ }
+
+ @Override
+ public void setSeen(@NonNull String externalUserId, @NonNull String[] messageIDs, MobileMessaging.ResultListener listener) {
+ if (isBlank(externalUserId)) {
+ MobileMessagingLogger.e("[Inbox] externalUserId was empty");
+ return;
+ }
+ if (messageIDs.length == 0) {
+ MobileMessagingLogger.w("[Inbox] No messages to report");
+ return;
+ }
+ inboxSeenStatusReporter().reportSeen(listener, externalUserId, MobileMessagingCore.getInstance(context).enrichMessageIdsWithTimestamp(messageIDs));
+ }
+
+ @Override
+ public void init(Context appContext) {
+ this.context = appContext;
+ }
+
+ @Override
+ public boolean handleMessage(Message message) {
+ if (!hasInbox(message)) {
+ return false;
+ }
+ coreBroadcaster().messageReceived(message);
+ MobileMessagingLogger.d("Message with id: " + message.getMessageId() + " will be handled by Inbox MessageHandler");
+ return true;
+ }
+
+ synchronized private AndroidBroadcaster coreBroadcaster() {
+ if (coreBroadcaster == null) {
+ coreBroadcaster = new AndroidBroadcaster(context);
+ }
+ return coreBroadcaster;
+ }
+
+ synchronized private MobileInboxBroadcaster mobileInboxBroadcaster() {
+ if (mobileInboxBroadcaster == null) {
+ mobileInboxBroadcaster = new MobileInboxBroadcasterImpl(context);
+ }
+ return mobileInboxBroadcaster;
+ }
+
+ synchronized private MobileApiResourceProvider mobileApiResourceProvider() {
+ if (mobileApiResourceProvider == null) {
+ mobileApiResourceProvider = new MobileApiResourceProvider();
+ }
+ return mobileApiResourceProvider;
+ }
+
+ @Override
+ public boolean messageTapped(Message message) {
+ return false;
+ }
+
+ @Override
+ public void applicationInForeground() {
+ performSyncActions();
+ }
+
+ @Override
+ public void cleanup() {
+ cleanupInboxData();
+ }
+
+ private void cleanupInboxData() {
+ mobileApiResourceProvider = null;
+ mobileInboxSynchronizer = null;
+ inboxSeenStatusReporter = null;
+ }
+
+ @Override
+ public void depersonalize() {
+ cleanupInboxData();
+ }
+
+ @Override
+ public void performSyncActions() {
+ //do nothing
+ }
+
+ private static boolean hasInbox(Message message) {
+ if (message == null || message.getInternalData() == null) {
+ return false;
+ }
+
+ try {
+ JSONObject inbox = new JSONObject(message.getInternalData());
+ JSONArray topic = inbox.optJSONArray("inbox");
+ return topic != null && topic.length() > 0;
+ } catch (JSONException e) {
+ MobileMessagingLogger.e(e.getMessage());
+ return false;
+ }
+ }
+
+ synchronized private MobileInboxSynchronizer mobileInboxSynchronizer() {
+ if (mobileInboxSynchronizer == null) {
+ mobileInboxSynchronizer = new MobileInboxSynchronizer(
+ context,
+ MobileMessagingCore.getInstance(context),
+ coreBroadcaster(),
+ mobileInboxBroadcaster(),
+ mobileApiResourceProvider().getMobileApiInbox(context)
+ );
+ }
+ return mobileInboxSynchronizer;
+ }
+
+ synchronized private InboxSeenStatusReporter inboxSeenStatusReporter() {
+ if (inboxSeenStatusReporter == null) {
+ inboxSeenStatusReporter = new InboxSeenStatusReporter(
+ context,
+ MobileMessagingCore.getInstance(context),
+ coreBroadcaster(),
+ mobileInboxBroadcaster(),
+ mobileApiResourceProvider().getMobileApiInbox(context)
+ );
+ }
+ return inboxSeenStatusReporter;
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxSynchronizer.java b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxSynchronizer.java
new file mode 100644
index 00000000..399f6de8
--- /dev/null
+++ b/infobip-mobile-messaging-huawei-inbox-sdk/src/main/java/org/infobip/mobile/messaging/inbox/MobileInboxSynchronizer.java
@@ -0,0 +1,102 @@
+package org.infobip.mobile.messaging.inbox;
+
+import static org.infobip.mobile.messaging.util.StringUtils.isBlank;
+
+import android.content.Context;
+
+import org.infobip.mobile.messaging.MobileMessaging;
+import org.infobip.mobile.messaging.MobileMessagingCore;
+import org.infobip.mobile.messaging.api.inbox.FetchInboxResponse;
+import org.infobip.mobile.messaging.api.inbox.MobileApiInbox;
+import org.infobip.mobile.messaging.logging.MobileMessagingLogger;
+import org.infobip.mobile.messaging.mobileapi.InternalSdkError;
+import org.infobip.mobile.messaging.mobileapi.MobileMessagingError;
+import org.infobip.mobile.messaging.mobileapi.Result;
+import org.infobip.mobile.messaging.mobileapi.common.MRetryPolicy;
+import org.infobip.mobile.messaging.mobileapi.common.MRetryableTask;
+import org.infobip.mobile.messaging.mobileapi.common.RetryPolicyProvider;
+import org.infobip.mobile.messaging.mobileapi.common.exceptions.BackendInvalidParameterException;
+import org.infobip.mobile.messaging.platform.AndroidBroadcaster;
+import org.infobip.mobile.messaging.platform.Platform;
+
+public class MobileInboxSynchronizer {
+
+ private final Context context;
+ private final MobileMessagingCore mobileMessagingCore;
+ private final AndroidBroadcaster coreBroadcaster;
+ private final MobileInboxBroadcaster mobileInboxBroadcaster;
+ private final MobileApiInbox mobileApiInbox;
+ private final MRetryPolicy retryPolicy;
+
+ public MobileInboxSynchronizer(Context context,
+ MobileMessagingCore mobileMessagingCore,
+ AndroidBroadcaster coreBroadcaster,
+ MobileInboxBroadcaster mobileInboxBroadcaster,
+ MobileApiInbox mobileApiInbox) {
+ this.context = context;
+ this.mobileMessagingCore = mobileMessagingCore;
+ this.coreBroadcaster = coreBroadcaster;
+ this.mobileApiInbox = mobileApiInbox;
+ this.mobileInboxBroadcaster = mobileInboxBroadcaster;
+ this.retryPolicy = new RetryPolicyProvider(context).DEFAULT();
+ }
+
+ public void fetchInbox(String token, String externalUserId, MobileInboxFilterOptions filterOptions, MobileMessaging.ResultListener listener) {
+ if (!mobileMessagingCore.isRegistrationAvailable()) {
+ if (listener != null) {
+ listener.onResult(new Result<>(InternalSdkError.NO_VALID_REGISTRATION.getError()));
+ }
+ return;
+ }
+
+ new MRetryableTask() {
+
+ @Override
+ public FetchInboxResponse run(Void[] voids) {
+ MobileMessagingLogger.v("FETCHING INBOX >>>");
+ String header = token != null ? "Bearer " + token : "App " + mobileMessagingCore.getApplicationCode();
+ if (filterOptions == null) {
+ return mobileApiInbox.fetchInbox(externalUserId, header, null, null, null, null, Platform.usedPushServiceType.name().toLowerCase());
+ }
+ String from = filterOptions.getFromDateTime() == null ? null : String.valueOf(filterOptions.getFromDateTime().getTime());
+ String to = filterOptions.getToDateTime() == null ? null : String.valueOf(filterOptions.getToDateTime().getTime());
+ String topic = isBlank(filterOptions.getTopic()) ? null : filterOptions.getTopic();
+ Integer limit = filterOptions.getLimit();
+ return mobileApiInbox.fetchInbox(externalUserId, header, from, to, topic, limit, Platform.usedPushServiceType.name().toLowerCase());
+ }
+
+ @Override
+ public void after(FetchInboxResponse fetchInboxResponse) {
+ if (fetchInboxResponse == null) {
+ MobileMessagingLogger.v("FETCHING INBOX WAS NULL <<<");
+ listener.onResult(new Result<>(new Inbox()));
+ return;
+ }
+ MobileMessagingLogger.v("FETCHING INBOX DONE <<<");
+ Inbox inbox = InboxMapper.fromBackend(fetchInboxResponse);
+ mobileInboxBroadcaster.inboxFetched(inbox);
+
+ if (listener != null) {
+ listener.onResult(new Result<>(inbox));
+ }
+ }
+
+ @Override
+ public void error(Throwable error) {
+ MobileMessagingLogger.v("FETCHING INBOX ERROR <<<", error);
+ MobileMessagingError mobileMessagingError = MobileMessagingError.createFrom(error);
+
+ if (error instanceof BackendInvalidParameterException) {
+ mobileMessagingCore.handleNoRegistrationError(mobileMessagingError);
+ }
+
+ coreBroadcaster.error(mobileMessagingError);
+ if (listener != null) {
+ listener.onResult(new Result<>(mobileMessagingError));
+ }
+ }
+ }
+ .retryWith(retryPolicy)
+ .execute();
+ }
+}
diff --git a/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/BroadcastParameter.java b/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/BroadcastParameter.java
index 90fc108d..54607bc4 100644
--- a/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/BroadcastParameter.java
+++ b/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/BroadcastParameter.java
@@ -22,6 +22,9 @@ public final class BroadcastParameter {
public static final String EXTRA_UNREAD_CHAT_MESSAGES_COUNT = "org.infobip.mobile.messaging.unread.chat.messages.count";
+ public static final String EXTRA_INBOX = "org.infobip.mobile.messaging.inbox";
+ public static final String EXTRA_INBOX_SEEN_IDS = "org.infobip.mobile.messaging.inbox.seen.ids";
+
public static final int NOTIFICATION_NOT_DISPLAYED_ID = -1;
private BroadcastParameter() {
diff --git a/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/Message.java b/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/Message.java
index a6f2a541..9ba7aef4 100644
--- a/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/Message.java
+++ b/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/Message.java
@@ -25,6 +25,7 @@ public class Message implements Comparable {
public static final String MESSAGE_TYPE_CHAT = "chat";
public static final String MESSAGE_TYPE_GEO = "geo";
+
private String messageId;
private String title;
private String body;
diff --git a/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/MobileMessagingCore.java b/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/MobileMessagingCore.java
index 8ecf3b00..c2bcd6ae 100644
--- a/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/MobileMessagingCore.java
+++ b/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/MobileMessagingCore.java
@@ -9,7 +9,6 @@
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
-
import android.text.TextUtils;
import android.util.Pair;
@@ -698,7 +697,7 @@ public void addUnreportedMessageIds(String... messageIDs) {
}
public void addSyncMessagesIds(String... messageIDs) {
- String[] timestampMessageIdPair = concatTimestampToMessageId(messageIDs);
+ String[] timestampMessageIdPair = enrichMessageIdsWithTimestamp(messageIDs);
PreferenceHelper.appendToStringArray(context, MobileMessagingProperty.INFOBIP_SYNC_MESSAGES_IDS, timestampMessageIdPair);
}
@@ -809,11 +808,11 @@ private void removeGeneratedMessageIds(final String... messageIDs) {
}
private void addUnreportedSeenMessageIds(final String... messageIDs) {
- String[] seenMessages = concatTimestampToMessageId(messageIDs);
+ String[] seenMessages = enrichMessageIdsWithTimestamp(messageIDs);
PreferenceHelper.appendToStringArray(context, MobileMessagingProperty.INFOBIP_UNREPORTED_SEEN_MESSAGE_IDS, seenMessages);
}
- private String[] concatTimestampToMessageId(String[] messageIDs) {
+ public String[] enrichMessageIdsWithTimestamp(String[] messageIDs) {
List syncMessages = new ArrayList<>(messageIDs.length);
if (messageIDs.length > 0) {
for (String messageId : messageIDs) {
diff --git a/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/mobileapi/MobileApiResourceProvider.java b/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/mobileapi/MobileApiResourceProvider.java
index a4941938..19338d3d 100644
--- a/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/mobileapi/MobileApiResourceProvider.java
+++ b/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/mobileapi/MobileApiResourceProvider.java
@@ -8,6 +8,7 @@
import org.infobip.mobile.messaging.api.baseurl.MobileApiBaseUrl;
import org.infobip.mobile.messaging.api.chat.MobileApiChat;
import org.infobip.mobile.messaging.api.geo.MobileApiGeo;
+import org.infobip.mobile.messaging.api.inbox.MobileApiInbox;
import org.infobip.mobile.messaging.api.messages.MobileApiMessages;
import org.infobip.mobile.messaging.api.support.CustomApiHeaders;
import org.infobip.mobile.messaging.api.support.Generator;
@@ -93,6 +94,7 @@ public void beforeResponse(Exception error) {
private MobileApiAppInstance mobileApiAppInstance;
private MobileApiChat mobileApiChat;
private MobileApiBaseUrl mobileApiBaseUrl;
+ private MobileApiInbox mobileApiInbox;
public MobileApiMessages getMobileApiMessages(Context context) {
if (null != mobileApiMessages) {
@@ -154,6 +156,16 @@ public MobileApiBaseUrl getMobileApiBaseUrl(Context context) {
return mobileApiBaseUrl;
}
+ public MobileApiInbox getMobileApiInbox(Context context) {
+ if (null != mobileApiInbox) {
+ return mobileApiInbox;
+ }
+
+ mobileApiInbox = getGenerator(context).create(MobileApiInbox.class);
+
+ return mobileApiInbox;
+ }
+
private String[] getUserAgentAdditions(Context context) {
List userAgentAdditions = new ArrayList<>();
if (PreferenceHelper.findBoolean(context, MobileMessagingProperty.REPORT_SYSTEM_INFO)) {
diff --git a/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/mobileapi/seen/SeenMessagesMapper.java b/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/mobileapi/seen/SeenMessagesMapper.java
index 74cb7cf4..bfa041a1 100644
--- a/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/mobileapi/seen/SeenMessagesMapper.java
+++ b/infobip-mobile-messaging-huawei-sdk/src/main/java/org/infobip/mobile/messaging/mobileapi/seen/SeenMessagesMapper.java
@@ -11,7 +11,7 @@
* @author sslavin
* @since 25/04/16.
*/
-class SeenMessagesMapper extends SeenMessages {
+public class SeenMessagesMapper extends SeenMessages {
static SeenMessages fromMessageIds(String[] messageIds) {
List messages = new ArrayList<>();
@@ -20,12 +20,14 @@ static SeenMessages fromMessageIds(String[] messageIds) {
String messageId = messageIdWithTimestamp[0];
String seenTimestampString = messageIdWithTimestamp[1];
- long seenTimestamp = Long.valueOf(seenTimestampString);
- long deltaTimestamp = Time.now() - seenTimestamp;
- long deltaInSeconds = Math.round((float) deltaTimestamp / 1000);
-
- messages.add(new Message(messageId, deltaInSeconds));
+ messages.add(new Message(messageId, countDeltaInSeconds(seenTimestampString)));
}
return new SeenMessages(messages.toArray(new Message[0]));
}
+
+ public static long countDeltaInSeconds(String seenTimestampString) {
+ long seenTimestamp = Long.valueOf(seenTimestampString);
+ long deltaTimestamp = Time.now() - seenTimestamp;
+ return Math.round((float) deltaTimestamp / 1000);
+ }
}
diff --git a/settings.gradle b/settings.gradle
index 6b3a916d..149924ff 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1,18 @@
-include ':infobip-mobile-messaging-huawei-sdk', ':infobip-mobile-messaging-huawei-demo', ':infobip-mobile-messaging-huawei-test', ':infobip-mobile-messaging-huawei-chat-sdk', ':infobip-mobile-messaging-huawei-cryptor-migration'
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ // Configure the Maven repository address for the HMS Core SDK.
+ maven { url 'https://developer.huawei.com/repo/' }
+ }
+}
+dependencyResolutionManagement {
+ repositories {
+ google()
+ mavenCentral()
+ // Configure the Maven repository address for the HMS Core SDK.
+ maven {url 'https://developer.huawei.com/repo/'}
+ }
+}
+include ':infobip-mobile-messaging-huawei-sdk', ':infobip-mobile-messaging-huawei-demo', ':infobip-mobile-messaging-huawei-test', ':infobip-mobile-messaging-huawei-chat-sdk', ':infobip-mobile-messaging-huawei-cryptor-migration', 'infobip-mobile-messaging-huawei-inbox-sdk'