Skip to content

Commit 5099bcb

Browse files
authored
Outcome callback cleanup (#871)
* Changing outcome callbacks and removing cache settings * Cache settings was removed as a decision to prevent users from turning off cache (will need to find a good way to clean cached notifications/outcomes in future though) * Changed callback functionality so there is no failure now (failure could give users the ability to reattempt sending an outcome which we do already through caching and new init) * Fixed example app text view for debugging * Also cleaned unique notification id for-loops so only one is needed * Previously changed/removed getParams * Added getParams back and make sure it returns a String * Added new outcome callback and cache cleaning * Outcome callback should just return the entire OutcomeEvent instead of a name * Also removed the failure and only allow success since we don't want people reattempting sending unique outcomes when they will be reattempted by us anyway (could result in duplicate outcome events failling and being sent later) * Added SQL caching for the unique outcome events with notifications (ATTRIBUTED) and cleaning so they are wiped away when the cached notifications are wiped away * Added SharedPrefs caching for UNATTRIBUTED unique outcome events that simply wipe and start over on enw sessions only * Outcome event clean up (#873) * Outcome event clean up * Removed OutcomeParams class and instead only use a float weight * Fixed all tests that broke after removing OutcomeParams * Cleaned up requests in OutcomeEventsRepository so now there is only 1 of each session (DIRECT, INDIRECT, UNATTRIBUTED) * Reordered expected outcome unit tests because of outcome event changes to JSON creation * Added checking in two places for weight of 0 being sent with an OutcomeEvent, one is when we call the public method (logs an error) and the other is in the toJSON of the OutcomeEvent preventing a weight of 0 from being added into the JSON * Minor clean up after PR * Change equals check on weight to Float in OutcomeEvent equals override * Add '||' check for valid outcome name and value instead of '&&' * Updated example app for new outcomes callback logic * Made CachedUniqueOutcomeNotification package-private * Fixed all unit tests by adding CachedUniqueOutcomeNotification to OneSignalPackagePrivateHelper * Made OutcomeEvent toJsonObject public for access in wrappers * Removed TODO for clean up in OutcomeEventsRepository
1 parent 317107c commit 5099bcb

25 files changed

+615
-649
lines changed

OneSignalSDK/app/build.gradle

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ apply plugin: 'com.android.application'
88
//apply plugin: com.onesignal.androidsdk.GradleProjectPlugin
99

1010
android {
11-
compileSdkVersion 27
12-
buildToolsVersion '27.0.3'
11+
compileSdkVersion 28
12+
buildToolsVersion '28.0.3'
1313
defaultConfig {
1414
applicationId "com.onesignal.example"
1515
manifestPlaceholders = [onesignal_app_id: "b2f7f966-d8cc-11e4-bed1-df8f05be55ba",
1616
// Project number pulled from dashboard, local value is ignored.
1717
onesignal_google_project_number: "REMOTE"]
1818
minSdkVersion 15
19-
targetSdkVersion 27
19+
targetSdkVersion 28
2020
versionCode 1
2121
versionName "1.0"
2222
}
@@ -39,7 +39,7 @@ repositories {
3939

4040
dependencies {
4141
implementation fileTree(include: ['*.jar'], dir: 'libs')
42-
implementation 'com.android.support:appcompat-v7:27.1.0'
42+
implementation 'com.android.support:appcompat-v7:28.0.0'
4343

4444
// Use for SDK Development
4545
implementation(project(':onesignal')) {
@@ -64,10 +64,10 @@ dependencies {
6464
// Old Instructions - Use for released SDK
6565
// compile 'com.onesignal:OneSignal:3.+@aar'
6666

67-
implementation 'com.google.android.gms:play-services-location:12.0.0'
67+
implementation 'com.google.android.gms:play-services-location:16.0.0'
6868

6969
// For Chrome tabs
70-
implementation 'com.android.support:customtabs:27.1.0'
70+
implementation 'com.android.support:customtabs:28.0.0'
7171
}
7272

7373
//apply plugin: 'com.google.gms.google-services'

OneSignalSDK/app/src/main/java/com/onesignal/MainActivity.java

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -330,27 +330,17 @@ public void onFailure(OneSignal.EmailUpdateError error) {
330330
public void onSendOutcomeClicked(View view) {
331331
OneSignal.sendOutcome(outcomeName.getText().toString(), new OneSignal.OutcomeCallback() {
332332
@Override
333-
public void onOutcomeSuccess(String name) {
334-
updateTextView(name + " Outcome sent successfully");
335-
}
336-
337-
@Override
338-
public void onOutcomeFail(int statusCode, String response) {
339-
updateTextView("Outcome fail with status code: " + statusCode);
333+
public void onSuccess(OutcomeEvent outcomeEvent) {
334+
updateTextView(outcomeEvent.toString());
340335
}
341336
});
342337
}
343338

344339
public void onSendUniqueOutcomeClicked(View view) {
345340
OneSignal.sendUniqueOutcome(outcomeUnique.getText().toString(), new OneSignal.OutcomeCallback() {
346341
@Override
347-
public void onOutcomeSuccess(String name) {
348-
updateTextView(name + " Unique Outcome sent successfully");
349-
}
350-
351-
@Override
352-
public void onOutcomeFail(int statusCode, String response) {
353-
updateTextView("Unique Outcome fail with status code: " + statusCode);
342+
public void onSuccess(OutcomeEvent outcomeEvent) {
343+
updateTextView(outcomeEvent.toString());
354344
}
355345
});
356346
}
@@ -361,13 +351,8 @@ public void onSendOutcomeWithValueClicked(View view) {
361351

362352
OneSignal.sendOutcomeWithValue(outcomeValueName.getText().toString(), Float.parseFloat(outcomeValue.getText().toString()), new OneSignal.OutcomeCallback() {
363353
@Override
364-
public void onOutcomeSuccess(String name) {
365-
updateTextView(name + " Outcome sent with value successfully");
366-
}
367-
368-
@Override
369-
public void onOutcomeFail(int statusCode, String response) {
370-
updateTextView("Outcome with value fail with status code: " + statusCode);
354+
public void onSuccess(OutcomeEvent outcomeEvent) {
355+
updateTextView(outcomeEvent.toString());
371356
}
372357
});
373358
}

OneSignalSDK/app/src/main/res/layout/activity_main.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@
243243
<TextView
244244
android:id="@+id/debugTextView"
245245
android:layout_width="match_parent"
246-
android:layout_height="80dp" />
246+
android:layout_height="wrap_content" />
247247

248248
</LinearLayout>
249249

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.onesignal;
2+
3+
class CachedUniqueOutcomeNotification {
4+
5+
private String notificationId;
6+
private String name;
7+
8+
CachedUniqueOutcomeNotification(String notificationId, String name) {
9+
this.notificationId = notificationId;
10+
this.name = name;
11+
}
12+
13+
String getNotificationId() {
14+
return notificationId;
15+
}
16+
17+
String getName() {
18+
return name;
19+
}
20+
21+
}

OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationBundleProcessor.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,6 @@ private static void saveNotification(NotificationGenerationJob notifiJob, boolea
178178

179179
writableDb.beginTransaction();
180180

181-
deleteOldNotifications(writableDb);
182-
183181
// Count any notifications with duplicated android notification ids as dismissed.
184182
// -1 is used to note never displayed
185183
if (notifiJob.isNotificationToDisplay()) {
@@ -272,13 +270,6 @@ static void markRestoredNotificationAsDismissed(NotificationGenerationJob notifi
272270
}
273271
}
274272

275-
// Clean up old records after 1 week.
276-
static void deleteOldNotifications(SQLiteDatabase writableDb) {
277-
writableDb.delete(NotificationTable.TABLE_NAME,
278-
NotificationTable.COLUMN_NAME_CREATED_TIME + " < " + ((System.currentTimeMillis() / 1_000L) - 604_800L),
279-
null);
280-
}
281-
282273
static @NonNull JSONObject bundleAsJSONObject(Bundle bundle) {
283274
JSONObject json = new JSONObject();
284275
Set<String> keys = bundle.keySet();

OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationRestorer.java

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -122,35 +122,13 @@ public static void restore(Context context) {
122122
OneSignal.Log(OneSignal.LOG_LEVEL.INFO, "Restoring notifications");
123123

124124
OneSignalDbHelper dbHelper = OneSignalDbHelper.getInstance(context);
125-
deleteOldNotificationsFromDb(dbHelper);
126125

127126
StringBuilder dbQuerySelection = OneSignalDbHelper.recentUninteractedWithNotificationsWhere();
128127
skipVisibleNotifications(context, dbQuerySelection);
129128

130129
queryAndRestoreNotificationsAndBadgeCount(context, dbHelper, dbQuerySelection);
131130
}
132131

133-
private static void deleteOldNotificationsFromDb(OneSignalDbHelper dbHelper) {
134-
SQLiteDatabase writableDb = null;
135-
136-
try {
137-
writableDb = dbHelper.getWritableDbWithRetries();
138-
writableDb.beginTransaction();
139-
NotificationBundleProcessor.deleteOldNotifications(writableDb);
140-
writableDb.setTransactionSuccessful();
141-
} catch (Throwable t) {
142-
OneSignal.Log(OneSignal.LOG_LEVEL.ERROR, "Error deleting old notification records! ", t);
143-
} finally {
144-
if (writableDb != null) {
145-
try {
146-
writableDb.endTransaction(); // May throw if transaction was never opened or DB is full.
147-
} catch (Throwable t) {
148-
OneSignal.Log(OneSignal.LOG_LEVEL.ERROR, "Error closing transaction! ", t);
149-
}
150-
}
151-
}
152-
}
153-
154132
private static void queryAndRestoreNotificationsAndBadgeCount(
155133
Context context,
156134
OneSignalDbHelper dbHelper,

OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java

Lines changed: 28 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -595,10 +595,12 @@ public static void setAppContext(@NonNull Context context) {
595595

596596
if (wasAppContextNull) {
597597
sessionManager = new OSSessionManager(getNewSessionListener());
598-
outcomeEventsController = new OutcomeEventsController(sessionManager, OneSignalDbHelper.getInstance(appContext), outcomeSettings);
598+
outcomeEventsController = new OutcomeEventsController(sessionManager, OneSignalDbHelper.getInstance(appContext));
599599
// Prefs require a context to save
600600
// If the previous state of appContext was null, kick off write in-case it was waiting
601601
OneSignalPrefs.startDelayedWrite();
602+
// Cleans out old cached data to prevent over using the storage on devices
603+
OneSignalCacheCleaner.cleanOldCachedData(context);
602604
}
603605
}
604606

@@ -3077,13 +3079,6 @@ static void fireEmailUpdateFailure() {
30773079
/*
30783080
* Start OneSignalOutcome module
30793081
*/
3080-
private static OutcomeSettings outcomeSettings = null;
3081-
3082-
static void changeOutcomeSettings(OutcomeSettings settings) {
3083-
outcomeSettings = settings;
3084-
outcomeEventsController.setOutcomeSettings(settings);
3085-
}
3086-
30873082
static OSSessionManager getSessionManager() {
30883083
return sessionManager;
30893084
}
@@ -3096,6 +3091,11 @@ public static void sendOutcome(@NonNull String name, OutcomeCallback callback) {
30963091
if (!isValidOutcomeEntry(name))
30973092
return;
30983093

3094+
if (outcomeEventsController == null) {
3095+
OneSignal.Log(LOG_LEVEL.ERROR, "Make sure OneSignal.init is called first");
3096+
return;
3097+
}
3098+
30993099
outcomeEventsController.sendOutcomeEvent(name, callback);
31003100
}
31013101

@@ -3107,6 +3107,11 @@ public static void sendUniqueOutcome(@NonNull String name, OutcomeCallback callb
31073107
if (!isValidOutcomeEntry(name))
31083108
return;
31093109

3110+
if (outcomeEventsController == null) {
3111+
OneSignal.Log(LOG_LEVEL.ERROR, "Make sure OneSignal.init is called first");
3112+
return;
3113+
}
3114+
31103115
outcomeEventsController.sendUniqueOutcomeEvent(name, callback);
31113116
}
31123117

@@ -3115,18 +3120,27 @@ public static void sendOutcomeWithValue(@NonNull String name, float value) {
31153120
}
31163121

31173122
public static void sendOutcomeWithValue(@NonNull String name, float value, OutcomeCallback callback) {
3118-
if (!isValidOutcomeEntry(name))
3123+
if (!isValidOutcomeEntry(name) || !isValidOutcomeValue(value))
3124+
return;
3125+
3126+
if (outcomeEventsController == null) {
3127+
OneSignal.Log(LOG_LEVEL.ERROR, "Make sure OneSignal.init is called first");
31193128
return;
3129+
}
31203130

31213131
outcomeEventsController.sendOutcomeEventWithValue(name, value, callback);
31223132
}
31233133

3124-
private static boolean isValidOutcomeEntry(String name) {
3125-
if (outcomeEventsController == null) {
3126-
OneSignal.Log(LOG_LEVEL.ERROR, "Make sure OneSignal.init is called first");
3127-
return false;
3134+
private static boolean isValidOutcomeValue(float value) {
3135+
if (value <= 0) {
3136+
OneSignal.Log(LOG_LEVEL.ERROR, "Outcome value must be greater than 0");
3137+
return false;
31283138
}
31293139

3140+
return true;
3141+
}
3142+
3143+
private static boolean isValidOutcomeEntry(String name) {
31303144
if (name == null || name.isEmpty()) {
31313145
OneSignal.Log(LOG_LEVEL.ERROR, "Outcome name must not be empty");
31323146
return false;
@@ -3136,41 +3150,7 @@ private static boolean isValidOutcomeEntry(String name) {
31363150
}
31373151

31383152
public interface OutcomeCallback {
3139-
void onOutcomeSuccess(String name);
3140-
void onOutcomeFail(int statusCode, String response);
3141-
}
3142-
3143-
static class OutcomeSettings {
3144-
private boolean cacheActive;
3145-
3146-
OutcomeSettings(Builder builder) {
3147-
this.cacheActive = builder.cacheActive;
3148-
}
3149-
3150-
boolean isCacheActive() {
3151-
return cacheActive;
3152-
}
3153-
3154-
public static class Builder {
3155-
3156-
private boolean cacheActive = true;
3157-
3158-
public static Builder newInstance() {
3159-
return new Builder();
3160-
}
3161-
3162-
private Builder() {
3163-
}
3164-
3165-
public Builder setCacheActive(boolean active) {
3166-
this.cacheActive = active;
3167-
return this;
3168-
}
3169-
3170-
public OutcomeSettings build() {
3171-
return new OutcomeSettings(this);
3172-
}
3173-
}
3153+
void onSuccess(OutcomeEvent outcomeEvent);
31743154
}
31753155
/*
31763156
* End OneSignalOutcome module
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.onesignal;
2+
3+
import com.onesignal.OneSignalDbContract.NotificationTable;
4+
import com.onesignal.OneSignalDbContract.CachedUniqueOutcomeNotificationTable;
5+
6+
import android.content.Context;
7+
import android.database.sqlite.SQLiteDatabase;
8+
import android.os.Process;
9+
10+
class OneSignalCacheCleaner {
11+
12+
private static String OS_DELETE_OLD_CACHED_DATA = "OS_DELETE_OLD_CACHED_DATA";
13+
14+
/**
15+
* We clean outdated cache from several places within the OneSignal SDK here
16+
* 1. In App Messaging id sets (impressions, clicks, views)
17+
* 2. Notifications after 1 week
18+
* 3. Unique outcome events linked to notification ids (1 week)
19+
*/
20+
synchronized static void cleanOldCachedData(final Context context) {
21+
new Thread(new Runnable() {
22+
@Override
23+
public void run() {
24+
Thread.currentThread().setPriority(Process.THREAD_PRIORITY_BACKGROUND);
25+
OneSignalDbHelper dbHelper = OneSignalDbHelper.getInstance(context);
26+
SQLiteDatabase writableDb = dbHelper.getWritableDbWithRetries();
27+
28+
cleanInAppMessagingCache();
29+
cleanNotificationCache(writableDb);
30+
}
31+
}, OS_DELETE_OLD_CACHED_DATA).start();
32+
}
33+
34+
/**
35+
* TODO: Needs to be implemented to clean out old IAM data used to track impressions, clicks, and viewed IAMs
36+
*/
37+
static void cleanInAppMessagingCache() {
38+
// NOTE: Currently IAMs will pile up overtime and since IAMs can be modified, active, inactive, etc.
39+
// we never truly know when it is the correct time to remove these ids form our cache
40+
}
41+
42+
/**
43+
* Cleans two notification tables
44+
* 1. NotificationTable.TABLE_NAME
45+
* 2. CachedUniqueOutcomeNotificationTable.TABLE_NAME
46+
*/
47+
static void cleanNotificationCache(SQLiteDatabase writableDb) {
48+
cleanOldNotificationData(writableDb);
49+
cleanOldUniqueOutcomeEventNotificationsCache(writableDb);
50+
}
51+
52+
/**
53+
* Deletes any notifications with created timestamps older than 7 days
54+
*/
55+
private static void cleanOldNotificationData(SQLiteDatabase writableDb) {
56+
writableDb.delete(NotificationTable.TABLE_NAME,
57+
NotificationTable.COLUMN_NAME_CREATED_TIME + " < " + ((System.currentTimeMillis() / 1_000L) - 604_800L),
58+
null);
59+
}
60+
61+
/**
62+
* Deletes any notifications whose ids do not exist inside of the NotificationTable.TABLE_NAME
63+
*/
64+
static void cleanOldUniqueOutcomeEventNotificationsCache(SQLiteDatabase writableDb) {
65+
writableDb.delete(CachedUniqueOutcomeNotificationTable.TABLE_NAME,
66+
"NOT EXISTS(SELECT NULL FROM " + NotificationTable.TABLE_NAME +
67+
" n WHERE" +
68+
" n." + NotificationTable.COLUMN_NAME_NOTIFICATION_ID + " = " + CachedUniqueOutcomeNotificationTable.COLUMN_NAME_NOTIFICATION_ID + ")",
69+
null);
70+
}
71+
72+
}

0 commit comments

Comments
 (0)