That fuck shit the fascists are using
1package org.tm.archive.service;
2
3import android.os.Handler;
4import android.os.Looper;
5
6import androidx.annotation.NonNull;
7import androidx.annotation.Nullable;
8
9import org.signal.core.util.concurrent.SignalExecutors;
10import org.signal.core.util.logging.Log;
11
12import java.util.Objects;
13
14/**
15 * Represents a delayed foreground notification.
16 * <p>
17 * With this, you can {@link #close()} it to dismiss or prevent it showing if it hasn't already.
18 * <p>
19 * You can also {@link #showNow()} to show if it's not already. This returns a regular {@link NotificationController} which can be updated if required.
20 */
21public abstract class DelayedNotificationController implements AutoCloseable {
22
23 private static final String TAG = Log.tag(DelayedNotificationController.class);
24
25 public static final long SHOW_WITHOUT_DELAY = 0;
26 public static final long DO_NOT_SHOW = -1;
27
28 private DelayedNotificationController() {}
29
30 static DelayedNotificationController create(long delayMillis, @NonNull Create createTask) {
31 if (delayMillis == SHOW_WITHOUT_DELAY) return new Shown(createTask.create());
32 if (delayMillis == DO_NOT_SHOW) return new NoShow();
33 if (delayMillis > 0) return new DelayedShow(delayMillis, createTask);
34
35 throw new IllegalArgumentException("Illegal delay " + delayMillis);
36 }
37
38 /**
39 * Show the foreground notification if it's not already showing.
40 * <p>
41 * If it does show, it returns a regular {@link NotificationController} which you can use to update its message or progress.
42 */
43 public abstract @Nullable NotificationController showNow();
44
45 @Override
46 public void close() {
47 }
48
49 private static final class NoShow extends DelayedNotificationController {
50
51 @Override
52 public @Nullable NotificationController showNow() {
53 return null;
54 }
55 }
56
57 private static final class Shown extends DelayedNotificationController {
58
59 private final NotificationController controller;
60
61 Shown(@NonNull NotificationController controller) {
62 this.controller = controller;
63 }
64
65 @Override
66 public void close() {
67 this.controller.close();
68 }
69
70 @Override
71 public NotificationController showNow() {
72 return controller;
73 }
74 }
75
76 private static final class DelayedShow extends DelayedNotificationController {
77
78 private final Create createTask;
79 private final Handler handler;
80 private final Runnable start;
81 private NotificationController notificationController;
82 private boolean isClosed;
83
84 private DelayedShow(long delayMillis, @NonNull Create createTask) {
85 this.createTask = createTask;
86 this.handler = new Handler(Looper.getMainLooper());
87 this.start = this::start;
88
89 handler.postDelayed(start, delayMillis);
90 }
91
92 private void start() {
93 SignalExecutors.BOUNDED.execute(this::showNowInner);
94 }
95
96 public synchronized @NonNull NotificationController showNow() {
97 if (isClosed) {
98 throw new AssertionError("showNow called after close");
99 }
100 return Objects.requireNonNull(showNowInner());
101 }
102
103 private synchronized @Nullable NotificationController showNowInner() {
104 if (notificationController != null) {
105 return notificationController;
106 }
107
108 if (!isClosed) {
109 Log.i(TAG, "Starting foreground service");
110 notificationController = createTask.create();
111 return notificationController;
112 } else {
113 Log.i(TAG, "Did not start foreground service as close has been called");
114 return null;
115 }
116 }
117
118 @Override
119 public synchronized void close() {
120 handler.removeCallbacks(start);
121 isClosed = true;
122 if (notificationController != null) {
123 Log.d(TAG, "Closing");
124 notificationController.close();
125 } else {
126 Log.d(TAG, "Never showed");
127 }
128 }
129 }
130
131 public interface Create {
132 @NonNull NotificationController create();
133 }
134}