diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java index 2345c15f29..a7b4f41ba0 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java @@ -27,10 +27,12 @@ package com.onesignal; +import static com.onesignal.GenerateNotification.BUNDLE_KEY_ACTION_ID; +import static com.onesignal.OSUtils.isStringNotEmpty; + import android.content.ContentValues; import android.content.Context; import android.database.Cursor; -import android.os.Build; import android.os.Bundle; import androidx.annotation.NonNull; @@ -47,9 +49,6 @@ import java.util.Set; -import static com.onesignal.GenerateNotification.BUNDLE_KEY_ACTION_ID; -import static com.onesignal.OSUtils.isStringNotEmpty; - /** Processes the Bundle received from a push. * This class handles both processing bundles from a BroadcastReceiver or from a Service * - Entry points are processBundleFromReceiver or ProcessFromFCMIntentService respectively @@ -249,8 +248,8 @@ private static void saveNotification(OSNotificationGenerationJob notificationJob values.put(NotificationTable.COLUMN_NAME_MESSAGE, notificationJob.getBody().toString()); // Set expire_time - long sentTime = jsonPayload.optLong("google.sent_time", OneSignal.getTime().getCurrentThreadTimeMillis()) / 1_000L; - int ttl = jsonPayload.optInt("google.ttl", OSNotificationRestoreWorkManager.DEFAULT_TTL_IF_NOT_IN_PAYLOAD); + long sentTime = jsonPayload.optLong(OSNotificationController.GOOGLE_SENT_TIME_KEY, OneSignal.getTime().getCurrentThreadTimeMillis()) / 1_000L; + int ttl = jsonPayload.optInt(OSNotificationController.GOOGLE_TTL_KEY, OSNotificationRestoreWorkManager.DEFAULT_TTL_IF_NOT_IN_PAYLOAD); long expireTime = sentTime + ttl; values.put(NotificationTable.COLUMN_NAME_EXPIRE_TIME, expireTime); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedProcessor.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedProcessor.java index f0d2bedf8f..afdafd571b 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedProcessor.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedProcessor.java @@ -206,7 +206,6 @@ private static void markNotificationsConsumed(Context context, Intent intent, On } else whereStr = NotificationTable.COLUMN_NAME_ANDROID_NOTIFICATION_ID + " = " + intent.getIntExtra(BUNDLE_KEY_ANDROID_NOTIFICATION_ID, 0); - clearStatusBarNotifications(context, writableDb, summaryGroup); writableDb.update(NotificationTable.TABLE_NAME, newContentValuesWithConsumed(intent), whereStr, whereArgs); BadgeCountUpdater.update(writableDb, context); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotification.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotification.java index a5d0208530..e607d333c3 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotification.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotification.java @@ -40,6 +40,10 @@ import static com.onesignal.GenerateNotification.BUNDLE_KEY_ACTION_ID; import static com.onesignal.NotificationBundleProcessor.PUSH_ADDITIONAL_DATA_KEY; +import static com.onesignal.OSNotificationController.GOOGLE_SENT_TIME_KEY; +import static com.onesignal.OSNotificationController.GOOGLE_TTL_KEY; +import static com.onesignal.OneSignalHmsEventBridge.HMS_SENT_TIME_KEY; +import static com.onesignal.OneSignalHmsEventBridge.HMS_TTL_KEY; /** * The notification the user received @@ -87,6 +91,9 @@ public class OSNotification { private int priority; private String rawPayload; + private long sentTime; + private int ttl; + protected OSNotification() { } @@ -94,14 +101,6 @@ protected OSNotification() { this(null, payload, 0); } - OSNotification(@NonNull JSONObject payload, int androidNotificationId) { - this(null, payload, androidNotificationId); - } - - OSNotification(@Nullable List groupedNotifications, @NonNull JSONObject payload) { - this(groupedNotifications, payload, 0); - } - OSNotification(@Nullable List groupedNotifications, @NonNull JSONObject jsonPayload, int androidNotificationId) { initPayloadData(jsonPayload); this.groupedNotifications = groupedNotifications; @@ -144,6 +143,18 @@ private void initPayloadData(JSONObject currentJsonPayload) { return; } + long currentTime = OneSignal.getTime().getCurrentThreadTimeMillis(); + if (currentJsonPayload.has(GOOGLE_TTL_KEY)) { + sentTime = currentJsonPayload.optLong(GOOGLE_SENT_TIME_KEY, currentTime) / 1_000; + ttl = currentJsonPayload.optInt(GOOGLE_TTL_KEY, OSNotificationRestoreWorkManager.DEFAULT_TTL_IF_NOT_IN_PAYLOAD); + } else if (currentJsonPayload.has(HMS_TTL_KEY)) { + sentTime = currentJsonPayload.optLong(HMS_SENT_TIME_KEY, currentTime) / 1_000; + ttl = currentJsonPayload.optInt(HMS_TTL_KEY, OSNotificationRestoreWorkManager.DEFAULT_TTL_IF_NOT_IN_PAYLOAD); + } else { + sentTime = currentTime / 1_000; + ttl = OSNotificationRestoreWorkManager.DEFAULT_TTL_IF_NOT_IN_PAYLOAD; + } + notificationId = customJson.optString("i"); templateId = customJson.optString("ti"); templateName = customJson.optString("tn"); @@ -243,6 +254,8 @@ OSNotification copy() { .setCollapseId(collapseId) .setPriority(priority) .setRawPayload(rawPayload) + .setSenttime(sentTime) + .setTTL(ttl) .build(); } @@ -451,6 +464,22 @@ void setRawPayload(String rawPayload) { this.rawPayload = rawPayload; } + public long getSentTime() { + return sentTime; + } + + private void setSentTime(long sentTime) { + this.sentTime = sentTime; + } + + public int getTtl() { + return ttl; + } + + private void setTtl(int ttl) { + this.ttl = ttl; + } + public JSONObject toJSONObject() { JSONObject mainObj = new JSONObject(); @@ -631,6 +660,9 @@ public static class OSNotificationBuilder { private int priority; private String rawPayload; + private long sentTime; + private int ttl; + public OSNotificationBuilder() { } @@ -759,6 +791,16 @@ public OSNotificationBuilder setRawPayload(String rawPayload) { return this; } + public OSNotificationBuilder setSenttime(long sentTime) { + this.sentTime = sentTime; + return this; + } + + public OSNotificationBuilder setTTL(int ttl) { + this.ttl = ttl; + return this; + } + public OSNotification build() { OSNotification payload = new OSNotification(); payload.setNotificationExtender(notificationExtender); @@ -786,6 +828,8 @@ public OSNotification build() { payload.setCollapseId(collapseId); payload.setPriority(priority); payload.setRawPayload(rawPayload); + payload.setSentTime(sentTime); + payload.setTtl(ttl); return payload; } } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationController.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationController.java index 3c12b8f441..e08306287a 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationController.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationController.java @@ -27,6 +27,10 @@ package com.onesignal; +import static com.onesignal.OSUtils.isStringNotEmpty; +import static com.onesignal.OneSignalHmsEventBridge.HMS_SENT_TIME_KEY; +import static com.onesignal.OneSignalHmsEventBridge.HMS_TTL_KEY; + import android.content.Context; import androidx.annotation.Nullable; @@ -35,12 +39,12 @@ import org.json.JSONObject; -import static com.onesignal.OSUtils.isStringNotEmpty; - public class OSNotificationController { // The extension service app AndroidManifest.xml meta data tag key name private static final String EXTENSION_SERVICE_META_DATA_TAG_NAME = "com.onesignal.NotificationServiceExtension"; + static final String GOOGLE_SENT_TIME_KEY = "google.sent_time"; + static final String GOOGLE_TTL_KEY = "google.ttl"; private final CallbackToFutureAdapter.Completer callbackCompleter; private final OSNotificationGenerationJob notificationJob; @@ -55,12 +59,12 @@ public class OSNotificationController { } OSNotificationController(CallbackToFutureAdapter.Completer callbackCompleter, - Context context, JSONObject jsonPayload, boolean restoring, boolean fromBackgroundLogic, Long timestamp) { + Context context, OSNotification notification, JSONObject jsonPayload, boolean restoring, boolean fromBackgroundLogic, Long timestamp) { this.callbackCompleter = callbackCompleter; this.restoring = restoring; this.fromBackgroundLogic = fromBackgroundLogic; - notificationJob = createNotificationJobFromCurrent(context, jsonPayload, timestamp); + notificationJob = createNotificationJobFromCurrent(context, notification, jsonPayload, timestamp); } /** @@ -69,11 +73,12 @@ public class OSNotificationController { *

* @see OSNotificationGenerationJob */ - private OSNotificationGenerationJob createNotificationJobFromCurrent(Context context, JSONObject jsonPayload, Long timestamp) { + private OSNotificationGenerationJob createNotificationJobFromCurrent(Context context, OSNotification notification, JSONObject jsonPayload, Long timestamp) { OSNotificationGenerationJob notificationJob = new OSNotificationGenerationJob(callbackCompleter, context); notificationJob.setJsonPayload(jsonPayload); notificationJob.setShownTimeStamp(timestamp); notificationJob.setRestoring(restoring); + notificationJob.setNotification(notification); return notificationJob; } @@ -88,13 +93,14 @@ private OSNotificationGenerationJob createNotificationJobFromCurrent(Context con void processNotification(OSNotification originalNotification, @Nullable OSNotification notification) { if (notification != null) { boolean display = isStringNotEmpty(notification.getBody()); - if (!display) { - // Save as processed to prevent possible duplicate calls from canonical ids - notDisplayNotificationLogic(originalNotification); - } else { + boolean withinTtl = isNotificationWithinTTL(); + if (display && withinTtl) { // Set modified notification notificationJob.setNotification(notification); NotificationBundleProcessor.processJobForDisplay(this, fromBackgroundLogic); + } else { + // Save as processed to prevent possible duplicate calls from canonical ids + notDisplayNotificationLogic(originalNotification); } // Delay to prevent CPU spikes // Normally more than one notification is restored at a time @@ -120,6 +126,19 @@ private void notDisplayNotificationLogic(OSNotification originalNotification) { } } + public boolean isNotificationWithinTTL() { + boolean useTtl = OneSignal.getRemoteParamController().isRestoreTTLFilterActive(); + if (!useTtl) + return true; + + long currentTimeInSeconds = OneSignal.getTime().getCurrentThreadTimeMillis() / 1_000; + long sentTime = notificationJob.getNotification().getSentTime(); + // If available TTL times comes in seconds, by default is 3 days in seconds + int ttl = notificationJob.getNotification().getTtl(); + + return sentTime + ttl > currentTimeInSeconds; + } + public OSNotificationGenerationJob getNotificationJob() { return notificationJob; } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java index 1016c8eccb..07d7ceb578 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java @@ -140,7 +140,7 @@ static void processNotificationData(CallbackToFutureAdapter.Completer " + currentTimeSec; where.append(expireTimeWhere); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalHmsEventBridge.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalHmsEventBridge.java index b8f629d65a..5639912664 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalHmsEventBridge.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignalHmsEventBridge.java @@ -8,6 +8,9 @@ import com.huawei.hms.push.RemoteMessage; +import org.json.JSONException; +import org.json.JSONObject; + import java.util.concurrent.atomic.AtomicBoolean; /** @@ -20,6 +23,9 @@ */ public class OneSignalHmsEventBridge { + public static final String HMS_TTL_KEY = "hms.ttl"; + public static final String HMS_SENT_TIME_KEY = "hms.sent_time"; + private static final AtomicBoolean firstToken = new AtomicBoolean(true); /** @@ -44,6 +50,15 @@ public static void onNewToken(@NonNull Context context, @NonNull String token) { } public static void onMessageReceived(@NonNull Context context, @NonNull RemoteMessage message) { - NotificationPayloadProcessorHMS.processDataMessageReceived(context, message.getData()); + String data = message.getData(); + try { + JSONObject messageDataJSON = new JSONObject(message.getData()); + messageDataJSON.put(HMS_TTL_KEY, message.getTtl()); + messageDataJSON.put(HMS_SENT_TIME_KEY, message.getSentTime()); + data = messageDataJSON.toString(); + } catch (JSONException e) { + OneSignal.Log(OneSignal.LOG_LEVEL.ERROR, "OneSignalHmsEventBridge error when trying to create RemoteMessage data JSON"); + } + NotificationPayloadProcessorHMS.processDataMessageReceived(context, data); } } diff --git a/OneSignalSDK/unittest/src/test/java/com/onesignal/MockOSTimeImpl.java b/OneSignalSDK/unittest/src/test/java/com/onesignal/MockOSTimeImpl.java index 28bf467725..adaf8ad051 100644 --- a/OneSignalSDK/unittest/src/test/java/com/onesignal/MockOSTimeImpl.java +++ b/OneSignalSDK/unittest/src/test/java/com/onesignal/MockOSTimeImpl.java @@ -44,6 +44,11 @@ public void advanceSystemTimeBy(long sec) { setMockedTime(getCurrentTimeMillis() + ms); } + public void advanceThreadTimeBy(long sec) { + long ms = sec * 1_000L; + setMockedCurrentThreadTimeMillis(getCurrentThreadTimeMillis() + ms); + } + public void advanceSystemAndElapsedTimeBy(long sec) { long ms = sec * 1_000L; setMockedElapsedTime(getCurrentTimeMillis() + ms); diff --git a/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java b/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java index 1e57443d1e..50dcd2e862 100644 --- a/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java +++ b/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java @@ -8,7 +8,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.concurrent.futures.CallbackToFutureAdapter; +import androidx.work.ListenableWorker; +import com.huawei.hms.push.RemoteMessage; import com.onesignal.influence.data.OSTrackerFactory; import org.json.JSONArray; @@ -28,6 +31,10 @@ import static org.robolectric.Shadows.shadowOf; public class OneSignalPackagePrivateHelper { + + public static final String GOOGLE_SENT_TIME_KEY = OSNotificationController.GOOGLE_SENT_TIME_KEY; + public static final String GOOGLE_TTL_KEY = OSNotificationController.GOOGLE_TTL_KEY; + public static final String IN_APP_MESSAGES_JSON_KEY = com.onesignal.OSInAppMessageController.IN_APP_MESSAGES_JSON_KEY; public static final long MIN_ON_SESSION_TIME_MILLIS = com.onesignal.OneSignal.MIN_ON_SESSION_TIME_MILLIS; @@ -179,6 +186,10 @@ public static void FCMBroadcastReceiver_onReceived_withBundle(Context context, B threadAndTaskWait(); } + public static void HMSEventBridge_onMessageReceive(final Context context, final RemoteMessage message) { + OneSignalHmsEventBridge.onMessageReceived(context, message); + } + public static void HMSProcessor_processDataMessageReceived(final Context context, final String jsonStrPayload) { NotificationPayloadProcessorHMS.processDataMessageReceived(context, jsonStrPayload); } diff --git a/OneSignalSDK/unittest/src/test/java/com/onesignal/ShadowBadgeCountUpdater.java b/OneSignalSDK/unittest/src/test/java/com/onesignal/ShadowBadgeCountUpdater.java index a9be945cb9..f721ecbdc1 100644 --- a/OneSignalSDK/unittest/src/test/java/com/onesignal/ShadowBadgeCountUpdater.java +++ b/OneSignalSDK/unittest/src/test/java/com/onesignal/ShadowBadgeCountUpdater.java @@ -41,4 +41,8 @@ public class ShadowBadgeCountUpdater { public static void updateCount(int count, Context context) { lastCount = count; } + + public static void resetStatics() { + lastCount = 0; + } } diff --git a/OneSignalSDK/unittest/src/test/java/com/onesignal/ShadowHmsRemoteMessage.java b/OneSignalSDK/unittest/src/test/java/com/onesignal/ShadowHmsRemoteMessage.java new file mode 100644 index 0000000000..1e213b39a3 --- /dev/null +++ b/OneSignalSDK/unittest/src/test/java/com/onesignal/ShadowHmsRemoteMessage.java @@ -0,0 +1,32 @@ +package com.onesignal; + +import androidx.annotation.Nullable; + +import com.huawei.hms.push.RemoteMessage; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@Implements(RemoteMessage.class) +public class ShadowHmsRemoteMessage { + + @Nullable + public static String data; + public static int ttl; + public static long sentTime; + + @Implementation + public String getData() { + return data; + } + + @Implementation + public int getTtl() { + return ttl; + } + + public long getSentTime() { + return sentTime; + } + +} diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java index ad2212e423..06fc897c93 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java @@ -27,6 +27,40 @@ package com.test.onesignal; +import static com.onesignal.OneSignalPackagePrivateHelper.FCMBroadcastReceiver_onReceived_withIntent; +import static com.onesignal.OneSignalPackagePrivateHelper.FCMBroadcastReceiver_processBundle; +import static com.onesignal.OneSignalPackagePrivateHelper.GenerateNotification.BUNDLE_KEY_ACTION_ID; +import static com.onesignal.OneSignalPackagePrivateHelper.GenerateNotification.BUNDLE_KEY_ANDROID_NOTIFICATION_ID; +import static com.onesignal.OneSignalPackagePrivateHelper.GenerateNotification.BUNDLE_KEY_ONESIGNAL_DATA; +import static com.onesignal.OneSignalPackagePrivateHelper.NotificationBundleProcessor.PUSH_MINIFIED_BUTTONS_LIST; +import static com.onesignal.OneSignalPackagePrivateHelper.NotificationBundleProcessor.PUSH_MINIFIED_BUTTON_ID; +import static com.onesignal.OneSignalPackagePrivateHelper.NotificationBundleProcessor.PUSH_MINIFIED_BUTTON_TEXT; +import static com.onesignal.OneSignalPackagePrivateHelper.NotificationBundleProcessor_ProcessFromFCMIntentService; +import static com.onesignal.OneSignalPackagePrivateHelper.NotificationBundleProcessor_ProcessFromFCMIntentService_NoWrap; +import static com.onesignal.OneSignalPackagePrivateHelper.NotificationOpenedProcessor_processFromContext; +import static com.onesignal.OneSignalPackagePrivateHelper.NotificationSummaryManager_updateSummaryNotificationAfterChildRemoved; +import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_getAccentColor; +import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTime; +import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setupNotificationServiceExtension; +import static com.onesignal.OneSignalPackagePrivateHelper.createInternalPayloadBundle; +import static com.onesignal.ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse; +import static com.onesignal.ShadowRoboNotificationManager.getNotificationsInGroup; +import static com.test.onesignal.RestClientAsserts.assertReportReceivedAtIndex; +import static com.test.onesignal.RestClientAsserts.assertRestCalls; +import static com.test.onesignal.TestHelpers.fastColdRestartApp; +import static com.test.onesignal.TestHelpers.pauseActivity; +import static com.test.onesignal.TestHelpers.startRemoteNotificationReceivedHandlerService; +import static com.test.onesignal.TestHelpers.threadAndTaskWait; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertThat; +import static org.robolectric.Shadows.shadowOf; + import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; @@ -75,7 +109,6 @@ import com.onesignal.ShadowOSWebView; import com.onesignal.ShadowOneSignal; import com.onesignal.ShadowOneSignalRestClient; -import com.onesignal.ShadowReceiveReceiptController; import com.onesignal.ShadowResources; import com.onesignal.ShadowRoboNotificationManager; import com.onesignal.ShadowRoboNotificationManager.PostedNotification; @@ -109,41 +142,6 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import static com.onesignal.OneSignalPackagePrivateHelper.FCMBroadcastReceiver_onReceived_withIntent; -import static com.onesignal.OneSignalPackagePrivateHelper.FCMBroadcastReceiver_processBundle; -import static com.onesignal.OneSignalPackagePrivateHelper.GenerateNotification.BUNDLE_KEY_ACTION_ID; -import static com.onesignal.OneSignalPackagePrivateHelper.GenerateNotification.BUNDLE_KEY_ANDROID_NOTIFICATION_ID; -import static com.onesignal.OneSignalPackagePrivateHelper.GenerateNotification.BUNDLE_KEY_ONESIGNAL_DATA; -import static com.onesignal.OneSignalPackagePrivateHelper.NotificationBundleProcessor.PUSH_MINIFIED_BUTTONS_LIST; -import static com.onesignal.OneSignalPackagePrivateHelper.NotificationBundleProcessor.PUSH_MINIFIED_BUTTON_ID; -import static com.onesignal.OneSignalPackagePrivateHelper.NotificationBundleProcessor.PUSH_MINIFIED_BUTTON_TEXT; -import static com.onesignal.OneSignalPackagePrivateHelper.NotificationBundleProcessor_ProcessFromFCMIntentService; -import static com.onesignal.OneSignalPackagePrivateHelper.NotificationBundleProcessor_ProcessFromFCMIntentService_NoWrap; -import static com.onesignal.OneSignalPackagePrivateHelper.NotificationOpenedProcessor_processFromContext; -import static com.onesignal.OneSignalPackagePrivateHelper.NotificationSummaryManager_updateSummaryNotificationAfterChildRemoved; -import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_getAccentColor; -import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setDelayTaskController; -import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTime; -import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setupNotificationServiceExtension; -import static com.onesignal.OneSignalPackagePrivateHelper.createInternalPayloadBundle; -import static com.onesignal.ShadowOneSignalRestClient.setRemoteParamsGetHtmlResponse; -import static com.onesignal.ShadowRoboNotificationManager.getNotificationsInGroup; -import static com.test.onesignal.RestClientAsserts.assertReportReceivedAtIndex; -import static com.test.onesignal.RestClientAsserts.assertRestCalls; -import static com.test.onesignal.TestHelpers.fastColdRestartApp; -import static com.test.onesignal.TestHelpers.pauseActivity; -import static com.test.onesignal.TestHelpers.startRemoteNotificationReceivedHandlerService; -import static com.test.onesignal.TestHelpers.threadAndTaskWait; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; -import static junit.framework.Assert.assertTrue; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertThat; -import static org.robolectric.Shadows.shadowOf; - @Config(packageName = "com.onesignal.example", shadows = { ShadowRoboNotificationManager.class, @@ -763,7 +761,8 @@ public void shouldSetBadgesWhenRestoringNotifications() throws Exception { assertEquals(1, ShadowBadgeCountUpdater.lastCount); } - @Test public void shouldNotRestoreNotificationsIfPermissionIsDisabled() throws Exception { + @Test + public void shouldNotRestoreNotificationsIfPermissionIsDisabled() throws Exception { NotificationBundleProcessor_ProcessFromFCMIntentService(blankActivity, getBaseNotifBundle()); ShadowNotificationManagerCompat.enabled = false; @@ -946,10 +945,12 @@ private void restoreNotifications() { } private void helperShouldRestoreNotificationsPastExpireTime(boolean should) throws Exception { + long sentTime = System.currentTimeMillis(); long ttl = 60L; + Bundle bundle = getBaseNotifBundle(); - bundle.putLong("google.sent_time", System.currentTimeMillis()); - bundle.putLong("google.ttl", ttl); + bundle.putLong(OneSignalPackagePrivateHelper.GOOGLE_SENT_TIME_KEY, sentTime); + bundle.putLong(OneSignalPackagePrivateHelper.GOOGLE_TTL_KEY, ttl); NotificationBundleProcessor_ProcessFromFCMIntentService(blankActivity, bundle); restoreNotifications(); @@ -960,6 +961,7 @@ private void helperShouldRestoreNotificationsPastExpireTime(boolean should) thro // Go forward just past the TTL of the notification time.advanceSystemTimeBy(ttl + 1); restoreNotifications(); + if (should) assertEquals(1, ShadowBadgeCountUpdater.lastCount); else @@ -1234,8 +1236,8 @@ public void shouldSetExpireTimeCorrectlyFromGoogleTTL() throws Exception { long ttl = 60L; Bundle bundle = getBaseNotifBundle(); - bundle.putLong("google.sent_time", sentTime); - bundle.putLong("google.ttl", ttl); + bundle.putLong(OneSignalPackagePrivateHelper.GOOGLE_SENT_TIME_KEY, sentTime); + bundle.putLong(OneSignalPackagePrivateHelper.GOOGLE_TTL_KEY, ttl); NotificationBundleProcessor_ProcessFromFCMIntentService(blankActivity, bundle); threadAndTaskWait(); @@ -1244,6 +1246,25 @@ public void shouldSetExpireTimeCorrectlyFromGoogleTTL() throws Exception { assertEquals(sentTime + (ttl * 1_000), expireTime * 1_000); } + @Test + @Config (sdk = 23, shadows = { ShadowGenerateNotification.class }) + public void notShowNotificationPastTTL() throws Exception { + long sentTime = time.getCurrentThreadTimeMillis(); + long ttl = 60L; + + Bundle bundle = getBaseNotifBundle(); + bundle.putLong(OneSignalPackagePrivateHelper.GOOGLE_SENT_TIME_KEY, sentTime); + bundle.putLong(OneSignalPackagePrivateHelper.GOOGLE_TTL_KEY, ttl); + + // Go forward just past the TTL of the notification + time.advanceThreadTimeBy(ttl + 1); + + NotificationBundleProcessor_ProcessFromFCMIntentService(blankActivity, bundle); + threadAndTaskWait(); + + assertEquals(0, ShadowBadgeCountUpdater.lastCount); + } + @Test @Config(shadows = { ShadowGenerateNotification.class }) public void shouldSetExpireTimeCorrectlyWhenMissingFromPayload() throws Exception { diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/HMSDataMessageReceivedIntegrationTestsRunner.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/HMSDataMessageReceivedIntegrationTestsRunner.java index c479fd99a3..d8c65c7214 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/HMSDataMessageReceivedIntegrationTestsRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/HMSDataMessageReceivedIntegrationTestsRunner.java @@ -2,9 +2,14 @@ import android.annotation.SuppressLint; import android.app.Activity; +import android.os.Bundle; +import com.huawei.hms.push.RemoteMessage; +import com.onesignal.MockOSTimeImpl; import com.onesignal.OneSignalPackagePrivateHelper.NotificationPayloadProcessorHMS; +import com.onesignal.ShadowBadgeCountUpdater; import com.onesignal.ShadowGenerateNotification; +import com.onesignal.ShadowHmsRemoteMessage; import com.onesignal.ShadowNotificationManagerCompat; import com.onesignal.ShadowOSUtils; import com.onesignal.ShadowRoboNotificationManager; @@ -28,9 +33,11 @@ import java.util.UUID; +import static com.onesignal.OneSignalPackagePrivateHelper.HMSEventBridge_onMessageReceive; import static com.onesignal.OneSignalPackagePrivateHelper.HMSProcessor_processDataMessageReceived; import static com.onesignal.OneSignalPackagePrivateHelper.OSNotificationFormatHelper.PAYLOAD_OS_NOTIFICATION_ID; import static com.onesignal.OneSignalPackagePrivateHelper.OSNotificationFormatHelper.PAYLOAD_OS_ROOT_CUSTOM; +import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTime; import static com.test.onesignal.TestHelpers.threadAndTaskWait; import static junit.framework.Assert.assertEquals; @@ -50,6 +57,8 @@ public class HMSDataMessageReceivedIntegrationTestsRunner { private static final String ALERT_TEST_MESSAGE_BODY = "Test Message body"; + private MockOSTimeImpl time; + @BeforeClass // Runs only once, before any tests public static void setUpClass() throws Exception { ShadowLog.stream = System.out; @@ -63,6 +72,9 @@ public void beforeEachTest() throws Exception { ShadowOSUtils.supportsHMS(true); + time = new MockOSTimeImpl(); + OneSignal_setTime(time); + blankActivityController = Robolectric.buildActivity(BlankActivity.class).create(); blankActivity = blankActivityController.get(); } @@ -106,6 +118,26 @@ public void basicPayload_shouldDisplayNotification() throws Exception { assertEquals(ALERT_TEST_MESSAGE_BODY, ShadowRoboNotificationManager.getLastShadowNotif().getBigText()); } + @Test + @Config(shadows = { ShadowGenerateNotification.class, ShadowHmsRemoteMessage.class, ShadowBadgeCountUpdater.class }) + public void ttl_shouldNotDisplayNotification() throws Exception { + blankActivityController.pause(); + + long sentTime = 1_635_971_895_940L; + int ttl = 60; + + time.setMockedCurrentThreadTimeMillis(sentTime * 1_000); + + ShadowHmsRemoteMessage.data = helperBasicOSPayload(); + ShadowHmsRemoteMessage.ttl = ttl; + ShadowHmsRemoteMessage.sentTime = sentTime; + + HMSEventBridge_onMessageReceive(blankActivity, new RemoteMessage(new Bundle())); + threadAndTaskWait(); + + assertEquals(0, ShadowBadgeCountUpdater.lastCount); + } + // NOTE: More tests can be added but they would be duplicated with GenerateNotificationRunner // In 4.0.0 or later these should be written in a reusable way between HMS, FCM, and ADM } diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/TestHelpers.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/TestHelpers.java index 64b24813aa..3c580b94ec 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/TestHelpers.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/TestHelpers.java @@ -25,6 +25,7 @@ import com.onesignal.OneSignalPackagePrivateHelper.TestOneSignalPrefs; import com.onesignal.OneSignalShadowPackageManager; import com.onesignal.OSOutcomeEvent; +import com.onesignal.ShadowBadgeCountUpdater; import com.onesignal.ShadowCustomTabsClient; import com.onesignal.ShadowDynamicTimer; import com.onesignal.ShadowFCMBroadcastReceiver; @@ -128,6 +129,7 @@ static void beforeTestInitAndCleanup() throws Exception { ShadowGenerateNotification.resetStatics(); ShadowNotificationReceivedEvent.resetStatics(); ShadowOneSignalNotificationManager.resetStatics(); + ShadowBadgeCountUpdater.resetStatics(); lastException = null; }