diff --git a/android-core/src/main/java/com/mparticle/internal/AppStateManager.java b/android-core/src/main/java/com/mparticle/internal/AppStateManager.java index e51526c49..be94590d0 100644 --- a/android-core/src/main/java/com/mparticle/internal/AppStateManager.java +++ b/android-core/src/main/java/com/mparticle/internal/AppStateManager.java @@ -17,6 +17,7 @@ import com.mparticle.JobSchedulerUtilsKt; import com.mparticle.MPEvent; import com.mparticle.MParticle; +import com.mparticle.SchedulingBatchingType; import com.mparticle.identity.IdentityApi; import com.mparticle.identity.IdentityApiRequest; import com.mparticle.identity.MParticleUser; @@ -377,13 +378,11 @@ private void logBackgrounded() { } private void scheduleBackgroundJob() { - if (mConfigManager.isBackgroundEventBatchingEnabled()) { - JobSchedulerUtilsKt.scheduleBatchUploading(this.mContext, delay -> { - mMessageManager.mUploadHandler.sendMessageDelayed(mMessageManager.mUploadHandler.obtainMessage(UploadHandler.UPLOAD_TRIGGER_MESSAGES, 1, 0, mConfigManager.getMpid()), delay); - Logger.debug("Legacy action with delay: " + delay); - return Unit.INSTANCE; - }); - } + JobSchedulerUtilsKt.scheduleBatchUploading(this.mContext, mConfigManager.getUploadInterval(), SchedulingBatchingType.ONE_SHOOT, true, delay -> { + mMessageManager.mUploadHandler.sendMessageDelayed(mMessageManager.mUploadHandler.obtainMessage(UploadHandler.UPLOAD_TRIGGER_MESSAGES, 1, 0, mConfigManager.getMpid()), delay); + Logger.debug("Legacy action with delay: " + delay); + return Unit.INSTANCE; + }); } @TargetApi(14) diff --git a/android-core/src/main/java/com/mparticle/internal/ConfigManager.java b/android-core/src/main/java/com/mparticle/internal/ConfigManager.java index fbc78b87f..74864db12 100644 --- a/android-core/src/main/java/com/mparticle/internal/ConfigManager.java +++ b/android-core/src/main/java/com/mparticle/internal/ConfigManager.java @@ -91,7 +91,6 @@ public class ConfigManager { private JSONObject mProviderPersistence; private int mRampValue = -1; private int mUserBucket = -1; - private boolean isBackgroundEventBatchingEnabled = false; private int mSessionTimeoutInterval = -1; private int mUploadInterval = -1; @@ -231,10 +230,6 @@ public void deleteUserStorage(long mpId) { deleteUserStorage(mContext, mpId); } - public boolean isBackgroundEventBatchingEnabled() { - return isBackgroundEventBatchingEnabled; - } - static void deleteConfigManager(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { context.deleteSharedPreferences(PREFERENCES_FILE); @@ -413,8 +408,6 @@ private synchronized void updateCoreConfig(JSONObject responseJSON, boolean newC } mRampValue = responseJSON.optInt(KEY_RAMP, -1); - isBackgroundEventBatchingEnabled = responseJSON.optBoolean(ENABLE_BACKGROUND_BATCHING, false); - if (responseJSON.has(KEY_OPT_OUT)) { mSendOoEvents = responseJSON.getBoolean(KEY_OPT_OUT); } else { @@ -929,11 +922,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(isBackgroundEventBatchingEnabled && isBackgroundAst){ + if (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); if (!shouldTrigger && messageMatches != null && messageMatches.length() > 0) { shouldTrigger = true; diff --git a/android-core/src/main/java/com/mparticle/internal/MessageManager.java b/android-core/src/main/java/com/mparticle/internal/MessageManager.java index f71d514bc..8824a7e2e 100644 --- a/android-core/src/main/java/com/mparticle/internal/MessageManager.java +++ b/android-core/src/main/java/com/mparticle/internal/MessageManager.java @@ -815,10 +815,8 @@ public void onFailed() { @Override public void endUploadLoop() { - if (!mConfigManager.isBackgroundEventBatchingEnabled()) { - mUploadHandler.removeMessages(UploadHandler.UPLOAD_MESSAGES); - MParticle.getInstance().upload(); - } + mUploadHandler.removeMessages(UploadHandler.UPLOAD_MESSAGES); + MParticle.getInstance().upload(); } @Override diff --git a/android-core/src/main/java/com/mparticle/internal/UploadHandler.java b/android-core/src/main/java/com/mparticle/internal/UploadHandler.java index a7e6aa217..e539dda49 100644 --- a/android-core/src/main/java/com/mparticle/internal/UploadHandler.java +++ b/android-core/src/main/java/com/mparticle/internal/UploadHandler.java @@ -9,7 +9,9 @@ import androidx.annotation.Nullable; +import com.mparticle.JobSchedulerUtilsKt; import com.mparticle.MParticle; +import com.mparticle.SchedulingBatchingType; import com.mparticle.identity.AliasRequest; import com.mparticle.identity.AliasResponse; import com.mparticle.internal.database.services.MParticleDBManager; @@ -25,6 +27,8 @@ import javax.net.ssl.SSLHandshakeException; +import kotlin.Unit; + /** * Primary queue handler which is responsible for querying, packaging, and uploading data. */ @@ -133,10 +137,12 @@ public void handleMessageImpl(Message msg) { break; case UPLOAD_MESSAGES: case UPLOAD_TRIGGER_MESSAGES: + boolean anythingToUpload = false; long uploadInterval = mConfigManager.getUploadInterval(); if (isNetworkConnected) { if (uploadInterval > 0 || msg.arg1 == 1) { while (mParticleDBManager.hasMessagesForUpload()) { + anythingToUpload = true; prepareMessageUploads(false); } boolean needsHistory = upload(false); @@ -145,11 +151,19 @@ public void handleMessageImpl(Message msg) { } } } - - if (!mConfigManager.isBackgroundEventBatchingEnabled()) { - if (mAppStateManager.getSession().isActive() && uploadInterval > 0 && msg.arg1 == 0) { - this.sendEmptyDelayed(UPLOAD_MESSAGES, uploadInterval); - } + if (!anythingToUpload && (!mAppStateManager.getSession().isActive() || mAppStateManager.isBackgrounded())) { + //If there isn't anything to upload and is in background or inactive, cancel schedule job until a new message is stored + JobSchedulerUtilsKt.cancelScheduledUploadBatchJob(mContext); + } + if (mAppStateManager.getSession().isActive() && uploadInterval > 0 && msg.arg1 == 0) { + JobSchedulerUtilsKt.scheduleBatchUploading(mContext, + uploadInterval, + SchedulingBatchingType.PERIODIC, + true, + delay -> { + sendEmptyDelayed(UPLOAD_MESSAGES, uploadInterval); + return Unit.INSTANCE; + }); } break; case UPLOAD_HISTORY: diff --git a/android-core/src/main/java/com/mparticle/media/MPMediaAPI.java b/android-core/src/main/java/com/mparticle/media/MPMediaAPI.java index fdbb0617c..cef2b98bf 100644 --- a/android-core/src/main/java/com/mparticle/media/MPMediaAPI.java +++ b/android-core/src/main/java/com/mparticle/media/MPMediaAPI.java @@ -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) { @@ -45,6 +46,7 @@ public void setAudioPlaying(boolean playing) { } } + @Deprecated public boolean getAudioPlaying() { return mAudioPlaying.get(); } diff --git a/android-core/src/main/kotlin/com.mparticle/JobSchedulerUtils.kt b/android-core/src/main/kotlin/com.mparticle/JobSchedulerUtils.kt index cd8b2d7c1..7bf76531e 100644 --- a/android-core/src/main/kotlin/com.mparticle/JobSchedulerUtils.kt +++ b/android-core/src/main/kotlin/com.mparticle/JobSchedulerUtils.kt @@ -11,33 +11,70 @@ import com.mparticle.uploadbatching.BatchUploadingJob const val UPLOAD_BATCH_JOB = 123 -private fun Int.minutesToMillis(): Long = this * 60000L +fun Long.secondsToMillis(): Long = this * 1000 +fun Long.minutesToMillis(): Long = (this * 60).secondsToMillis() -fun scheduleBatchUploading(context: Context, legacyAction: (delay: Long) -> Unit) { - val delay = 15.minutesToMillis() +enum class SchedulingBatchingType { PERIODIC, ONE_SHOOT; } + +fun cancelScheduledUploadBatchJob(context: Context) { + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + val jobScheduler = context.getJobScheduler() + val jobRunning = context.getScheduledJob(UPLOAD_BATCH_JOB) + jobRunning?.let { jobScheduler?.cancel(UPLOAD_BATCH_JOB) } + } + } catch (e: Exception) { + } +} + +fun scheduleBatchUploading( + context: Context, + delayInSeconds: Long, + type: SchedulingBatchingType, + cancelPrevious: Boolean = true, + legacyAction: (delay: Long) -> Unit +) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - val job = - JobInfo.Builder( + try { + if (cancelPrevious) { + context.getJobScheduler()?.cancel(UPLOAD_BATCH_JOB) + } + val builder = JobInfo.Builder( UPLOAD_BATCH_JOB, ComponentName(context, BatchUploadingJob::class.java.name) ) - .setMinimumLatency(delay) - .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) - .build() - context.scheduleJob(job) + if (type == SchedulingBatchingType.PERIODIC) { + builder.setPeriodic(delayInSeconds.secondsToMillis()) + } else if (type == SchedulingBatchingType.ONE_SHOOT) { + builder.setMinimumLatency(delayInSeconds.secondsToMillis()) + } + builder.apply { + setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + } + context.scheduleJob(builder.build()) + } catch (e: Exception) { + Logger.error("Error while trying to use JobSceduler for batch upload") + legacyAction.invoke(delayInSeconds.secondsToMillis()) + } } else { - legacyAction.invoke(delay) + legacyAction.invoke(delayInSeconds.secondsToMillis()) } } +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +private fun Context.getJobScheduler(): JobScheduler? = + this.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler? + +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +private fun Context.getScheduledJob(jobId: Int): JobInfo? = + this.getJobScheduler()?.allPendingJobs?.firstOrNull { it.id == jobId } @RequiresApi(Build.VERSION_CODES.LOLLIPOP) private fun Context.scheduleJob(job: JobInfo) { - val jobScheduler = this.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler? - val jobRunning = jobScheduler?.allPendingJobs?.firstOrNull { it.id == job.id } != null + val jobRunning = this.getScheduledJob(job.id) != null if (!jobRunning) { //Schedule / Re-schedule the job if its not running/scheduled to run at the system service - jobScheduler?.schedule(job) + this.getJobScheduler()?.schedule(job) } else { Logger.debug("Trying to schedule job in uploadBatch service. Service ALREADY RUNNING") } diff --git a/android-core/src/main/kotlin/com/mparticle/uploadbatching/BatchUploadingJob.kt b/android-core/src/main/kotlin/com/mparticle/uploadbatching/BatchUploadingJob.kt index a4be13e05..3f729b1b7 100644 --- a/android-core/src/main/kotlin/com/mparticle/uploadbatching/BatchUploadingJob.kt +++ b/android-core/src/main/kotlin/com/mparticle/uploadbatching/BatchUploadingJob.kt @@ -20,7 +20,7 @@ class BatchUploadingJob : JobService() { } ?: run { Logger.debug("MParticle instance null while trying to call uploadBatching:upload") } - return false + return true } override fun onStopJob(params: JobParameters?): Boolean = false