Improve db usage around ensuring custom notification channel stae.

This commit is contained in:
Cody Henthorne
2026-04-29 12:12:13 -04:00
committed by Greyson Parrelli
parent f32184c27e
commit 4d09776277
4 changed files with 59 additions and 57 deletions
@@ -303,14 +303,10 @@ object ContactDiscovery {
}
if (NotificationChannels.supported()) {
SignalDatabase.recipients.getRecipientsWithNotificationChannels().use { reader ->
var recipient: Recipient? = reader.getNext()
while (recipient != null) {
NotificationChannels.getInstance().updateContactChannelName(recipient)
recipient = reader.getNext()
}
}
SignalDatabase
.recipients
.getRecipientsWithNotificationChannels()
.forEach { NotificationChannels.getInstance().updateContactChannelName(Recipient.resolved(it.id)) }
}
}
@@ -731,9 +731,13 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
return RecipientReader(cursor)
}
fun getRecipientsWithNotificationChannels(): RecipientReader {
val cursor = readableDatabase.query(TABLE_NAME, ID_PROJECTION, "$NOTIFICATION_CHANNEL NOT NULL", null, null, null, null)
return RecipientReader(cursor)
fun getRecipientsWithNotificationChannels(): List<RecipientNotificationData> {
return readableDatabase
.select(ID, NOTIFICATION_CHANNEL)
.from(TABLE_NAME)
.where("$NOTIFICATION_CHANNEL NOT NULL")
.run()
.readToList { RecipientNotificationData(RecipientId.from(it.requireLong(ID)), it.requireNonNullString(NOTIFICATION_CHANNEL)) }
}
fun getExistingRecords(ids: Collection<RecipientId>): Map<RecipientId, RecipientRecord> {
@@ -5027,4 +5031,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
val expiringProfileKeyCredential: Pair<ProfileKey, ExpiringProfileKeyCredential>? = null,
val clearUsername: Boolean = false
)
data class RecipientNotificationData(val id: RecipientId, val channel: String)
}
@@ -175,7 +175,7 @@ public class NotificationChannels {
/**
* Whether or not notifications for the entire app are enabled.
*/
public synchronized boolean areNotificationsEnabled() {
public boolean areNotificationsEnabled() {
if (Build.VERSION.SDK_INT >= 24) {
return ServiceUtil.getNotificationManager(context).areNotificationsEnabled();
} else {
@@ -193,18 +193,16 @@ public class NotificationChannels {
return;
}
RecipientTable db = SignalDatabase.recipients();
try (RecipientTable.RecipientReader reader = db.getRecipientsWithNotificationChannels()) {
Recipient recipient;
while ((recipient = reader.getNext()) != null) {
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
if (!channelExists(notificationManager.getNotificationChannel(recipient.getNotificationChannel()))) {
String id = createChannelFor(recipient);
db.setNotificationChannel(recipient.getId(), id);
}
}
}
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
SignalDatabase.recipients()
.getRecipientsWithNotificationChannels()
.stream()
.forEach((info) -> {
if (!channelExists(notificationManager.getNotificationChannel(info.getChannel()))) {
String id = createChannelFor(Recipient.resolved(info.getId()));
SignalDatabase.recipients().setNotificationChannel(info.getId(), id);
}
});
ensureCustomChannelConsistency();
}
@@ -465,7 +463,7 @@ public class NotificationChannels {
* {@link #areNotificationsEnabled()} and {@link #isMessagesChannelGroupEnabled()}
* to be safe.
*/
public synchronized boolean isMessageChannelEnabled() {
public boolean isMessageChannelEnabled() {
if (!supported()) {
return true;
}
@@ -481,7 +479,7 @@ public class NotificationChannels {
* a user could have blocked the specific channel, or notifications overall, and it'd still be
* true. See {@link #isMessageChannelEnabled()} and {@link #areNotificationsEnabled()}.
*/
public synchronized boolean isMessagesChannelGroupEnabled() {
public boolean isMessagesChannelGroupEnabled() {
if (Build.VERSION.SDK_INT < 28) {
return true;
}
@@ -492,7 +490,7 @@ public class NotificationChannels {
return group != null && !group.isBlocked();
}
public synchronized boolean isCallsChannelValid() {
public boolean isCallsChannelValid() {
if (!supported()) {
return true;
}
@@ -561,18 +559,16 @@ public class NotificationChannels {
}
Log.d(TAG, "ensureCustomChannelConsistency()");
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
RecipientTable db = SignalDatabase.recipients();
List<Recipient> customRecipients = new ArrayList<>();
Set<String> customChannelIds = new HashSet<>();
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
RecipientTable db = SignalDatabase.recipients();
List<RecipientTable.RecipientNotificationData> customRecipients;
Set<String> customChannelIds = new HashSet<>();
try (RecipientTable.RecipientReader reader = db.getRecipientsWithNotificationChannels()) {
Recipient recipient;
while ((recipient = reader.getNext()) != null) {
customRecipients.add(recipient);
customChannelIds.add(recipient.getNotificationChannel());
}
}
customRecipients = db.getRecipientsWithNotificationChannels();
customRecipients.stream()
.forEach((info) -> {
customChannelIds.add(info.getChannel());
});
Set<String> existingChannelIds = notificationManager.getNotificationChannels().stream().map(NotificationChannel::getId).collect(Collectors.toSet());
@@ -602,9 +598,9 @@ public class NotificationChannels {
}
}
for (Recipient customRecipient : customRecipients) {
if (!existingChannelIds.contains(customRecipient.getNotificationChannel())) {
Log.i(TAG, "Consistency: Removing custom channel '"+ customRecipient.getNotificationChannel() + "' because the system doesn't have it.");
for (RecipientTable.RecipientNotificationData customRecipient : customRecipients) {
if (!existingChannelIds.contains(customRecipient.getChannel())) {
Log.i(TAG, "Consistency: Removing custom channel '"+ customRecipient.getChannel() + "' because the system doesn't have it.");
db.setNotificationChannel(customRecipient.getId(), null);
}
}
@@ -738,7 +734,11 @@ public class NotificationChannels {
private static @NonNull String generateChannelIdFor(@NonNull Recipient recipient) {
return CONTACT_PREFIX + recipient.getId().serialize() + "_" + System.currentTimeMillis();
return generateChannelIdFor(recipient.getId());
}
private static @NonNull String generateChannelIdFor(@NonNull RecipientId recipientId) {
return CONTACT_PREFIX + recipientId.serialize() + "_" + System.currentTimeMillis();
}
@TargetApi(26)
@@ -771,17 +771,14 @@ public class NotificationChannels {
private void updateAllRecipientChannelLedColors(@NonNull NotificationManager notificationManager, @NonNull String color) {
RecipientTable database = SignalDatabase.recipients();
try (RecipientTable.RecipientReader recipients = database.getRecipientsWithNotificationChannels()) {
Recipient recipient;
while ((recipient = recipients.getNext()) != null) {
assert recipient.getNotificationChannel() != null;
database.getRecipientsWithNotificationChannels()
.stream()
.forEach((info) -> {
String newChannelId = generateChannelIdFor(info.getId());
boolean success = updateExistingChannel(notificationManager, info.getChannel(), newChannelId, channel -> setLedPreference(channel, color));
String newChannelId = generateChannelIdFor(recipient);
boolean success = updateExistingChannel(notificationManager, recipient.getNotificationChannel(), newChannelId, channel -> setLedPreference(channel, color));
database.setNotificationChannel(recipient.getId(), success ? newChannelId : null);
}
}
database.setNotificationChannel(info.getId(), success ? newChannelId : null);
});
ensureCustomChannelConsistency();
}
@@ -53,23 +53,26 @@ class VitalsViewModel(private val context: Application) : AndroidViewModel(conte
return Single.fromCallable {
val deviceSpecificCondition = SlowNotificationHeuristics.getDeviceSpecificShowCondition()
if (deviceSpecificCondition == ShowCondition.ALWAYS && SlowNotificationHeuristics.shouldShowDeviceSpecificDialog()) {
val shouldShowDeviceSpecificDialog = SlowNotificationHeuristics.shouldShowDeviceSpecificDialog()
val havingDelayedNotifications = SlowNotificationHeuristics.isHavingDelayedNotifications()
if (deviceSpecificCondition == ShowCondition.ALWAYS && shouldShowDeviceSpecificDialog) {
return@fromCallable State.PROMPT_SPECIFIC_BATTERY_SAVER_DIALOG
}
if (deviceSpecificCondition == ShowCondition.HAS_BATTERY_OPTIMIZATION_ON && SlowNotificationHeuristics.shouldShowDeviceSpecificDialog() && SlowNotificationHeuristics.isBatteryOptimizationsOn()) {
if (deviceSpecificCondition == ShowCondition.HAS_BATTERY_OPTIMIZATION_ON && shouldShowDeviceSpecificDialog && SlowNotificationHeuristics.isBatteryOptimizationsOn()) {
return@fromCallable State.PROMPT_SPECIFIC_BATTERY_SAVER_DIALOG
}
if (deviceSpecificCondition == ShowCondition.HAS_SLOW_NOTIFICATIONS && SlowNotificationHeuristics.shouldShowDeviceSpecificDialog() && SlowNotificationHeuristics.isHavingDelayedNotifications()) {
if (deviceSpecificCondition == ShowCondition.HAS_SLOW_NOTIFICATIONS && shouldShowDeviceSpecificDialog && havingDelayedNotifications) {
return@fromCallable State.PROMPT_SPECIFIC_BATTERY_SAVER_DIALOG
}
if (SlowNotificationHeuristics.isHavingDelayedNotifications() && SlowNotificationHeuristics.shouldPromptBatterySaver()) {
if (havingDelayedNotifications && SlowNotificationHeuristics.shouldPromptBatterySaver()) {
return@fromCallable State.PROMPT_GENERAL_BATTERY_SAVER_DIALOG
}
if (SlowNotificationHeuristics.isHavingDelayedNotifications() && SlowNotificationHeuristics.shouldPromptUserForDelayedNotificationLogs()) {
if (havingDelayedNotifications && SlowNotificationHeuristics.shouldPromptUserForDelayedNotificationLogs()) {
return@fromCallable State.PROMPT_DEBUGLOGS_FOR_NOTIFICATIONS
}