Skip to content

Commit

Permalink
Add TTL notification display control
Browse files Browse the repository at this point in the history
* Notifications where being batches and displayed by android standBy
* For cases where notification sent time + ttl is more than the current time, don't display notification
  • Loading branch information
Jeasmine committed Nov 10, 2021
1 parent 3bcc63f commit 473b6c7
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<ListenableWorker.Result> callbackCompleter;
private final OSNotificationGenerationJob notificationJob;
Expand Down Expand Up @@ -88,13 +92,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 ttl = isNotificationWithinTTL();
if (display && ttl) {
// 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
Expand All @@ -120,6 +125,32 @@ private void notDisplayNotificationLogic(OSNotification originalNotification) {
}
}

public boolean isNotificationWithinTTL() {
boolean useTtl = OneSignal.getRemoteParamController().isRestoreTTLFilterActive();
if (!useTtl)
return true;

JSONObject jsonPayload = notificationJob.getJsonPayload();
long currentTime = OneSignal.getTime().getCurrentThreadTimeMillis();
long currentTimeInSeconds = currentTime / 1_000;
long sentTime;
// If available TTL times comes in seconds, by default is 3 days in seconds
int ttl;

if (jsonPayload.has(GOOGLE_TTL_KEY)) {
sentTime = jsonPayload.optLong(GOOGLE_SENT_TIME_KEY, currentTime) / 1_000;
ttl = jsonPayload.optInt(GOOGLE_TTL_KEY, OSNotificationRestoreWorkManager.DEFAULT_TTL_IF_NOT_IN_PAYLOAD);
} else if (jsonPayload.has(HMS_TTL_KEY)) {
sentTime = jsonPayload.optLong(HMS_SENT_TIME_KEY, currentTime) / 1_000;
ttl = jsonPayload.optInt(HMS_TTL_KEY, OSNotificationRestoreWorkManager.DEFAULT_TTL_IF_NOT_IN_PAYLOAD);
} else {
// If no TTL provided display notification
return true;
}

return sentTime + ttl > currentTimeInSeconds;
}

public OSNotificationGenerationJob getNotificationJob() {
return notificationJob;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ void saveRemoteParams(OneSignalRemoteParams.Params remoteParams,
OneSignalPrefs.PREFS_GT_FIREBASE_TRACKING_ENABLED,
remoteParams.firebaseAnalytics
);
OneSignalPrefs.saveBool(
OneSignalPrefs.PREFS_ONESIGNAL,
OneSignalPrefs.PREFS_OS_RESTORE_TTL_FILTER,
remoteParams.restoreTTLFilter
);
saveRestoreTTLFilter(remoteParams.restoreTTLFilter);
OneSignalPrefs.saveBool(
OneSignalPrefs.PREFS_ONESIGNAL,
OneSignalPrefs.PREFS_OS_CLEAR_GROUP_SUMMARY_CLICK,
Expand Down Expand Up @@ -82,6 +78,18 @@ void clearRemoteParams() {
remoteParams = null;
}

private void saveRestoreTTLFilter(boolean restoreTTLFilter) {
OneSignalPrefs.saveBool(
OneSignalPrefs.PREFS_ONESIGNAL,
OneSignalPrefs.PREFS_OS_RESTORE_TTL_FILTER,
remoteParams.restoreTTLFilter
);
}

boolean isRestoreTTLFilterActive() {
return OneSignalPrefs.getBool(OneSignalPrefs.PREFS_ONESIGNAL, OneSignalPrefs.PREFS_OS_RESTORE_TTL_FILTER, true);
}

private void saveReceiveReceiptEnabled(boolean receiveReceiptEnabled) {
OneSignalPrefs.saveBool(
OneSignalPrefs.PREFS_ONESIGNAL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ static StringBuilder recentUninteractedWithNotificationsWhere() {
NotificationTable.COLUMN_NAME_IS_SUMMARY + " = 0"
);

boolean useTtl = OneSignalPrefs.getBool(OneSignalPrefs.PREFS_ONESIGNAL, OneSignalPrefs.PREFS_OS_RESTORE_TTL_FILTER,true);
boolean useTtl = OneSignal.getRemoteParamController().isRestoreTTLFilterActive();
if (useTtl) {
String expireTimeWhere = " AND " + NotificationTable.COLUMN_NAME_EXPIRE_TIME + " > " + currentTimeSec;
where.append(expireTimeWhere);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

import com.huawei.hms.push.RemoteMessage;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.concurrent.atomic.AtomicBoolean;

/**
Expand All @@ -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);

/**
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,8 @@ public class ShadowBadgeCountUpdater {
public static void updateCount(int count, Context context) {
lastCount = count;
}

public static void resetStatics() {
lastCount = 0;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}

}
Loading

0 comments on commit 473b6c7

Please sign in to comment.