That fuck shit the fascists are using
1package org.tm.archive.util;
2
3import android.app.Notification;
4import android.app.NotificationChannel;
5import android.app.NotificationManager;
6import android.content.Context;
7import android.os.Build;
8import android.service.notification.StatusBarNotification;
9
10import androidx.annotation.NonNull;
11import androidx.annotation.Nullable;
12import androidx.annotation.RequiresApi;
13import androidx.annotation.WorkerThread;
14
15import com.annimon.stream.Stream;
16
17import org.signal.core.util.concurrent.SignalExecutors;
18import org.signal.core.util.logging.Log;
19import org.tm.archive.dependencies.ApplicationDependencies;
20import org.tm.archive.keyvalue.SignalStore;
21import org.tm.archive.notifications.NotificationIds;
22import org.tm.archive.notifications.v2.NotificationFactory;
23import org.tm.archive.notifications.v2.ConversationId;
24import org.tm.archive.preferences.widgets.NotificationPrivacyPreference;
25import org.tm.archive.recipients.Recipient;
26import org.tm.archive.recipients.RecipientId;
27
28import static org.tm.archive.util.ConversationUtil.CONVERSATION_SUPPORT_VERSION;
29
30/**
31 * Bubble-related utility methods.
32 */
33public final class BubbleUtil {
34
35 private static final String TAG = Log.tag(BubbleUtil.class);
36 private static String currentState = "";
37
38 private BubbleUtil() {
39 }
40
41 /**
42 * Checks whether we are allowed to create a bubble for the given recipient.
43 *
44 * In order to Bubble, a recipient must have a thread, be unblocked, and the user must not have
45 * notification privacy settings enabled. Furthermore, we check the Notifications system to verify
46 * that bubbles are allowed in the first place.
47 */
48 @RequiresApi(CONVERSATION_SUPPORT_VERSION)
49 @WorkerThread
50 public static boolean canBubble(@NonNull Context context, @NonNull RecipientId recipientId, @Nullable Long threadId) {
51 Recipient recipient = Recipient.resolved(recipientId);
52 return canBubble(context, recipient, threadId);
53 }
54
55 @RequiresApi(CONVERSATION_SUPPORT_VERSION)
56 public static boolean canBubble(@NonNull Context context, @NonNull Recipient recipient, @Nullable Long threadId) {
57 if (threadId == null) {
58 Log.d(TAG, "Cannot bubble recipient without thread");
59 return false;
60 }
61
62 NotificationPrivacyPreference privacyPreference = SignalStore.settings().getMessageNotificationsPrivacy();
63 if (!privacyPreference.isDisplayContact()) {
64 Log.d(TAG, "Bubbles are not available when notification privacy settings are enabled.");
65 return false;
66 }
67
68 if (recipient.isBlocked()) {
69 Log.d(TAG, "Cannot bubble blocked recipient");
70 return false;
71 }
72
73 NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
74 NotificationChannel conversationChannel = notificationManager.getNotificationChannel(ConversationUtil.getChannelId(context, recipient),
75 ConversationUtil.getShortcutId(recipient.getId()));
76
77 final StringBuilder bubbleLoggingMessage = new StringBuilder("Bubble State:");
78 if (Build.VERSION.SDK_INT < 31) {
79 bubbleLoggingMessage.append("\tisBelowApi31 = true");
80 } else {
81 bubbleLoggingMessage.append("\tnm.areBubblesEnabled() = ").append(notificationManager.areBubblesEnabled());
82 bubbleLoggingMessage.append("\tnm.getBubblePreference() = ").append(notificationManager.getBubblePreference());
83 }
84
85 bubbleLoggingMessage.append("\tnm.areBubblesAllowed() = ").append(notificationManager.areBubblesAllowed());
86 if (conversationChannel != null) {
87 bubbleLoggingMessage.append("\tcc.canBubble(").append(conversationChannel.getId()).append(") = ").append(conversationChannel.canBubble());
88 } else {
89 bubbleLoggingMessage.append("\tcc = null");
90 }
91
92
93 boolean canBubble = (Build.VERSION.SDK_INT < 31 || (notificationManager.areBubblesEnabled() && notificationManager.getBubblePreference() != NotificationManager.BUBBLE_PREFERENCE_NONE)) &&
94 (notificationManager.areBubblesAllowed() || (conversationChannel != null && conversationChannel.canBubble()));
95
96 bubbleLoggingMessage.append("\tFinal answer… canBubble: ").append(canBubble);
97 final String state = bubbleLoggingMessage.toString();
98 if (!state.equals(currentState)) {
99 Log.d(TAG, state);
100 currentState = state;
101 }
102 return canBubble;
103 }
104
105 /**
106 * Display a bubble for a given recipient's thread.
107 */
108 public static void displayAsBubble(@NonNull Context context, @NonNull RecipientId recipientId, long threadId) {
109 if (Build.VERSION.SDK_INT >= CONVERSATION_SUPPORT_VERSION) {
110 ConversationId conversationId = ConversationId.forConversation(threadId);
111 SignalExecutors.BOUNDED.execute(() -> {
112 if (canBubble(context, recipientId, threadId)) {
113 NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
114 StatusBarNotification[] notifications = notificationManager.getActiveNotifications();
115 int threadNotificationId = NotificationIds.getNotificationIdForThread(conversationId);
116 Notification activeThreadNotification = Stream.of(notifications)
117 .filter(n -> n.getId() == threadNotificationId)
118 .findFirst()
119 .map(StatusBarNotification::getNotification)
120 .orElse(null);
121
122 if (activeThreadNotification != null && activeThreadNotification.deleteIntent != null) {
123 ApplicationDependencies.getMessageNotifier().forceBubbleNotification(context, conversationId);
124 } else {
125 Recipient recipient = Recipient.resolved(recipientId);
126 NotificationFactory.notifyToBubbleConversation(context, recipient, threadId);
127 }
128 }
129 });
130 }
131 }
132
133 public enum BubbleState {
134 SHOWN,
135 HIDDEN
136 }
137}