From 35e1ecb9c594c7889c49359a6a39862642001deb Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 7 Jun 2021 18:28:21 -0300 Subject: [PATCH 1/6] Add Receive Receipt delay. * Add random delay from 0 to 25 seconds * Add OSDelayTaskController for future delays * Add MockDelayTaskController for test approaches * Refactor Notification Work Manager to extends ListenableWorker. This adds future completion. Complete future after sending receive receipts * Refactor Restoration Work Manager to not init another work manager, since Notification Work Manager nows expects a future when Restoration work is done, it will finish notification work manager that will end on a FutureGarbageCollectedException, avoid this exception by running everything on the same work thread --- OneSignalSDK/build.gradle | 1 + OneSignalSDK/onesignal/build.gradle | 2 + .../NotificationBundleProcessor.java | 21 +++-- .../onesignal/NotificationSummaryManager.java | 2 +- .../com/onesignal/OSDelayTaskController.kt | 40 +++++++++ .../onesignal/OSNotificationController.java | 10 ++- .../OSNotificationGenerationJob.java | 16 +++- .../OSNotificationRestoreWorkManager.java | 14 ++-- .../onesignal/OSNotificationWorkManager.java | 83 ++++++++++++++----- .../onesignal/OSReceiveReceiptController.java | 55 +++++++----- .../onesignal/OSRemoteParamController.java | 23 +++-- .../main/java/com/onesignal/OneSignal.java | 10 +++ OneSignalSDK/unittest/build.gradle | 2 +- .../onesignal/MockDelayTaskController.java | 32 +++++++ .../OneSignalPackagePrivateHelper.java | 5 ++ .../java/com/onesignal/StaticResetHelper.java | 6 +- .../onesignal/GenerateNotificationRunner.java | 6 +- .../onesignal/MainOneSignalClassRunner.java | 37 +++++++++ 18 files changed, 296 insertions(+), 69 deletions(-) create mode 100644 OneSignalSDK/onesignal/src/main/java/com/onesignal/OSDelayTaskController.kt create mode 100644 OneSignalSDK/unittest/src/test/java/com/onesignal/MockDelayTaskController.java diff --git a/OneSignalSDK/build.gradle b/OneSignalSDK/build.gradle index 836e452dbc..dfb2065de5 100644 --- a/OneSignalSDK/build.gradle +++ b/OneSignalSDK/build.gradle @@ -8,6 +8,7 @@ buildscript { targetSdkVersion: 28 ] androidGradlePluginVersion = '3.6.2' + androidConcurrentFutures = '1.1.0' googleServicesGradlePluginVersion = '4.3.2' huaweiAgconnectVersion = '1.2.1.301' huaweiHMSPushVersion = '4.0.2.300' diff --git a/OneSignalSDK/onesignal/build.gradle b/OneSignalSDK/onesignal/build.gradle index 9dd66477ff..8c797fddc4 100644 --- a/OneSignalSDK/onesignal/build.gradle +++ b/OneSignalSDK/onesignal/build.gradle @@ -49,6 +49,8 @@ dependencies { // Required for GoogleApiAvailability implementation 'com.google.android.gms:play-services-base:[17.0.0, 17.6.99]' + implementation("androidx.concurrent:concurrent-futures:$androidConcurrentFutures") + // firebase-messaging:18.0.0 is the last version before going to AndroidX // firebase-messaging:19.0.0 is the first version using AndroidX api 'com.google.firebase:firebase-messaging:[19.0.0, 22.0.99]' diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java index 2544949f63..a8d9e042cd 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java @@ -36,6 +36,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import androidx.concurrent.futures.CallbackToFutureAdapter; +import androidx.work.ListenableWorker; import com.onesignal.OneSignalDbContract.NotificationTable; @@ -97,9 +99,10 @@ public void onResult(boolean result) { osNotificationId, finalAndroidNotificationId, jsonStrPayload, - isRestoring, shownTimeStamp, - false); + isRestoring, + false, + true); // Delay to prevent CPU spikes. // Normally more than one notification is restored at a time. @@ -131,7 +134,7 @@ static int processJobForDisplay(OSNotificationController notificationController, } @WorkerThread - static int processJobForDisplay(OSNotificationController notificationController, boolean opened, boolean fromBackgroundLogic) { + private static int processJobForDisplay(OSNotificationController notificationController, boolean opened, boolean fromBackgroundLogic) { OneSignal.Log(OneSignal.LOG_LEVEL.DEBUG, "Starting processJobForDisplay opened: " + opened + " fromBackgroundLogic: " + fromBackgroundLogic); OSNotificationGenerationJob notificationJob = notificationController.getNotificationJob(); @@ -176,17 +179,22 @@ private static boolean shouldDisplayNotification(OSNotificationGenerationJob not */ static void processNotification(OSNotificationGenerationJob notificationJob, boolean opened, boolean notificationDisplayed) { saveNotification(notificationJob, opened); + CallbackToFutureAdapter.Completer callbackCompleter = notificationJob.getCallbackCompleter(); if (!notificationDisplayed) { // Notification channel disable or not displayed // save notification as dismissed to avoid user re-enabling channel and notification being displayed due to restore markNotificationAsDismissed(notificationJob); + + OneSignal.Log(OneSignal.LOG_LEVEL.DEBUG, "Process notification not displayed with callback completer: " + callbackCompleter); + if (callbackCompleter != null) + callbackCompleter.set(ListenableWorker.Result.success()); return; } // Logic for when the notification is displayed String notificationId = notificationJob.getApiNotificationId(); - OSReceiveReceiptController.getInstance().sendReceiveReceipt(notificationId); + OSReceiveReceiptController.getInstance().sendReceiveReceipt(callbackCompleter, notificationId); OneSignal.getSessionManager().onNotificationReceived(notificationId); } @@ -425,9 +433,10 @@ public void onResult(boolean result) { osNotificationId, androidNotificationId, jsonPayload.toString(), - isRestoring, timestamp, - isHighPriority); + isRestoring, + isHighPriority, + true); bundleResult.setWorkManagerProcessing(true); notificationProcessingCallback.onResult(true); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationSummaryManager.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationSummaryManager.java index e789b65747..5ac83bce9d 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationSummaryManager.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationSummaryManager.java @@ -143,7 +143,7 @@ private static void restoreSummary(Context context, String group) { null ); - OSNotificationRestoreWorkManager.showNotificationsFromCursor(context, cursor, 0); + OSNotificationRestoreWorkManager.showNotificationsFromCursor(context, cursor, 0, true); } catch (Throwable t) { OneSignal.Log(OneSignal.LOG_LEVEL.ERROR, "Error restoring notification records! ", t); } finally { diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSDelayTaskController.kt b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSDelayTaskController.kt new file mode 100644 index 0000000000..2b1969d922 --- /dev/null +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSDelayTaskController.kt @@ -0,0 +1,40 @@ +package com.onesignal + +import java.util.concurrent.ScheduledThreadPoolExecutor +import java.util.concurrent.ThreadFactory +import java.util.concurrent.TimeUnit +import kotlin.math.roundToInt + +open class OSDelayTaskController(private val logger: OSLogger) { + private val maxDelay = 25 + private val minDelay = 0 + + private var scheduledThreadPoolExecutor: ScheduledThreadPoolExecutor + + init { + scheduledThreadPoolExecutor = ScheduledThreadPoolExecutor(1, JobThreadFactory()) + } + + protected open fun getRandomNumber(): Int { + return (Math.random() * (maxDelay - minDelay + 1) + minDelay).roundToInt() + } + + open fun delayTaskByRandom(runnable: Runnable) { + val randomNum = getRandomNumber() + + logger.debug("OSDelayTaskController delaying task $randomNum second from thread: ${Thread.currentThread()}") + scheduledThreadPoolExecutor.schedule(runnable, randomNum.toLong(), TimeUnit.SECONDS) + } + + fun shutdownNow() { + scheduledThreadPoolExecutor.shutdownNow() + } + + private class JobThreadFactory : ThreadFactory { + private val delayThreadName = "ONE_SIGNAL_DELAY" + + override fun newThread(runnable: Runnable): Thread { + return Thread(runnable, delayThreadName) + } + } +} diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationController.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationController.java index e02b205eef..3c12b8f441 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationController.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationController.java @@ -30,6 +30,8 @@ import android.content.Context; import androidx.annotation.Nullable; +import androidx.concurrent.futures.CallbackToFutureAdapter; +import androidx.work.ListenableWorker; import org.json.JSONObject; @@ -40,6 +42,7 @@ 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"; + private final CallbackToFutureAdapter.Completer callbackCompleter; private final OSNotificationGenerationJob notificationJob; private boolean restoring; private boolean fromBackgroundLogic; @@ -48,9 +51,12 @@ public class OSNotificationController { this.restoring = restoring; this.fromBackgroundLogic = fromBackgroundLogic; this.notificationJob = notificationJob; + this.callbackCompleter = notificationJob.getCallbackCompleter(); } - OSNotificationController(Context context, JSONObject jsonPayload, boolean restoring, boolean fromBackgroundLogic, Long timestamp) { + OSNotificationController(CallbackToFutureAdapter.Completer callbackCompleter, + Context context, JSONObject jsonPayload, boolean restoring, boolean fromBackgroundLogic, Long timestamp) { + this.callbackCompleter = callbackCompleter; this.restoring = restoring; this.fromBackgroundLogic = fromBackgroundLogic; @@ -64,7 +70,7 @@ public class OSNotificationController { * @see OSNotificationGenerationJob */ private OSNotificationGenerationJob createNotificationJobFromCurrent(Context context, JSONObject jsonPayload, Long timestamp) { - OSNotificationGenerationJob notificationJob = new OSNotificationGenerationJob(context); + OSNotificationGenerationJob notificationJob = new OSNotificationGenerationJob(callbackCompleter, context); notificationJob.setJsonPayload(jsonPayload); notificationJob.setShownTimeStamp(timestamp); notificationJob.setRestoring(restoring); diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationGenerationJob.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationGenerationJob.java index 5e0fb67e71..1da9a0a664 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationGenerationJob.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationGenerationJob.java @@ -30,15 +30,17 @@ import android.content.Context; import android.net.Uri; -import androidx.core.app.NotificationCompat; +import androidx.annotation.Nullable; +import androidx.concurrent.futures.CallbackToFutureAdapter; +import androidx.work.ListenableWorker; -import org.json.JSONException; import org.json.JSONObject; import java.security.SecureRandom; public class OSNotificationGenerationJob { + private CallbackToFutureAdapter.Completer callbackCompleter; private OSNotification notification; private Context context; private JSONObject jsonPayload; @@ -54,6 +56,11 @@ public class OSNotificationGenerationJob { private Uri orgSound; OSNotificationGenerationJob(Context context) { + this(null, context); + } + + OSNotificationGenerationJob(CallbackToFutureAdapter.Completer callbackCompleter, Context context) { + this.callbackCompleter = callbackCompleter; this.context = context; } @@ -67,6 +74,11 @@ public class OSNotificationGenerationJob { this.notification = notification; } + @Nullable + public CallbackToFutureAdapter.Completer getCallbackCompleter() { + return callbackCompleter; + } + /** * Get the notification title from the payload */ diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationRestoreWorkManager.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationRestoreWorkManager.java index 945f9c6dd1..c8e6e982b4 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationRestoreWorkManager.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationRestoreWorkManager.java @@ -77,7 +77,7 @@ public Result doWork() { StringBuilder dbQuerySelection = OneSignalDbHelper.recentUninteractedWithNotificationsWhere(); skipVisibleNotifications(context, dbQuerySelection); - queryAndRestoreNotificationsAndBadgeCount(context, dbHelper, dbQuerySelection); + queryAndRestoreNotificationsAndBadgeCount(context, dbHelper, dbQuerySelection, false); return Result.success(); } @@ -87,7 +87,8 @@ public Result doWork() { private static void queryAndRestoreNotificationsAndBadgeCount( Context context, OneSignalDbHelper dbHelper, - StringBuilder dbQuerySelection) { + StringBuilder dbQuerySelection, + boolean needsWorkerThread) { OneSignal.Log(OneSignal.LOG_LEVEL.INFO, "Querying DB for notifications to restore: " + dbQuerySelection.toString()); @@ -104,7 +105,7 @@ private static void queryAndRestoreNotificationsAndBadgeCount( OneSignalDbContract.NotificationTable._ID + " DESC", // sort order, new to old NotificationLimitManager.MAX_NUMBER_OF_NOTIFICATIONS_STR // limit ); - showNotificationsFromCursor(context, cursor, DELAY_BETWEEN_NOTIFICATION_RESTORES_MS); + showNotificationsFromCursor(context, cursor, DELAY_BETWEEN_NOTIFICATION_RESTORES_MS, needsWorkerThread); BadgeCountUpdater.update(dbHelper, context); } catch (Throwable t) { OneSignal.Log(OneSignal.LOG_LEVEL.ERROR, "Error restoring notification records! ", t); @@ -144,7 +145,7 @@ private static void skipVisibleNotifications(Context context, StringBuilder dbQu * @param cursor - Source cursor to generate notifications from * @param delay - Delay to slow down process to ensure we don't spike CPU and I/O on the device */ - static void showNotificationsFromCursor(Context context, Cursor cursor, int delay) { + static void showNotificationsFromCursor(Context context, Cursor cursor, int delay, boolean needsWorkerThread) { if (!cursor.moveToFirst()) return; @@ -159,9 +160,10 @@ static void showNotificationsFromCursor(Context context, Cursor cursor, int dela osNotificationId, existingId, fullData, - true, dateTime, - false + true, + false, + needsWorkerThread ); if (delay > 0) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java index b25eb0ef80..5a42e24e64 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java @@ -3,13 +3,18 @@ import android.content.Context; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.concurrent.futures.CallbackToFutureAdapter; import androidx.work.Data; import androidx.work.ExistingWorkPolicy; +import androidx.work.ListenableWorker; import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; -import androidx.work.Worker; import androidx.work.WorkerParameters; +import com.google.common.util.concurrent.ListenableFuture; + +import org.jetbrains.annotations.NotNull; import org.json.JSONException; import org.json.JSONObject; @@ -19,6 +24,7 @@ class OSNotificationWorkManager { + private static final String OS_NOTIFICATION_ID = "os_bnotification_id"; private static final String ANDROID_NOTIF_ID_WORKER_DATA_PARAM = "android_notif_id"; private static final String JSON_PAYLOAD_WORKER_DATA_PARAM = "json_payload"; private static final String TIMESTAMP_WORKER_DATA_PARAM = "timestamp"; @@ -48,10 +54,22 @@ static void removeNotificationIdProcessed(String osNotificationId) { } } - static void beginEnqueueingWork(Context context, String osNotificationId, int androidNotificationId, String jsonPayload, - boolean isRestoring, long timestamp, boolean isHighPriority) { + static void beginEnqueueingWork(Context context, String osNotificationId, int androidNotificationId, String jsonPayload, long timestamp, + boolean isRestoring, boolean isHighPriority, boolean needsWorkerThread) { + + if (!needsWorkerThread) { + try { + JSONObject jsonPayloadObject = new JSONObject(jsonPayload); + processNotificationData(context, androidNotificationId, jsonPayloadObject, isRestoring, timestamp); + } catch (JSONException e) { + OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Error occurred parsing jsonPayload to JSONObject in beginEnqueueingWork e: " + e.getMessage()); + e.printStackTrace(); + } + return; + } // TODO: Need to figure out how to implement the isHighPriority param Data inputData = new Data.Builder() + .putString(OS_NOTIFICATION_ID, osNotificationId) .putInt(ANDROID_NOTIF_ID_WORKER_DATA_PARAM, androidNotificationId) .putString(JSON_PAYLOAD_WORKER_DATA_PARAM, jsonPayload) .putLong(TIMESTAMP_WORKER_DATA_PARAM, timestamp) @@ -67,7 +85,7 @@ static void beginEnqueueingWork(Context context, String osNotificationId, int an .enqueueUniqueWork(osNotificationId, ExistingWorkPolicy.KEEP, workRequest); } - public static class NotificationWorker extends Worker { + public static class NotificationWorker extends ListenableWorker { public NotificationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); @@ -75,7 +93,21 @@ public NotificationWorker(@NonNull Context context, @NonNull WorkerParameters wo @NonNull @Override - public Result doWork() { + public ListenableFuture startWork() { + return CallbackToFutureAdapter.getFuture(new CallbackToFutureAdapter.Resolver() { + @Nullable + @Override + public Object attachCompleter(@NonNull CallbackToFutureAdapter.Completer completer) throws Exception { + String notificationId = NotificationWorker.this.doWork(completer); + + // This value is used only for debug purposes: it will be used + // in toString() of returned future or error cases. + return "NotificationWorkerFutureCallback_" + notificationId; + } + }); + } + + private String doWork(@NotNull CallbackToFutureAdapter.Completer completer) { Data inputData = getInputData(); try { OneSignal.onesignalLog(OneSignal.LOG_LEVEL.DEBUG, "NotificationWorker running doWork with data: " + inputData); @@ -86,38 +118,45 @@ public Result doWork() { boolean isRestoring = inputData.getBoolean(IS_RESTORING_WORKER_DATA_PARAM, false); processNotificationData( + completer, getApplicationContext(), androidNotificationId, jsonPayload, isRestoring, timestamp); - } catch (JSONException e) { OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Error occurred doing work for job with id: " + getId().toString()); e.printStackTrace(); - return Result.failure(); + completer.setException(e); } - return Result.success(); + return inputData.getString(OS_NOTIFICATION_ID); } + } - private void processNotificationData(Context context, int androidNotificationId, JSONObject jsonPayload, boolean isRestoring, Long timestamp) { - OSNotification notification = new OSNotification(null, jsonPayload, androidNotificationId); - OSNotificationController controller = new OSNotificationController(context, jsonPayload, isRestoring, true, timestamp); - OSNotificationReceivedEvent notificationReceived = new OSNotificationReceivedEvent(controller, notification); + static void processNotificationData(Context context, int androidNotificationId, JSONObject jsonPayload, + boolean isRestoring, Long timestamp) { + processNotificationData(null, context, androidNotificationId, jsonPayload, isRestoring, timestamp); + } - if (OneSignal.remoteNotificationReceivedHandler != null) - try { - OneSignal.remoteNotificationReceivedHandler.remoteNotificationReceived(context, notificationReceived); - } catch (Throwable t) { - OneSignal.Log(OneSignal.LOG_LEVEL.ERROR, "remoteNotificationReceived throw an exception. Displaying normal OneSignal notification.", t); - notificationReceived.complete(notification); + static void processNotificationData(CallbackToFutureAdapter.Completer completer, + Context context, int androidNotificationId, JSONObject jsonPayload, + boolean isRestoring, Long timestamp) { + OSNotification notification = new OSNotification(null, jsonPayload, androidNotificationId); + OSNotificationController controller = new OSNotificationController(completer, context, jsonPayload, isRestoring, true, timestamp); + OSNotificationReceivedEvent notificationReceived = new OSNotificationReceivedEvent(controller, notification); - throw t; - } - else { - OneSignal.Log(OneSignal.LOG_LEVEL.WARN, "remoteNotificationReceivedHandler not setup, displaying normal OneSignal notification"); + if (OneSignal.remoteNotificationReceivedHandler != null) + try { + OneSignal.remoteNotificationReceivedHandler.remoteNotificationReceived(context, notificationReceived); + } catch (Throwable t) { + OneSignal.Log(OneSignal.LOG_LEVEL.ERROR, "remoteNotificationReceived throw an exception. Displaying normal OneSignal notification.", t); notificationReceived.complete(notification); + + throw t; } + else { + OneSignal.Log(OneSignal.LOG_LEVEL.WARN, "remoteNotificationReceivedHandler not setup, displaying normal OneSignal notification"); + notificationReceived.complete(notification); } } } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSReceiveReceiptController.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSReceiveReceiptController.java index 2e1d9c2b06..c51138c52d 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSReceiveReceiptController.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSReceiveReceiptController.java @@ -28,51 +28,64 @@ package com.onesignal; import androidx.annotation.NonNull; +import androidx.concurrent.futures.CallbackToFutureAdapter; +import androidx.work.ListenableWorker; class OSReceiveReceiptController { + private final OSDelayTaskController taskController; private final OSReceiveReceiptRepository repository; + private final OSRemoteParamController remoteParamController; private static OSReceiveReceiptController sInstance; - private OSReceiveReceiptController() { + private OSReceiveReceiptController(OSRemoteParamController remoteParamController, OSDelayTaskController taskController) { + this.remoteParamController = remoteParamController; + this.taskController = taskController; this.repository = new OSReceiveReceiptRepository(); } synchronized public static OSReceiveReceiptController getInstance() { if (sInstance == null) - sInstance = new OSReceiveReceiptController(); + sInstance = new OSReceiveReceiptController(OneSignal.getRemoteParamController(), OneSignal.getDelayTaskController()); return sInstance; } - void sendReceiveReceipt(@NonNull final String notificationId) { - String appId = OneSignal.appId == null || OneSignal.appId.isEmpty() ? OneSignal.getSavedAppId() : OneSignal.appId; - String playerId = OneSignal.getUserId(); + void sendReceiveReceipt(final CallbackToFutureAdapter.Completer callbackCompleter, @NonNull final String notificationId) { + final String appId = OneSignal.appId == null || OneSignal.appId.isEmpty() ? OneSignal.getSavedAppId() : OneSignal.appId; + final String playerId = OneSignal.getUserId(); - if (!isReceiveReceiptEnabled()) { + if (!remoteParamController.isReceiveReceiptEnabled()) { OneSignal.Log(OneSignal.LOG_LEVEL.DEBUG, "sendReceiveReceipt disable"); + endCallbackCompleterWithSuccess(callbackCompleter); return; } - OneSignal.Log(OneSignal.LOG_LEVEL.DEBUG, "sendReceiveReceipt appId: " + appId + " playerId: " + playerId + " notificationId: " + notificationId); - repository.sendReceiveReceipt(appId, playerId, notificationId, new OneSignalRestClient.ResponseHandler() { + Runnable receiveReceiptRunnable = new Runnable() { @Override - void onSuccess(String response) { - OneSignal.Log(OneSignal.LOG_LEVEL.DEBUG, "Receive receipt sent for notificationID: " + notificationId); - } + public void run() { + repository.sendReceiveReceipt(appId, playerId, notificationId, new OneSignalRestClient.ResponseHandler() { + @Override + void onSuccess(String response) { + OneSignal.Log(OneSignal.LOG_LEVEL.DEBUG, "Receive receipt sent for notificationID: " + notificationId); + endCallbackCompleterWithSuccess(callbackCompleter); + } - @Override - void onFailure(int statusCode, String response, Throwable throwable) { - OneSignal.Log(OneSignal.LOG_LEVEL.ERROR, "Receive receipt failed with statusCode: " + statusCode + " response: " + response); + @Override + void onFailure(int statusCode, String response, Throwable throwable) { + OneSignal.Log(OneSignal.LOG_LEVEL.ERROR, "Receive receipt failed with statusCode: " + statusCode + " response: " + response); + endCallbackCompleterWithSuccess(callbackCompleter); + } + }); } - }); + }; + + taskController.delayTaskByRandom(receiveReceiptRunnable); } - private boolean isReceiveReceiptEnabled() { - return OneSignalPrefs.getBool( - OneSignalPrefs.PREFS_ONESIGNAL, - OneSignalPrefs.PREFS_OS_RECEIVE_RECEIPTS_ENABLED, - false - ); + private void endCallbackCompleterWithSuccess(CallbackToFutureAdapter.Completer callbackCompleter) { + OneSignal.Log(OneSignal.LOG_LEVEL.DEBUG, "Receive receipt ending with success callback completer: " + callbackCompleter); + if (callbackCompleter != null) + callbackCompleter.set(ListenableWorker.Result.success()); } } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSRemoteParamController.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSRemoteParamController.java index abed21ef79..9887a5c97e 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSRemoteParamController.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSRemoteParamController.java @@ -33,17 +33,14 @@ void saveRemoteParams(OneSignalRemoteParams.Params remoteParams, OneSignalPrefs.PREFS_OS_CLEAR_GROUP_SUMMARY_CLICK, remoteParams.clearGroupOnSummaryClick ); - OneSignalPrefs.saveBool( - OneSignalPrefs.PREFS_ONESIGNAL, - OneSignalPrefs.PREFS_OS_RECEIVE_RECEIPTS_ENABLED, - remoteParams.receiveReceiptEnabled - ); OneSignalPrefs.saveBool( OneSignalPrefs.PREFS_ONESIGNAL, preferences.getOutcomesV2KeyName(), remoteParams.influenceParams.outcomesV2ServiceEnabled ); + saveReceiveReceiptEnabled(remoteParams.receiveReceiptEnabled); + logger.debug("OneSignal saveInfluenceParams: " + remoteParams.influenceParams.toString()); trackerFactory.saveInfluenceParams(remoteParams.influenceParams); @@ -85,6 +82,22 @@ void clearRemoteParams() { remoteParams = null; } + private void saveReceiveReceiptEnabled(boolean receiveReceiptEnabled) { + OneSignalPrefs.saveBool( + OneSignalPrefs.PREFS_ONESIGNAL, + OneSignalPrefs.PREFS_OS_RECEIVE_RECEIPTS_ENABLED, + receiveReceiptEnabled + ); + } + + boolean isReceiveReceiptEnabled() { + return OneSignalPrefs.getBool( + OneSignalPrefs.PREFS_ONESIGNAL, + OneSignalPrefs.PREFS_OS_RECEIVE_RECEIPTS_ENABLED, + false + ); + } + boolean getFirebaseAnalyticsEnabled() { return OneSignalPrefs.getBool( OneSignalPrefs.PREFS_ONESIGNAL, diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java index 90ae871ce1..0e0e69d34c 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java @@ -426,6 +426,7 @@ static OSInAppMessageController getInAppMessageController() { } private static OSTime time = new OSTimeImpl(); private static OSRemoteParamController remoteParamController = new OSRemoteParamController(); + private static OSDelayTaskController delayTaskController = new OSDelayTaskController(logger); private static OSTaskController taskController = new OSTaskController(logger); private static OSTaskRemoteController taskRemoteController = new OSTaskRemoteController(remoteParamController, logger); private static OneSignalAPIClient apiClient = new OneSignalRestClientWrapper(); @@ -3190,6 +3191,7 @@ static void fireSMSUpdateFailure() { static OSTime getTime() { return time; } + /* * Start Mock Injection module */ @@ -3209,6 +3211,10 @@ static void setSharedPreferences(OSSharedPreferences preferences) { OneSignal.preferences = preferences; } + static void setDelayTaskController(OSDelayTaskController delayTaskController) { + OneSignal.delayTaskController = delayTaskController; + } + static OSSessionManager.SessionListener getSessionListener() { return sessionListener; } @@ -3233,6 +3239,10 @@ static OSTaskController getTaskController() { return taskController; } + static OSDelayTaskController getDelayTaskController() { + return delayTaskController; + } + static FocusTimeController getFocusTimeController() { if (focusTimeController == null) { focusTimeController = new FocusTimeController(new OSFocusTimeProcessorFactory(), logger); diff --git a/OneSignalSDK/unittest/build.gradle b/OneSignalSDK/unittest/build.gradle index bf647fda9c..0144a18932 100644 --- a/OneSignalSDK/unittest/build.gradle +++ b/OneSignalSDK/unittest/build.gradle @@ -16,7 +16,6 @@ ext { apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' android { compileSdkVersion rootProject.buildVersions.compileSdkVersion @@ -75,6 +74,7 @@ dependencies { testImplementation "androidx.test:core:$androidTestCoreVersion" testImplementation "androidx.work:work-testing:$androidWorkTestVersion" + testImplementation("androidx.concurrent:concurrent-futures:$androidConcurrentFutures") testImplementation "org.robolectric:robolectric:$roboelectricVersion" testImplementation "org.awaitility:awaitility:$awaitilityVersion" diff --git a/OneSignalSDK/unittest/src/test/java/com/onesignal/MockDelayTaskController.java b/OneSignalSDK/unittest/src/test/java/com/onesignal/MockDelayTaskController.java new file mode 100644 index 0000000000..42dd9a6881 --- /dev/null +++ b/OneSignalSDK/unittest/src/test/java/com/onesignal/MockDelayTaskController.java @@ -0,0 +1,32 @@ +package com.onesignal; + +import org.jetbrains.annotations.NotNull; + +public class MockDelayTaskController extends OSDelayTaskController { + private int mockedRandomValue = 0; + private boolean runOnSameThread = true; + + public MockDelayTaskController(OSLogger logger) { + super(logger); + } + + protected int getRandomNumber() { + return mockedRandomValue; + } + + public void delayTaskByRandom(@NotNull Runnable runnable) { + if (runOnSameThread) { + runnable.run(); + } else { + super.delayTaskByRandom(runnable); + } + } + + public void setMockedRandomValue(int mockedRandomValue) { + this.mockedRandomValue = mockedRandomValue; + } + + public void setRunOnSameThread(boolean runOnSameThread) { + this.runOnSameThread = runOnSameThread; + } +} diff --git a/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java b/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java index 299f75b7c9..b988800360 100644 --- a/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java +++ b/OneSignalSDK/unittest/src/test/java/com/onesignal/OneSignalPackagePrivateHelper.java @@ -120,6 +120,10 @@ public static void OneSignal_setTrackerFactory(OSTrackerFactory trackerFactory) OneSignal.setTrackerFactory(trackerFactory); } + public static void OneSignal_setDelayTaskController(OSDelayTaskController delayTaskController) { + OneSignal.setDelayTaskController(delayTaskController); + } + public static JSONObject bundleAsJSONObject(Bundle bundle) { return NotificationBundleProcessor.bundleAsJSONObject(bundle); } @@ -282,6 +286,7 @@ public static ConcurrentLinkedQueue OneSignal_taskQueueWaitingForInit( public static void OneSignal_OSTaskController_ShutdownNow() { OneSignal.getTaskRemoteController().shutdownNow(); OneSignal.getTaskController().shutdownNow(); + OneSignal.getDelayTaskController().shutdownNow(); } public static boolean OneSignal_requiresUserPrivacyConsent() { diff --git a/OneSignalSDK/unittest/src/test/java/com/onesignal/StaticResetHelper.java b/OneSignalSDK/unittest/src/test/java/com/onesignal/StaticResetHelper.java index 6c89cebd68..dfabbbd55a 100644 --- a/OneSignalSDK/unittest/src/test/java/com/onesignal/StaticResetHelper.java +++ b/OneSignalSDK/unittest/src/test/java/com/onesignal/StaticResetHelper.java @@ -17,6 +17,7 @@ public class StaticResetHelper { private static Collection classes = new ArrayList<>(); public static void load() { + OSLogger logger = new OSLogWrapper(); classes.add(new ClassState(OneSignal.class, field -> { if (field.getName().equals("unprocessedOpenedNotifis")) { field.set(null, new ArrayList()); @@ -25,13 +26,14 @@ public static void load() { field.set(null, new OSRemoteParamController()); return true; } else if (field.getName().equals("taskController")) { - OSLogger logger = new OSLogWrapper(); field.set(null, new OSTaskController(logger)); return true; } else if (field.getName().equals("taskRemoteController")) { - OSLogger logger = new OSLogWrapper(); field.set(null, new OSTaskRemoteController(OneSignal.getRemoteParamController(), logger)); return true; + } else if (field.getName().equals("delayTaskController")) { + field.set(null, new MockDelayTaskController(logger)); + return true; } else if (field.getName().equals("inAppMessageControllerFactory")) { field.set(null, new OSInAppMessageControllerFactory()); return true; 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 5ac1ad0004..715f78bcad 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java @@ -121,6 +121,7 @@ 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; @@ -1253,8 +1254,11 @@ public void shouldShowInAppPreviewWhenOpeningPreviewNotification() throws Except } @Test - @Config(shadows = { ShadowReceiveReceiptController.class, ShadowGenerateNotification.class }) + @Config(shadows = { ShadowGenerateNotification.class }) public void shouldSendReceivedReceiptWhenEnabled() throws Exception { + + ShadowOneSignalRestClient.setRemoteParamsReceiveReceiptsEnable(true); + String appId = "b2f7f966-d8cc-11e4-bed1-df8f05be55ba"; OneSignal.setAppId(appId); OneSignal.initWithContext(blankActivity); diff --git a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java index 4b7a69d557..81222a7d21 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/MainOneSignalClassRunner.java @@ -38,12 +38,14 @@ import android.database.Cursor; import android.net.ConnectivityManager; import android.os.Bundle; +import android.util.Log; import androidx.annotation.Nullable; import androidx.test.core.app.ApplicationProvider; import com.onesignal.FocusDelaySyncJobService; import com.onesignal.FocusDelaySyncService; +import com.onesignal.MockDelayTaskController; import com.onesignal.MockOSLog; import com.onesignal.MockOSSharedPreferences; import com.onesignal.MockOSTimeImpl; @@ -133,6 +135,7 @@ import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_getSessionListener; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_handleNotificationOpen; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_isInForeground; +import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setDelayTaskController; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setSessionManager; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTime; import static com.onesignal.OneSignalPackagePrivateHelper.OneSignal_setTrackerFactory; @@ -1346,6 +1349,40 @@ public void testNotificationReceivedNoSendReceivedRequest_WhenNotificationNotDis assertNotEquals("notifications/UUID/report_received", ShadowOneSignalRestClient.lastUrl); } + @Test + @Config(shadows = { ShadowGenerateNotification.class }) + public void testNotificationReceivedNoSendReceivedRequest_Delay() throws Exception { + int delay = 2; + MockDelayTaskController mockDelayTaskController = new MockDelayTaskController(new MockOSLog()); + mockDelayTaskController.setMockedRandomValue(delay); + mockDelayTaskController.setRunOnSameThread(false); + OneSignal_setDelayTaskController(mockDelayTaskController); + + ShadowOneSignalRestClient.setRemoteParamsReceiveReceiptsEnable(true); + // First init run for appId to be saved + // At least OneSignal was init once for user to be subscribed + // If this doesn't' happen, notifications will not arrive + OneSignal.setAppId(ONESIGNAL_APP_ID); + OneSignal.initWithContext(blankActivity); + threadAndTaskWait(); + + long calledTime = System.currentTimeMillis(); + + // 1. Receive a notification in background + FCMBroadcastReceiver_processBundle(blankActivity, getBaseNotifBundle()); + threadAndTaskWait(); + + // 2. Check that report_received where sent + Awaitility.await() + .atMost(new Duration(3, TimeUnit.SECONDS)) + .pollInterval(new Duration(1, TimeUnit.SECONDS)) + .untilAsserted(() -> { + assertEquals(3, ShadowOneSignalRestClient.requests.size()); + assertEquals("notifications/UUID/report_received", ShadowOneSignalRestClient.lastUrl); + assertTrue(System.currentTimeMillis() - calledTime >= delay * 1000); + }); + } + /** * @see #testNotificationReceivedNoSendReceivedRequest_WhenNotificationNotDisplayed */ From a2f84cd2dc6d01b41f589ef29d0c61a62a9e773e Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 28 Jun 2021 14:24:57 -0300 Subject: [PATCH 2/6] Reformat code for processCollapseKey method --- .../NotificationBundleProcessor.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java index a8d9e042cd..bef81c0424 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java @@ -347,23 +347,23 @@ private static void processCollapseKey(OSNotificationGenerationJob notificationJ return; String collapse_id = notificationJob.getJsonPayload().optString("collapse_key"); - OneSignalDbHelper dbHelper = OneSignalDbHelper.getInstance(notificationJob.getContext()); - Cursor cursor = dbHelper.query( - NotificationTable.TABLE_NAME, - new String[]{NotificationTable.COLUMN_NAME_ANDROID_NOTIFICATION_ID}, // retColumn - NotificationTable.COLUMN_NAME_COLLAPSE_ID + " = ? AND " + - NotificationTable.COLUMN_NAME_DISMISSED + " = 0 AND " + - NotificationTable.COLUMN_NAME_OPENED + " = 0 ", - new String[]{collapse_id}, - null, null, null); - - if (cursor.moveToFirst()) { - int androidNotificationId = cursor.getInt(cursor.getColumnIndex(NotificationTable.COLUMN_NAME_ANDROID_NOTIFICATION_ID)); - notificationJob.setAndroidIdWithoutOverriding(androidNotificationId); - } + OneSignalDbHelper dbHelper = OneSignalDbHelper.getInstance(notificationJob.getContext()); + Cursor cursor = dbHelper.query( + NotificationTable.TABLE_NAME, + new String[]{NotificationTable.COLUMN_NAME_ANDROID_NOTIFICATION_ID}, // retColumn + NotificationTable.COLUMN_NAME_COLLAPSE_ID + " = ? AND " + + NotificationTable.COLUMN_NAME_DISMISSED + " = 0 AND " + + NotificationTable.COLUMN_NAME_OPENED + " = 0 ", + new String[]{collapse_id}, + null, null, null); + + if (cursor.moveToFirst()) { + int androidNotificationId = cursor.getInt(cursor.getColumnIndex(NotificationTable.COLUMN_NAME_ANDROID_NOTIFICATION_ID)); + notificationJob.setAndroidIdWithoutOverriding(androidNotificationId); + } - cursor.close(); - } + cursor.close(); + } /** * Process bundle passed from FCM / HMS / ADM broadcast receiver From 08132867f58807ff85ff00714b314e123160b086 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 28 Jun 2021 14:25:46 -0300 Subject: [PATCH 3/6] Refactor from NotNull to NonNull --- .../main/java/com/onesignal/OSNotificationWorkManager.java | 3 +-- .../test/java/com/onesignal/MockDelayTaskController.java | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java index 5a42e24e64..dfe2912b49 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java @@ -14,7 +14,6 @@ import com.google.common.util.concurrent.ListenableFuture; -import org.jetbrains.annotations.NotNull; import org.json.JSONException; import org.json.JSONObject; @@ -107,7 +106,7 @@ public Object attachCompleter(@NonNull CallbackToFutureAdapter.Completer }); } - private String doWork(@NotNull CallbackToFutureAdapter.Completer completer) { + private String doWork(@NonNull CallbackToFutureAdapter.Completer completer) { Data inputData = getInputData(); try { OneSignal.onesignalLog(OneSignal.LOG_LEVEL.DEBUG, "NotificationWorker running doWork with data: " + inputData); diff --git a/OneSignalSDK/unittest/src/test/java/com/onesignal/MockDelayTaskController.java b/OneSignalSDK/unittest/src/test/java/com/onesignal/MockDelayTaskController.java index 42dd9a6881..f2ea5bc082 100644 --- a/OneSignalSDK/unittest/src/test/java/com/onesignal/MockDelayTaskController.java +++ b/OneSignalSDK/unittest/src/test/java/com/onesignal/MockDelayTaskController.java @@ -1,6 +1,6 @@ package com.onesignal; -import org.jetbrains.annotations.NotNull; +import androidx.annotation.NonNull; public class MockDelayTaskController extends OSDelayTaskController { private int mockedRandomValue = 0; @@ -10,11 +10,11 @@ public MockDelayTaskController(OSLogger logger) { super(logger); } - protected int getRandomNumber() { + protected int getRandomDelay() { return mockedRandomValue; } - public void delayTaskByRandom(@NotNull Runnable runnable) { + public void delayTaskByRandom(@NonNull Runnable runnable) { if (runOnSameThread) { runnable.run(); } else { From 99d7f41a4b426ff2052e5fe1e3be934d73215012 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 28 Jun 2021 14:26:15 -0300 Subject: [PATCH 4/6] Rename from getRandomNumber() to getRandomDelay() --- .../src/main/java/com/onesignal/OSDelayTaskController.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSDelayTaskController.kt b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSDelayTaskController.kt index 2b1969d922..3dfe35c265 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSDelayTaskController.kt +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSDelayTaskController.kt @@ -15,12 +15,12 @@ open class OSDelayTaskController(private val logger: OSLogger) { scheduledThreadPoolExecutor = ScheduledThreadPoolExecutor(1, JobThreadFactory()) } - protected open fun getRandomNumber(): Int { + protected open fun getRandomDelay(): Int { return (Math.random() * (maxDelay - minDelay + 1) + minDelay).roundToInt() } open fun delayTaskByRandom(runnable: Runnable) { - val randomNum = getRandomNumber() + val randomNum = getRandomDelay() logger.debug("OSDelayTaskController delaying task $randomNum second from thread: ${Thread.currentThread()}") scheduledThreadPoolExecutor.schedule(runnable, randomNum.toLong(), TimeUnit.SECONDS) From 8fba77762859067979073e6e83904902167a90e9 Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Mon, 28 Jun 2021 14:26:44 -0300 Subject: [PATCH 5/6] Simplify queryAndRestoreNotificationsAndBadgeCount method params * Replace needsWorkerThread with false --- .../com/onesignal/OSNotificationRestoreWorkManager.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationRestoreWorkManager.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationRestoreWorkManager.java index c8e6e982b4..b803934b13 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationRestoreWorkManager.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationRestoreWorkManager.java @@ -77,7 +77,7 @@ public Result doWork() { StringBuilder dbQuerySelection = OneSignalDbHelper.recentUninteractedWithNotificationsWhere(); skipVisibleNotifications(context, dbQuerySelection); - queryAndRestoreNotificationsAndBadgeCount(context, dbHelper, dbQuerySelection, false); + queryAndRestoreNotificationsAndBadgeCount(context, dbHelper, dbQuerySelection); return Result.success(); } @@ -87,8 +87,7 @@ public Result doWork() { private static void queryAndRestoreNotificationsAndBadgeCount( Context context, OneSignalDbHelper dbHelper, - StringBuilder dbQuerySelection, - boolean needsWorkerThread) { + StringBuilder dbQuerySelection) { OneSignal.Log(OneSignal.LOG_LEVEL.INFO, "Querying DB for notifications to restore: " + dbQuerySelection.toString()); @@ -105,7 +104,7 @@ private static void queryAndRestoreNotificationsAndBadgeCount( OneSignalDbContract.NotificationTable._ID + " DESC", // sort order, new to old NotificationLimitManager.MAX_NUMBER_OF_NOTIFICATIONS_STR // limit ); - showNotificationsFromCursor(context, cursor, DELAY_BETWEEN_NOTIFICATION_RESTORES_MS, needsWorkerThread); + showNotificationsFromCursor(context, cursor, DELAY_BETWEEN_NOTIFICATION_RESTORES_MS, false); BadgeCountUpdater.update(dbHelper, context); } catch (Throwable t) { OneSignal.Log(OneSignal.LOG_LEVEL.ERROR, "Error restoring notification records! ", t); From 5ec91ab63490f4dd10f246f319c0ea8d3a5df9be Mon Sep 17 00:00:00 2001 From: Jeasmine Nahui Date: Thu, 1 Jul 2021 18:35:45 -0300 Subject: [PATCH 6/6] Cancel Worker thread if notification nor processed * Notification might not be processed if it is a restored one or an IAM --- .../main/java/com/onesignal/NotificationBundleProcessor.java | 5 +++++ .../main/java/com/onesignal/OSNotificationWorkManager.java | 1 - .../java/com/test/onesignal/GenerateNotificationRunner.java | 1 - 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java index bef81c0424..17878dcb86 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java @@ -165,6 +165,11 @@ private static int processJobForDisplay(OSNotificationController notificationCon String osNotificationId = OSNotificationFormatHelper.getOSNotificationIdFromJson(notificationController.getNotificationJob().getJsonPayload()); OSNotificationWorkManager.removeNotificationIdProcessed(osNotificationId); OneSignal.handleNotificationReceived(notificationJob); + } else { + CallbackToFutureAdapter.Completer callbackCompleter = notificationJob.getCallbackCompleter(); + OneSignal.Log(OneSignal.LOG_LEVEL.DEBUG, "Process notification restored or IAM with callback completer: " + callbackCompleter); + if (callbackCompleter != null) + callbackCompleter.set(ListenableWorker.Result.success()); } return androidNotificationId; diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java index dfe2912b49..1016c8eccb 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSNotificationWorkManager.java @@ -55,7 +55,6 @@ static void removeNotificationIdProcessed(String osNotificationId) { static void beginEnqueueingWork(Context context, String osNotificationId, int androidNotificationId, String jsonPayload, long timestamp, boolean isRestoring, boolean isHighPriority, boolean needsWorkerThread) { - if (!needsWorkerThread) { try { JSONObject jsonPayloadObject = new JSONObject(jsonPayload); 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 715f78bcad..637ee38cbd 100644 --- a/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java +++ b/OneSignalSDK/unittest/src/test/java/com/test/onesignal/GenerateNotificationRunner.java @@ -1256,7 +1256,6 @@ public void shouldShowInAppPreviewWhenOpeningPreviewNotification() throws Except @Test @Config(shadows = { ShadowGenerateNotification.class }) public void shouldSendReceivedReceiptWhenEnabled() throws Exception { - ShadowOneSignalRestClient.setRemoteParamsReceiveReceiptsEnable(true); String appId = "b2f7f966-d8cc-11e4-bed1-df8f05be55ba";