mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 17:29:32 +01:00
Add support for scheduled message sends.
This commit is contained in:
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user