diff --git a/packages/app/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.java b/packages/app/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.java index ebbcc8eac8..bce9bb5dc1 100644 --- a/packages/app/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.java +++ b/packages/app/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.java @@ -70,6 +70,12 @@ public String getStringValue(String key, String defaultValue) { return metaData.getString(META_PREFIX + key, defaultValue); } + public int getIntValue(String key, int defaultValue) { + Bundle metaData = getMetaData(); + if (metaData == null) return defaultValue; + return metaData.getInt(META_PREFIX + key, defaultValue); + } + public WritableMap getAll() { Bundle metaData = getMetaData(); WritableMap map = Arguments.createMap(); @@ -84,6 +90,8 @@ public WritableMap getAll() { map.putString(key, (String) value); } else if (value instanceof Boolean) { map.putBoolean(key, (Boolean) value); + } else if (value instanceof Integer) { + map.putInt(key, (Integer) value); } } } diff --git a/packages/app/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.java b/packages/app/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.java index c74f935ca8..ff396f7f55 100644 --- a/packages/app/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.java +++ b/packages/app/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.java @@ -46,6 +46,14 @@ public boolean getBooleanValue(String key, boolean defaultValue) { return getPreferences().getBoolean(key, defaultValue); } + public void setIntValue(String key, int value) { + getPreferences().edit().putInt(key,value).apply(); + } + + public int getIntValue(String key, int defaultValue) { + return getPreferences().getInt(key,defaultValue); + } + public void setLongValue(String key, long value) { getPreferences().edit().putLong(key, value).apply(); } diff --git a/packages/app/firebase-schema.json b/packages/app/firebase-schema.json index 3733edd956..94fb479980 100644 --- a/packages/app/firebase-schema.json +++ b/packages/app/firebase-schema.json @@ -119,6 +119,10 @@ "description": "On iOS, indicating how to present a notification in a foreground app.", "type": "array" }, + "messaging_max_stored_notifications": { + "description": "Controls how many notifications are retained on-device for background storage. Configuration priority order is SharedPreferences -> firebase.json -> AndroidManifest. Default 100. Very small values may cause unwanted excessive deletions, large values or large message contents run the risk of OutOfMemoryErrors.", + "type": "number" + }, "perf_auto_collection_enabled": { "description": "Disable or enable auto collection of performance monitoring data collection.\n This is useful for opt-in-first data flows, for example when dealing with GDPR compliance.\nThis can be overridden in JavaScript.", "type": "boolean" diff --git a/packages/messaging/android/src/main/java/io/invertase/firebase/messaging/ReactNativeFirebaseMessagingStoreImpl.java b/packages/messaging/android/src/main/java/io/invertase/firebase/messaging/ReactNativeFirebaseMessagingStoreImpl.java index 03cb8f2eaf..92c39719c4 100644 --- a/packages/messaging/android/src/main/java/io/invertase/firebase/messaging/ReactNativeFirebaseMessagingStoreImpl.java +++ b/packages/messaging/android/src/main/java/io/invertase/firebase/messaging/ReactNativeFirebaseMessagingStoreImpl.java @@ -8,31 +8,70 @@ import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableMap; import com.google.firebase.messaging.RemoteMessage; -import io.invertase.firebase.common.UniversalFirebasePreferences; + +import org.json.JSONException; +import org.json.JSONObject; + +import android.util.Log; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.json.JSONException; -import org.json.JSONObject; + +import io.invertase.firebase.common.ReactNativeFirebaseJSON; +import io.invertase.firebase.common.ReactNativeFirebaseMeta; +import io.invertase.firebase.common.ReactNativeFirebasePreferences; +import io.invertase.firebase.common.UniversalFirebasePreferences; public class ReactNativeFirebaseMessagingStoreImpl implements ReactNativeFirebaseMessagingStore { + private static final String TAG = "RNFirebaseMsgStore"; + private static final String KEY_MAX_STORED_NOTIFICATIONS = "messaging_max_stored_notifications"; private static final String S_KEY_ALL_NOTIFICATION_IDS = "all_notification_ids"; - private static final int MAX_SIZE_NOTIFICATIONS = 100; private final String DELIMITER = ","; + private static final int DEFAULT_MAX_SIZE_NOTIFICATIONS = 100; + private static final int maxNotificationSize = resolveMaxNotificationSize(); + + private static int resolveMaxNotificationSize() { + int maxSize = DEFAULT_MAX_SIZE_NOTIFICATIONS; + String source = "default"; + ReactNativeFirebaseJSON json = ReactNativeFirebaseJSON.getSharedInstance(); + ReactNativeFirebaseMeta meta = ReactNativeFirebaseMeta.getSharedInstance(); + ReactNativeFirebasePreferences prefs = ReactNativeFirebasePreferences.getSharedInstance(); + + try { + // Priority: SharedPreferences -> firebase.json -> AndroidManifest + if (prefs.contains(KEY_MAX_STORED_NOTIFICATIONS)) { + maxSize = prefs.getIntValue(KEY_MAX_STORED_NOTIFICATIONS, DEFAULT_MAX_SIZE_NOTIFICATIONS); + source = "SharedPreferences"; + } else if (json.contains(KEY_MAX_STORED_NOTIFICATIONS)) { + maxSize = json.getIntValue(KEY_MAX_STORED_NOTIFICATIONS, DEFAULT_MAX_SIZE_NOTIFICATIONS); + source = "firebase.json"; + } else if (meta.contains(KEY_MAX_STORED_NOTIFICATIONS)) { + maxSize = meta.getIntValue(KEY_MAX_STORED_NOTIFICATIONS, DEFAULT_MAX_SIZE_NOTIFICATIONS); + source = "AndroidManifest"; + } + + Log.d(TAG, "messaging_max_stored_notifications: " + maxSize + " (from " + source + ")"); + return maxSize; + } catch (Exception e) { + Log.w(TAG, "Error resolving max notification size, using default: " + DEFAULT_MAX_SIZE_NOTIFICATIONS, e); + return DEFAULT_MAX_SIZE_NOTIFICATIONS; + } + } @Override public void storeFirebaseMessage(RemoteMessage remoteMessage) { try { String remoteMessageString = - reactToJSON(remoteMessageToWritableMap(remoteMessage)).toString(); + reactToJSON(remoteMessageToWritableMap(remoteMessage)).toString(); // Log.d("storeFirebaseMessage", remoteMessageString); UniversalFirebasePreferences preferences = UniversalFirebasePreferences.getSharedInstance(); // remove old notifications message before store to free space as needed String notificationIds = preferences.getStringValue(S_KEY_ALL_NOTIFICATION_IDS, ""); List allNotificationList = convertToArray(notificationIds); - while (allNotificationList.size() > MAX_SIZE_NOTIFICATIONS - 1) { + while (allNotificationList.size() > maxNotificationSize - 1) { clearFirebaseMessage(allNotificationList.get(0)); allNotificationList.remove(0); } @@ -61,7 +100,7 @@ public RemoteMessage getFirebaseMessage(String remoteMessageId) { @Override public WritableMap getFirebaseMessageMap(String remoteMessageId) { String remoteMessageString = - UniversalFirebasePreferences.getSharedInstance().getStringValue(remoteMessageId, null); + UniversalFirebasePreferences.getSharedInstance().getStringValue(remoteMessageId, null); if (remoteMessageString != null) { // Log.d("getFirebaseMessage", remoteMessageString); try { diff --git a/tests/firebase.json b/tests/firebase.json index c61567ca73..e5ecd1c0fd 100644 --- a/tests/firebase.json +++ b/tests/firebase.json @@ -21,6 +21,7 @@ "messaging_android_notification_color": "@color/hotpink", "messaging_ios_auto_register_for_remote_messages": false, "messaging_android_notification_delegation_enabled": true, + "messaging_max_stored_notifications": 100, "analytics_auto_collection_enabled": true, "analytics_collection_deactivated": false,