That fuck shit the fascists are using
at master 148 lines 5.0 kB view raw
1package org.tm.archive.service; 2 3import android.app.AlarmManager; 4import android.app.Application; 5import android.app.PendingIntent; 6import android.content.BroadcastReceiver; 7import android.content.Context; 8import android.content.Intent; 9import android.os.Build; 10import android.os.Handler; 11import android.os.HandlerThread; 12 13import androidx.annotation.AnyThread; 14import androidx.annotation.NonNull; 15import androidx.annotation.Nullable; 16import androidx.annotation.WorkerThread; 17 18import org.signal.core.util.PendingIntentFlags; 19import org.signal.core.util.ThreadUtil; 20import org.signal.core.util.logging.Log; 21import org.tm.archive.util.ServiceUtil; 22 23/** 24 * Class to help manage scheduling events to happen in the future, whether the app is open or not. 25 */ 26public abstract class TimedEventManager<E> { 27 28 private static final String TAG = Log.tag(TimedEventManager.class); 29 30 private final Application application; 31 private final Handler handler; 32 33 public TimedEventManager(@NonNull Application application, @NonNull String threadName) { 34 HandlerThread handlerThread = new HandlerThread(threadName, ThreadUtil.PRIORITY_BACKGROUND_THREAD); 35 handlerThread.start(); 36 37 this.application = application; 38 this.handler = new Handler(handlerThread.getLooper()); 39 } 40 41 /** 42 * Should be called whenever the underlying data of events has changed. Will appropriately 43 * schedule new event executions. 44 */ 45 public void scheduleIfNecessary() { 46 handler.removeCallbacksAndMessages(null); 47 48 handler.post(() -> { 49 E event = getNextClosestEvent(); 50 51 if (event != null) { 52 long delay = getDelayForEvent(event); 53 54 handler.postDelayed(() -> { 55 executeEvent(event); 56 scheduleIfNecessary(); 57 }, delay); 58 59 scheduleAlarm(application, event, delay); 60 } 61 }); 62 } 63 64 /** 65 * @return The next event that should be executed, or {@code null} if there are no events to execute. 66 */ 67 @WorkerThread 68 protected @Nullable abstract E getNextClosestEvent(); 69 70 /** 71 * Execute the provided event. 72 */ 73 @WorkerThread 74 protected abstract void executeEvent(@NonNull E event); 75 76 /** 77 * @return How long before the provided event should be executed. 78 */ 79 @WorkerThread 80 protected abstract long getDelayForEvent(@NonNull E event); 81 82 /** 83 * Schedules an alarm to call {@link #scheduleIfNecessary()} after the specified delay. You can 84 * use {@link #setAlarm(Context, long, Class)} as a helper method. 85 */ 86 @AnyThread 87 protected abstract void scheduleAlarm(@NonNull Application application, E event, long delay); 88 89 /** 90 * Helper method to set an alarm. 91 */ 92 protected static void setAlarm(@NonNull Context context, long delay, @NonNull Class<? extends BroadcastReceiver> alarmClass) { 93 Intent intent = new Intent(context, alarmClass); 94 PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntentFlags.mutable()); 95 AlarmManager alarmManager = ServiceUtil.getAlarmManager(context); 96 97 alarmManager.cancel(pendingIntent); 98 alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + delay, pendingIntent); 99 } 100 101 protected static void trySetExactAlarm(@NonNull Context context, long timestamp, @NonNull Class<? extends BroadcastReceiver> alarmClass, @NonNull PendingIntent showIntent) { 102 Intent intent = new Intent(context, alarmClass); 103 PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntentFlags.mutable()); 104 AlarmManager alarmManager = ServiceUtil.getAlarmManager(context); 105 106 alarmManager.cancel(pendingIntent); 107 108 boolean hasManagerPermission = Build.VERSION.SDK_INT < 31 || alarmManager.canScheduleExactAlarms(); 109 if (hasManagerPermission) { 110 try { 111 alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(timestamp, showIntent), pendingIntent); 112 return; 113 } catch (Exception e) { 114 Log.w(TAG, e); 115 } 116 } 117 118 Log.w(TAG, "Unable to schedule exact alarm, falling back to inexact alarm, scheduling alarm for: " + timestamp); 119 alarmManager.set(AlarmManager.RTC_WAKEUP, timestamp, pendingIntent); 120 } 121 122 protected static void cancelAlarm(@NonNull Context context, @NonNull Class<? extends BroadcastReceiver> alarmClass) { 123 Intent intent = new Intent(context, alarmClass); 124 PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntentFlags.mutable()); 125 126 try { 127 pendingIntent.cancel(); 128 ServiceUtil.getAlarmManager(context).cancel(pendingIntent); 129 } catch (Exception e) { 130 Throwable cause = e; 131 int depth = 0; 132 while (cause != null && depth < 5) { 133 if (cause instanceof SecurityException) { 134 break; 135 } else { 136 cause = e.getCause(); 137 depth++; 138 } 139 } 140 141 if (e instanceof SecurityException) { 142 Log.i(TAG, "Unable to cancel alarm because we don't have permission"); 143 } else { 144 throw e; 145 } 146 } 147 } 148}