Improve notification channel consistency checks with Android Conversations.
This commit is contained in:
parent
36443c59f9
commit
bece58d939
8 changed files with 108 additions and 15 deletions
|
@ -430,7 +430,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
|||
}
|
||||
}
|
||||
|
||||
String channelId = NotificationChannels.createChannelFor(context, "contact_" + address + "_" + System.currentTimeMillis(), displayName, messageSoundUri, vibrateEnabled);
|
||||
String channelId = NotificationChannels.createChannelFor(context, "contact_" + address + "_" + System.currentTimeMillis(), displayName, messageSoundUri, vibrateEnabled, null);
|
||||
|
||||
ContentValues values = new ContentValues(1);
|
||||
values.put("notification_channel", channelId);
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.groups.MembershipNotSuitableForV2Exception;
|
|||
import org.thoughtcrime.securesms.groups.SelectionLimits;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeErrorCallback;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
|
@ -148,6 +149,15 @@ final class ManageGroupRepository {
|
|||
});
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
boolean hasCustomNotifications(Recipient recipient) {
|
||||
if (recipient.getNotificationChannel() != null || !NotificationChannels.supported()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return NotificationChannels.updateWithShortcutBasedChannel(context, recipient);
|
||||
}
|
||||
|
||||
static final class GroupStateResult {
|
||||
|
||||
private final long threadId;
|
||||
|
|
|
@ -36,7 +36,6 @@ import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
|
|||
import org.thoughtcrime.securesms.groups.ui.addmembers.AddMembersActivity;
|
||||
import org.thoughtcrime.securesms.groups.ui.managegroup.dialogs.GroupMentionSettingDialog;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupLinkUrlAndStatus;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
|
@ -120,8 +119,7 @@ public class ManageGroupViewModel extends ViewModel {
|
|||
this.canAddMembers = liveGroup.selfCanAddMembers();
|
||||
this.muteState = Transformations.map(this.groupRecipient,
|
||||
recipient -> new MuteState(recipient.getMuteUntil(), recipient.isMuted()));
|
||||
this.hasCustomNotifications = Transformations.map(this.groupRecipient,
|
||||
recipient -> recipient.getNotificationChannel() != null || !NotificationChannels.supported());
|
||||
this.hasCustomNotifications = LiveDataUtil.mapAsync(this.groupRecipient, manageGroupRepository::hasCustomNotifications);
|
||||
this.canLeaveGroup = liveGroup.isActive();
|
||||
this.canBlockGroup = Transformations.map(this.groupRecipient, recipient -> RecipientUtil.isBlockable(recipient) && !recipient.isBlocked());
|
||||
this.canUnblockGroup = Transformations.map(this.groupRecipient, Recipient::isBlocked);
|
||||
|
|
|
@ -176,10 +176,15 @@ public final class NotificationCancellationHelper {
|
|||
return true;
|
||||
}
|
||||
|
||||
RecipientId recipientId = RecipientId.from(notification.getShortcutId());
|
||||
Long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipientId);
|
||||
RecipientId recipientId = ConversationUtil.getRecipientId(notification.getShortcutId());
|
||||
if (recipientId == null) {
|
||||
Log.d(TAG, "isCancellable: Unable to get recipient from shortcut id");
|
||||
return true;
|
||||
}
|
||||
|
||||
Long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipientId);
|
||||
long focusedThreadId = ApplicationDependencies.getMessageNotifier().getVisibleThread();
|
||||
|
||||
if (Objects.equals(threadId, focusedThreadId)) {
|
||||
Log.d(TAG, "isCancellable: user entered full screen thread.");
|
||||
return true;
|
||||
|
|
|
@ -31,6 +31,8 @@ import org.thoughtcrime.securesms.database.RecipientDatabase;
|
|||
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.ConversationUtil;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
|
@ -38,8 +40,12 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.thoughtcrime.securesms.util.ConversationUtil.CONVERSATION_SUPPORT_VERSION;
|
||||
|
||||
public class NotificationChannels {
|
||||
|
||||
private static final String TAG = Log.tag(NotificationChannels.class);
|
||||
|
@ -165,7 +171,7 @@ public class NotificationChannels {
|
|||
Uri messageRingtone = recipient.getMessageRingtone() != null ? recipient.getMessageRingtone() : getMessageRingtone(context);
|
||||
String displayName = recipient.getDisplayName(context);
|
||||
|
||||
return createChannelFor(context, generateChannelIdFor(recipient), displayName, messageRingtone, vibrationEnabled);
|
||||
return createChannelFor(context, generateChannelIdFor(recipient), displayName, messageRingtone, vibrationEnabled, ConversationUtil.getShortcutId(recipient));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,7 +181,8 @@ public class NotificationChannels {
|
|||
@NonNull String channelId,
|
||||
@NonNull String displayName,
|
||||
@Nullable Uri messageSound,
|
||||
boolean vibrationEnabled)
|
||||
boolean vibrationEnabled,
|
||||
@Nullable String shortcutId)
|
||||
{
|
||||
if (!supported()) {
|
||||
return null;
|
||||
|
@ -193,6 +200,10 @@ public class NotificationChannels {
|
|||
.build());
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= CONVERSATION_SUPPORT_VERSION && shortcutId != null) {
|
||||
channel.setConversationId(getMessagesChannel(context), shortcutId);
|
||||
}
|
||||
|
||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
|
||||
|
@ -465,6 +476,32 @@ public class NotificationChannels {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to update a recipient with shortcut based notification channel if the system made one for us and we don't
|
||||
* have a channel set yet.
|
||||
*
|
||||
* @return true if a shortcut based notification channel was found and then associated with the recipient, false otherwise
|
||||
*/
|
||||
@WorkerThread
|
||||
public static boolean updateWithShortcutBasedChannel(@NonNull Context context, @NonNull Recipient recipient) {
|
||||
if (Build.VERSION.SDK_INT >= CONVERSATION_SUPPORT_VERSION && TextUtils.isEmpty(recipient.getNotificationChannel())) {
|
||||
String shortcutId = ConversationUtil.getShortcutId(recipient);
|
||||
|
||||
Optional<NotificationChannel> channel = ServiceUtil.getNotificationManager(context)
|
||||
.getNotificationChannels()
|
||||
.stream()
|
||||
.filter(c -> Objects.equals(shortcutId, c.getConversationId()))
|
||||
.findFirst();
|
||||
|
||||
if (channel.isPresent()) {
|
||||
Log.i(TAG, "Conversation channel created outside of app, while running. Update " + recipient.getId() + " to use '" + channel.get().getId() + "'");
|
||||
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient.getId(), channel.get().getId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the name of an existing channel to match the recipient's current name. Will have no
|
||||
* effect if the recipient doesn't have an existing valid channel.
|
||||
|
@ -516,8 +553,24 @@ public class NotificationChannels {
|
|||
if (existingChannel.getId().startsWith(CONTACT_PREFIX) && !customChannelIds.contains(existingChannel.getId())) {
|
||||
Log.i(TAG, "Consistency: Deleting channel '"+ existingChannel.getId() + "' because the DB has no record of it.");
|
||||
notificationManager.deleteNotificationChannel(existingChannel.getId());
|
||||
} else if (existingChannel.getId().startsWith(MESSAGES_PREFIX) &&
|
||||
Build.VERSION.SDK_INT >= CONVERSATION_SUPPORT_VERSION &&
|
||||
existingChannel.getConversationId() != null)
|
||||
{
|
||||
if (customChannelIds.contains(existingChannel.getId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RecipientId id = ConversationUtil.getRecipientId(existingChannel.getConversationId());
|
||||
if (id != null) {
|
||||
Log.i(TAG, "Consistency: Conversation channel created outside of app, update " + id + " to use '" + existingChannel.getId() + "'");
|
||||
db.setNotificationChannel(id, existingChannel.getId());
|
||||
} else {
|
||||
Log.i(TAG, "Consistency: Conversation channel created outside of app with no matching recipient, deleting channel '" + existingChannel.getId() + "'");
|
||||
notificationManager.deleteNotificationChannel(existingChannel.getId());
|
||||
}
|
||||
} else if (existingChannel.getId().startsWith(MESSAGES_PREFIX) && !existingChannel.getId().equals(getMessagesChannel(context))) {
|
||||
Log.i(TAG, "Consistency: Deleting channel '"+ existingChannel.getId() + "' because it's out of date.");
|
||||
Log.i(TAG, "Consistency: Deleting channel '" + existingChannel.getId() + "' because it's out of date.");
|
||||
notificationManager.deleteNotificationChannel(existingChannel.getId());
|
||||
}
|
||||
}
|
||||
|
@ -617,6 +670,10 @@ public class NotificationChannels {
|
|||
copy.setLightColor(original.getLightColor());
|
||||
copy.enableLights(original.shouldShowLights());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= CONVERSATION_SUPPORT_VERSION && original.getConversationId() != null) {
|
||||
copy.setConversationId(original.getParentChannelId(), original.getConversationId());
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,15 +10,12 @@ import com.annimon.stream.Stream;
|
|||
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.color.MaterialColor;
|
||||
import org.thoughtcrime.securesms.color.MaterialColors;
|
||||
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
||||
|
@ -106,4 +103,13 @@ final class ManageRecipientRepository {
|
|||
void getActiveGroupCount(@NonNull Consumer<Integer> onComplete) {
|
||||
SignalExecutors.BOUNDED.execute(() -> onComplete.accept(DatabaseFactory.getGroupDatabase(context).getActiveGroupCount()));
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
boolean hasCustomNotifications(Recipient recipient) {
|
||||
if (recipient.getNotificationChannel() != null || !NotificationChannels.supported()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return NotificationChannels.updateWithShortcutBasedChannel(context, recipient);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.thoughtcrime.securesms.recipients.ui.managerecipient;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.NotificationChannel;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
|
@ -78,7 +79,7 @@ public final class ManageRecipientViewModel extends ViewModel {
|
|||
this.groupListCollapseState = new DefaultValueLiveData<>(CollapseState.COLLAPSED);
|
||||
this.disappearingMessageTimer = Transformations.map(this.recipient, r -> ExpirationUtil.getExpirationDisplayValue(context, r.getExpireMessages()));
|
||||
this.muteState = Transformations.map(this.recipient, r -> new MuteState(r.getMuteUntil(), r.isMuted()));
|
||||
this.hasCustomNotifications = Transformations.map(this.recipient, r -> r.getNotificationChannel() != null || !NotificationChannels.supported());
|
||||
this.hasCustomNotifications = LiveDataUtil.mapAsync(this.recipient, manageRecipientRepository::hasCustomNotifications);
|
||||
this.canBlock = Transformations.map(this.recipient, r -> RecipientUtil.isBlockable(r) && !r.isBlocked());
|
||||
this.canUnblock = Transformations.map(this.recipient, Recipient::isBlocked);
|
||||
this.internalDetails = Transformations.map(this.recipient, this::populateInternalDetails);
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.content.pm.ShortcutManager;
|
|||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
|
@ -19,7 +20,6 @@ import org.thoughtcrime.securesms.R;
|
|||
import org.thoughtcrime.securesms.conversation.ConversationIntents;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.jobs.ConversationShortcutUpdateJob;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
|
@ -132,6 +132,22 @@ public final class ConversationUtil {
|
|||
return getShortcutId(recipient.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the recipient id from the provided shortcutId.
|
||||
*/
|
||||
public static @Nullable RecipientId getRecipientId(@Nullable String shortcutId) {
|
||||
if (shortcutId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return RecipientId.from(shortcutId);
|
||||
} catch (Throwable t) {
|
||||
Log.d(TAG, "Unable to parse recipientId from shortcutId", t);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(CONVERSATION_SUPPORT_VERSION)
|
||||
public static int getMaxShortcuts(@NonNull Context context) {
|
||||
ShortcutManager shortcutManager = ServiceUtil.getShortcutManager(context);
|
||||
|
|
Loading…
Add table
Reference in a new issue