That fuck shit the fascists are using
at master 137 lines 6.1 kB view raw
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}