Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Receive Receipt delay #1356

Merged
merged 6 commits into from
Jul 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions OneSignalSDK/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 2 additions & 0 deletions OneSignalSDK/onesignal/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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]'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -162,6 +165,11 @@ static int processJobForDisplay(OSNotificationController notificationController,
String osNotificationId = OSNotificationFormatHelper.getOSNotificationIdFromJson(notificationController.getNotificationJob().getJsonPayload());
OSNotificationWorkManager.removeNotificationIdProcessed(osNotificationId);
OneSignal.handleNotificationReceived(notificationJob);
} else {
CallbackToFutureAdapter.Completer<ListenableWorker.Result> 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;
Expand All @@ -176,17 +184,22 @@ private static boolean shouldDisplayNotification(OSNotificationGenerationJob not
*/
static void processNotification(OSNotificationGenerationJob notificationJob, boolean opened, boolean notificationDisplayed) {
saveNotification(notificationJob, opened);
CallbackToFutureAdapter.Completer<ListenableWorker.Result> 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);
}

Expand Down Expand Up @@ -339,23 +352,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
Expand Down Expand Up @@ -425,9 +438,10 @@ public void onResult(boolean result) {
osNotificationId,
androidNotificationId,
jsonPayload.toString(),
isRestoring,
timestamp,
isHighPriority);
isRestoring,
isHighPriority,
true);

bundleResult.setWorkManagerProcessing(true);
notificationProcessingCallback.onResult(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 getRandomDelay(): Int {
return (Math.random() * (maxDelay - minDelay + 1) + minDelay).roundToInt()
}

open fun delayTaskByRandom(runnable: Runnable) {
val randomNum = getRandomDelay()

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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<ListenableWorker.Result> callbackCompleter;
private final OSNotificationGenerationJob notificationJob;
private boolean restoring;
private boolean fromBackgroundLogic;
Expand All @@ -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<ListenableWorker.Result> callbackCompleter,
Context context, JSONObject jsonPayload, boolean restoring, boolean fromBackgroundLogic, Long timestamp) {
this.callbackCompleter = callbackCompleter;
this.restoring = restoring;
this.fromBackgroundLogic = fromBackgroundLogic;

Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ListenableWorker.Result> callbackCompleter;
private OSNotification notification;
private Context context;
private JSONObject jsonPayload;
Expand All @@ -54,6 +56,11 @@ public class OSNotificationGenerationJob {
private Uri orgSound;

OSNotificationGenerationJob(Context context) {
this(null, context);
}

OSNotificationGenerationJob(CallbackToFutureAdapter.Completer<ListenableWorker.Result> callbackCompleter, Context context) {
this.callbackCompleter = callbackCompleter;
this.context = context;
}

Expand All @@ -67,6 +74,11 @@ public class OSNotificationGenerationJob {
this.notification = notification;
}

@Nullable
public CallbackToFutureAdapter.Completer<ListenableWorker.Result> getCallbackCompleter() {
return callbackCompleter;
}

/**
* Get the notification title from the payload
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,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);
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);
Expand Down Expand Up @@ -144,7 +144,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;

Expand All @@ -159,9 +159,10 @@ static void showNotificationsFromCursor(Context context, Cursor cursor, int dela
osNotificationId,
existingId,
fullData,
true,
dateTime,
false
true,
false,
needsWorkerThread
);

if (delay > 0)
Expand Down
Loading