Add support for scheduled message sends.

This commit is contained in:
Clark
2023-01-26 10:37:08 -05:00
committed by Greyson Parrelli
parent df695f7611
commit f3e715e069
59 changed files with 1948 additions and 90 deletions

View File

@@ -0,0 +1,78 @@
package org.thoughtcrime.securesms.service
import android.app.Application
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import androidx.annotation.WorkerThread
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.IndividualSendJob
import org.thoughtcrime.securesms.jobs.PushGroupSendJob
/**
* Manages waking up and sending scheduled messages at the correct time
*/
class ScheduledMessageManager(
val application: Application
) : TimedEventManager<ScheduledMessageManager.Event>(application, "ScheduledMessagesManager") {
companion object {
private val TAG = Log.tag(ScheduledMessageManager::class.java)
}
private val messagesTable = SignalDatabase.messages
init {
scheduleIfNecessary()
}
@WorkerThread
override fun getNextClosestEvent(): Event? {
val oldestTimestamp = messagesTable.oldestScheduledSendTimestamp ?: return null
val delay = (oldestTimestamp - System.currentTimeMillis()).coerceAtLeast(0)
Log.i(TAG, "The next scheduled message needs to be sent in $delay ms.")
return Event(delay)
}
@WorkerThread
override fun executeEvent(event: Event) {
val scheduledMessagesToSend = messagesTable.getScheduledMessagesBefore(System.currentTimeMillis())
for (record in scheduledMessagesToSend) {
if (SignalDatabase.messages.clearScheduledStatus(record.threadId, record.id)) {
if (record.recipient.isPushGroup) {
PushGroupSendJob.enqueue(application, ApplicationDependencies.getJobManager(), record.id, record.recipient.id, emptySet())
} else {
IndividualSendJob.enqueue(application, ApplicationDependencies.getJobManager(), record.id, record.recipient)
}
} else {
Log.i(TAG, "messageId=${record.id} was not a scheduled message, ignoring")
}
}
}
@WorkerThread
override fun getDelayForEvent(event: Event): Long = event.delay
@WorkerThread
override fun scheduleAlarm(application: Application, delay: Long) {
trySetExactAlarm(application, System.currentTimeMillis() + delay, ScheduledMessagesAlarm::class.java)
}
data class Event(val delay: Long)
class ScheduledMessagesAlarm : BroadcastReceiver() {
companion object {
private val TAG = Log.tag(ScheduledMessagesAlarm::class.java)
}
override fun onReceive(context: Context?, intent: Intent?) {
Log.d(TAG, "onReceive()")
ApplicationDependencies.getScheduledMessageManager().scheduleIfNecessary()
}
}
}

View File

@@ -5,6 +5,7 @@ import android.app.Application;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
@@ -14,6 +15,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import org.signal.core.util.PendingIntentFlags;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.util.ServiceUtil;
/**
@@ -21,6 +23,8 @@ import org.thoughtcrime.securesms.util.ServiceUtil;
*/
public abstract class TimedEventManager<E> {
private static final String TAG = Log.tag(TimedEventManager.class);
private final Application application;
private final Handler handler;
@@ -91,4 +95,29 @@ public abstract class TimedEventManager<E> {
alarmManager.cancel(pendingIntent);
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + delay, pendingIntent);
}
protected static void trySetExactAlarm(@NonNull Context context, long timestamp, @NonNull Class alarmClass) {
Intent intent = new Intent(context, alarmClass);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntentFlags.mutable());
AlarmManager alarmManager = ServiceUtil.getAlarmManager(context);
alarmManager.cancel(pendingIntent);
boolean hasManagerPermission = Build.VERSION.SDK_INT < 31 || alarmManager.canScheduleExactAlarms();
if (hasManagerPermission) {
try {
if (Build.VERSION.SDK_INT >= 23) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timestamp, pendingIntent);
} else {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, timestamp, pendingIntent);
}
return;
} catch (Exception e) {
Log.w(TAG, e);
}
}
Log.w(TAG, "Unable to schedule exact alarm, falling back to inexact alarm, scheduling alarm for: " + timestamp);
alarmManager.set(AlarmManager.RTC_WAKEUP, timestamp, pendingIntent);
}
}