Skip to content
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) {
}
}
}
}
}