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

feat: SQDSDKS-5924 - Batching events with delay after app goes backgrounded #461

Open
wants to merge 9 commits into
base: development
Choose a base branch
from
1 change: 1 addition & 0 deletions android-core/proguard.pro
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
-keep class com.mparticle.MParticle$UserAttributes { *; }
-keep class com.mparticle.MParticle$ResetListener { *; }
-keep class com.mparticle.MParticle$OperatingSystem { *; }
-keep class com.mparticle.uploadbatching.UploadBatchReceiver


-keep class com.mparticle.Session { *; }
Expand Down
11 changes: 11 additions & 0 deletions android-core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.INTERNET" />

<application>
<receiver
android:name="com.mparticle.uploadbatching.UploadBatchReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="ACTION_UPLOAD_BATCH"/>
</intent-filter>
</receiver>
</application>
</manifest>
9 changes: 9 additions & 0 deletions android-core/src/main/java/com/mparticle/MParticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.location.Location;
import android.location.LocationManager;
Expand Down Expand Up @@ -51,6 +52,7 @@
import com.mparticle.messaging.MPMessagingAPI;
import com.mparticle.messaging.ProviderCloudMessage;
import com.mparticle.segmentation.SegmentListener;
import com.mparticle.uploadbatching.UploadBatchReceiver;

import org.jetbrains.annotations.NotNull;
import org.json.JSONObject;
Expand Down Expand Up @@ -136,6 +138,13 @@ private MParticle(MParticleOptions options) {
mMessageManager = new MessageManager(configManager, appStateManager, mKitManager, sDevicePerformanceMetricsDisabled, mDatabaseManager, options);
mConfigManager.setNetworkOptions(options.getNetworkOptions());
mPreferences = options.getContext().getSharedPreferences(Constants.PREFS_FILE, Context.MODE_PRIVATE);
UploadBatchReceiver receiver = new UploadBatchReceiver();
IntentFilter intentFilter = UploadBatchReceiver.Companion.getIntentFilter();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mAppContext.registerReceiver(receiver, intentFilter, Context.RECEIVER_EXPORTED);
} else {
mAppContext.registerReceiver(receiver, intentFilter);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.mparticle.identity.IdentityApiRequest;
import com.mparticle.identity.MParticleUser;
import com.mparticle.internal.listeners.InternalListenerManager;
import com.mparticle.uploadbatching.AlarmSchedulingUtilsKt;

import org.json.JSONObject;

Expand Down Expand Up @@ -364,6 +365,9 @@ public void onActivityStopped(Activity activity) {
private void logBackgrounded() {
MParticle instance = MParticle.getInstance();
if (instance != null) {
if (mConfigManager.isBackgroundBatchUploadingEnabled()) {
AlarmSchedulingUtilsKt.scheduleUploadBatchAlarm(mContext, mConfigManager.getUploadInterval());
}
logStateTransition(Constants.StateTransitionType.STATE_TRANS_BG, mCurrentActivityName);
instance.Internal().getKitManager().onApplicationBackground();
mCurrentActivityName = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public class ConfigManager {
public static final String VALUE_CUE_CATCH = "forcecatch";
public static final String PREFERENCES_FILE = "mp_preferences";
public static final String KEY_INCLUDE_SESSION_HISTORY = "inhd";
public static final String ENABLE_BACKGROUND_BATCHING = "ebb";
private static final String KEY_DEVICE_PERFORMANCE_METRICS_DISABLED = "dpmd";
public static final String WORKSPACE_TOKEN = "wst";
static final String ALIAS_MAX_WINDOW = "alias_max_window";
Expand Down Expand Up @@ -107,6 +108,7 @@ public class ConfigManager {
public static final int DEFAULT_UPLOAD_INTERVAL = 600;
private List<ConfigLoadedListener> configUpdatedListeners = new ArrayList<>();
private List<SideloadedKit> sideloadedKits = new ArrayList<>();
private boolean enableBackgroundBatchingUpload = false;

private ConfigManager() {
super();
Expand Down Expand Up @@ -401,13 +403,15 @@ private synchronized void updateCoreConfig(JSONObject responseJSON, boolean newC
mLogUnhandledExceptions = responseJSON.getString(KEY_UNHANDLED_EXCEPTIONS);
}

//TODO Read from backgroundEventBatching feature flag
editor.putBoolean(ENABLE_BACKGROUND_BATCHING, enableBackgroundBatchingUpload);

if (responseJSON.has(KEY_PUSH_MESSAGES) && newConfig) {
sPushKeys = responseJSON.getJSONArray(KEY_PUSH_MESSAGES);
editor.putString(KEY_PUSH_MESSAGES, sPushKeys.toString());
}

mRampValue = responseJSON.optInt(KEY_RAMP, -1);

if (responseJSON.has(KEY_OPT_OUT)) {
mSendOoEvents = responseJSON.getBoolean(KEY_OPT_OUT);
} else {
Expand Down Expand Up @@ -913,6 +917,10 @@ public JSONArray getTriggerMessageHashes() {
return mTriggerMessageHashes;
}

public boolean isBackgroundBatchUploadingEnabled() {
return enableBackgroundBatchingUpload;
}

public boolean shouldTrigger(BaseMPMessage message) {
JSONArray messageMatches = getTriggerMessageMatches();
JSONArray triggerHashes = getTriggerMessageHashes();
Expand All @@ -922,9 +930,11 @@ public boolean shouldTrigger(BaseMPMessage message) {
isBackgroundAst = (message.getMessageType().equals(Constants.MessageType.APP_STATE_TRANSITION) && message.get(Constants.MessageKey.STATE_TRANSITION_TYPE).equals(Constants.StateTransitionType.STATE_TRANS_BG));
} catch (JSONException ex) {
}
if (enableBackgroundBatchingUpload && isBackgroundAst) {
return false;
}
boolean shouldTrigger = message.getMessageType().equals(Constants.MessageType.PUSH_RECEIVED)
|| message.getMessageType().equals(Constants.MessageType.COMMERCE_EVENT)
|| isBackgroundAst;
|| message.getMessageType().equals(Constants.MessageType.COMMERCE_EVENT) || isBackgroundAst;

if (!shouldTrigger && messageMatches != null && messageMatches.length() > 0) {
shouldTrigger = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ public void handleMessageImpl(Message msg) {
}
}
}
if (mAppStateManager.getSession().isActive() && uploadInterval > 0 && msg.arg1 == 0) {
if ((mAppStateManager.getSession().isActive() && uploadInterval > 0 && msg.arg1 == 0) ||
(mParticleDBManager.hasMessagesForUpload() && mAppStateManager.isBackgrounded())) {
this.sendEmptyDelayed(UPLOAD_MESSAGES, uploadInterval);
}
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public MPMediaAPI(@Nullable Context context, @NonNull MediaCallbacks callbacks)
*
* @param playing Is your app currently playing music for the user.
*/
@Deprecated
public void setAudioPlaying(boolean playing) {
mAudioPlaying.set(playing);
if (playing) {
Expand All @@ -45,6 +46,7 @@ public void setAudioPlaying(boolean playing) {
}
}

@Deprecated
public boolean getAudioPlaying() {
return mAudioPlaying.get();
}
Expand Down
8 changes: 8 additions & 0 deletions android-core/src/main/kotlin/com.mparticle/TimeUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.mparticle

import java.text.SimpleDateFormat
import java.util.Date

fun millisToLoggingDate(millis: Long): String {
return SimpleDateFormat("MM/dd/yyyy HH:mm:ss").format(Date(millis))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.mparticle.uploadbatching

import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import com.mparticle.internal.Logger
import com.mparticle.millisToLoggingDate

fun scheduleUploadBatchAlarm(context: Context, delay: Long) {
val intent = Intent(context, UploadBatchReceiver::class.java).apply {
action = UploadBatchReceiver.ACTION_UPLOAD_BATCH
}
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
var alarmDelay = delay
//Setting alarm delay to 2min MINIMUM to prevent collision with end session message triggers
if (delay < 120000) {
alarmDelay = 120000L
}
val time = System.currentTimeMillis() + alarmDelay
val alarmType = AlarmManager.RTC_WAKEUP
(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager?)?.let { manager ->

PendingIntent.getService(
context,
0,
intent,
PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE
)?.let {
manager.cancel(it)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
manager.setAndAllowWhileIdle(alarmType, time, pendingIntent)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
manager.setAndAllowWhileIdle(alarmType, time, pendingIntent)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
manager.set(alarmType, time, pendingIntent)
} else {
manager.set(alarmType, time, pendingIntent)
}
}
Logger.debug("Upload batch alarm set at ${millisToLoggingDate(time)}")
}



Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.mparticle.uploadbatching

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import com.mparticle.MParticle
import com.mparticle.internal.Logger

internal class UploadBatchReceiver : BroadcastReceiver() {

companion object {
const val ACTION_UPLOAD_BATCH = "ACTION_UPLOAD_BATCH"
fun getIntentFilter(): IntentFilter {
return IntentFilter().apply { addAction(ACTION_UPLOAD_BATCH) }
}
}

override fun onReceive(context: Context?, intent: Intent?) {
Logger.debug("Received broadcast ${this::class.java.name}")
intent?.let {
if (it.action == ACTION_UPLOAD_BATCH) {
try {
MParticle.getInstance()?.let {
//Do if there is a non-null mParticle instance, force upload messages
it.upload()
Logger.debug("Uploading events in upload batch receiver")
}
} catch (e: Exception) {
}
}
}
}
}
Loading