Add per-contact notification channels.

Fixes #8119
Fixes #8121
Fixes #8122
master
Greyson Parrelli 2018-08-16 09:47:43 -07:00
parent e23fd9d491
commit e9b85a10a6
20 changed files with 609 additions and 68 deletions

View File

@ -288,7 +288,12 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ApplicationPreferencesActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.NOTIFICATION_PREFERENCES" />
</intent-filter>
</activity>
<activity android:name=".RegistrationActivity"
android:launchMode="singleTask"

View File

@ -666,13 +666,14 @@
<string name="MessageNotifier_unknown_contact_message">Contact</string>
<!-- Notification Channels -->
<string name="NotificationChannel_messages">Messages</string>
<string name="NotificationChannel_messages">Default</string>
<string name="NotificationChannel_calls">Calls</string>
<string name="NotificationChannel_failures">Failures</string>
<string name="NotificationChannel_backups">Backups</string>
<string name="NotificationChannel_locked_status">Lock status</string>
<string name="NotificationChannel_app_updates">App updates</string>
<string name="NotificationChannel_other">Other</string>
<string name="NotificationChannel_group_messages">Messages</string>
<!-- QuickResponseService -->
<string name="QuickResponseService_quick_response_unavailable_when_Signal_is_locked">Quick response unavailable when Signal is locked!</string>
@ -960,6 +961,8 @@
<!-- recipient_preferences -->
<string name="recipient_preferences__mute_conversation">Mute conversation</string>
<string name="recipient_preferences__custom_notifications">Custom notifications</string>
<string name="recipient_preferences__custom_notifications_settings">System notification settings</string>
<string name="recipient_preferences__notification_sound">Notification sound</string>
<string name="recipient_preferences__vibrate">Vibrate</string>
<string name="recipient_preferences__block">Block</string>
@ -1086,6 +1089,7 @@
<string name="preferences__inactivity_timeout_passphrase">Inactivity timeout passphrase</string>
<string name="preferences__inactivity_timeout_interval">Inactivity timeout interval</string>
<string name="preferences__notifications">Notifications</string>
<string name="preferences__system_notification_settings">System notification settings</string>
<string name="preferences__led_color">LED color</string>
<string name="preferences__led_color_unknown">Unknown</string>
<string name="preferences__pref_led_blink_title">LED blink pattern</string>

View File

@ -9,6 +9,12 @@
android:title="@string/preferences__notifications"
android:defaultValue="true" />
<org.thoughtcrime.securesms.preferences.widgets.SignalPreference
android:key="pref_key_system_notification_settings"
android:dependency="pref_key_enable_notifications"
android:persistent="false"
android:title="@string/preferences__system_notification_settings" />
<org.thoughtcrime.securesms.preferences.widgets.SignalPreference
android:dependency="pref_key_enable_notifications"
android:key="pref_key_ringtone"

View File

@ -16,6 +16,18 @@
android:disableDependentsState="true"
android:persistent="false" />
<org.thoughtcrime.securesms.components.SwitchPreferenceCompat
android:key="pref_key_recipient_custom_notifications"
android:title="@string/recipient_preferences__custom_notifications"
android:defaultValue="false"
android:persistent="false" />
<org.thoughtcrime.securesms.preferences.widgets.SignalPreference
android:key="pref_key_recipient_notification_settings"
android:dependency="pref_key_recipient_custom_notifications"
android:title="@string/recipient_preferences__custom_notifications_settings"
android:persistent="false" />
<org.thoughtcrime.securesms.preferences.widgets.SignalPreference
android:dependency="pref_key_recipient_mute"
android:key="pref_key_recipient_ringtone"

View File

@ -83,7 +83,9 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
//noinspection ConstantConditions
this.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (icicle == null) {
if (getIntent() != null && getIntent().getCategories() != null && getIntent().getCategories().contains("android.intent.category.NOTIFICATION_PREFERENCES")) {
initFragment(android.R.id.content, new NotificationsPreferenceFragment());
} else if (icicle == null) {
initFragment(android.R.id.content, new ApplicationPreferenceFragment());
}
}

View File

@ -143,6 +143,7 @@ import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.profiles.GroupShareProfileView;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
@ -462,6 +463,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
recipient = Recipient.from(this, data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true);
recipient.addListener(this);
titleView.setTitle(glideRequests, recipient);
NotificationChannels.updateContactChannelName(this, recipient);
setBlockedUserState(recipient, isSecureText, isDefaultSms);
supportInvalidateOptionsMenu();
break;

View File

@ -28,6 +28,8 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.widget.Toolbar;
import android.telephony.PhoneNumberUtils;
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
import org.thoughtcrime.securesms.logging.Log;
import android.util.Pair;
import android.view.MenuItem;
@ -53,6 +55,7 @@ import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.widgets.ColorPickerPreference;
@ -81,15 +84,17 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
public static final String ADDRESS_EXTRA = "recipient_address";
public static final String CAN_HAVE_SAFETY_NUMBER_EXTRA = "can_have_safety_number";
private static final String PREFERENCE_MUTED = "pref_key_recipient_mute";
private static final String PREFERENCE_MESSAGE_TONE = "pref_key_recipient_ringtone";
private static final String PREFERENCE_CALL_TONE = "pref_key_recipient_call_ringtone";
private static final String PREFERENCE_MESSAGE_VIBRATE = "pref_key_recipient_vibrate";
private static final String PREFERENCE_CALL_VIBRATE = "pref_key_recipient_call_vibrate";
private static final String PREFERENCE_BLOCK = "pref_key_recipient_block";
private static final String PREFERENCE_COLOR = "pref_key_recipient_color";
private static final String PREFERENCE_IDENTITY = "pref_key_recipient_identity";
private static final String PREFERENCE_ABOUT = "pref_key_number";
private static final String PREFERENCE_MUTED = "pref_key_recipient_mute";
private static final String PREFERENCE_MESSAGE_TONE = "pref_key_recipient_ringtone";
private static final String PREFERENCE_CALL_TONE = "pref_key_recipient_call_ringtone";
private static final String PREFERENCE_MESSAGE_VIBRATE = "pref_key_recipient_vibrate";
private static final String PREFERENCE_CALL_VIBRATE = "pref_key_recipient_call_vibrate";
private static final String PREFERENCE_BLOCK = "pref_key_recipient_block";
private static final String PREFERENCE_COLOR = "pref_key_recipient_color";
private static final String PREFERENCE_IDENTITY = "pref_key_recipient_identity";
private static final String PREFERENCE_ABOUT = "pref_key_number";
private static final String PREFERENCE_CUSTOM_NOTIFICATIONS = "pref_key_recipient_custom_notifications";
private static final String PREFERENCE_NOTIFICATION_SETTINGS = "pref_key_recipient_notification_settings";
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@ -255,16 +260,32 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
this.canHaveSafetyNumber = getActivity().getIntent()
.getBooleanExtra(RecipientPreferenceActivity.CAN_HAVE_SAFETY_NUMBER_EXTRA, false);
this.findPreference(PREFERENCE_MESSAGE_TONE)
.setOnPreferenceChangeListener(new RingtoneChangeListener(false));
Preference customNotificationsPref = this.findPreference(PREFERENCE_CUSTOM_NOTIFICATIONS);
Preference notificationSettingsPref = this.findPreference(PREFERENCE_NOTIFICATION_SETTINGS);
Preference messageTonePref = this.findPreference(PREFERENCE_MESSAGE_TONE);
Preference messageVibratePref = this.findPreference(PREFERENCE_MESSAGE_VIBRATE);
if (NotificationChannels.supported()) {
messageTonePref.setVisible(false);
messageVibratePref.setVisible(false);
((SwitchPreferenceCompat) customNotificationsPref).setChecked(recipient.hasCustomNotifications());
customNotificationsPref.setOnPreferenceChangeListener(new CustomNotificationsChangedListener());
notificationSettingsPref.setOnPreferenceClickListener(new NotificationSettingsClickedListener());
} else {
customNotificationsPref.setVisible(false);
notificationSettingsPref.setVisible(false);
messageTonePref.setOnPreferenceChangeListener(new RingtoneChangeListener(false));
messageVibratePref.setOnPreferenceChangeListener(new VibrateChangeListener(false));
}
this.findPreference(PREFERENCE_MESSAGE_TONE)
.setOnPreferenceClickListener(new RingtoneClickedListener(false));
this.findPreference(PREFERENCE_CALL_TONE)
.setOnPreferenceChangeListener(new RingtoneChangeListener(true));
this.findPreference(PREFERENCE_CALL_TONE)
.setOnPreferenceClickListener(new RingtoneClickedListener(true));
this.findPreference(PREFERENCE_MESSAGE_VIBRATE)
.setOnPreferenceChangeListener(new VibrateChangeListener(false));
this.findPreference(PREFERENCE_CALL_VIBRATE)
.setOnPreferenceChangeListener(new VibrateChangeListener(true));
this.findPreference(PREFERENCE_MUTED)
@ -688,5 +709,38 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
}
}
}
private class CustomNotificationsChangedListener implements Preference.OnPreferenceChangeListener {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean enabled = (boolean) newValue;
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
if (enabled) {
String channel = NotificationChannels.createChannelFor(getActivity(), recipient);
DatabaseFactory.getRecipientDatabase(getActivity()).setNotificationChannel(recipient, channel);
} else {
NotificationChannels.deleteChannelFor(getActivity(), recipient);
DatabaseFactory.getRecipientDatabase(getActivity()).setNotificationChannel(recipient, null);
}
return null;
}
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
return true;
}
}
private class NotificationSettingsClickedListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
NotificationChannels.openChannelSettings(getActivity(), recipient.getNotificationChannel(getActivity()));
return true;
}
}
}
}

View File

@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.Closeable;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
@ -53,11 +54,12 @@ public class RecipientDatabase extends Database {
private static final String PROFILE_SHARING = "profile_sharing_approval";
private static final String CALL_RINGTONE = "call_ringtone";
private static final String CALL_VIBRATE = "call_vibrate";
private static final String NOTIFICATION_CHANNEL = "notification_channel";
private static final String[] RECIPIENT_PROJECTION = new String[] {
BLOCK, NOTIFICATION, CALL_RINGTONE, VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED,
PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_CONTACT_URI,
SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING
SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL
};
static final List<String> TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
@ -122,7 +124,8 @@ public class RecipientDatabase extends Database {
SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " +
PROFILE_SHARING + " INTEGER DEFAULT 0, " +
CALL_RINGTONE + " TEXT DEFAULT NULL, " +
CALL_VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ");";
CALL_VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
NOTIFICATION_CHANNEL + " TEXT DEFAULT NULL);";
public RecipientDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
@ -135,8 +138,16 @@ public class RecipientDatabase extends Database {
null, null, null, null, null);
}
public BlockedReader readerForBlocked(Cursor cursor) {
return new BlockedReader(context, cursor);
public RecipientReader readerForBlocked(Cursor cursor) {
return new RecipientReader(context, cursor);
}
public RecipientReader getRecipientsWithNotificationChannels() {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS}, NOTIFICATION_CHANNEL + " NOT NULL",
null, null, null, null, null);
return new RecipientReader(context, cursor);
}
@ -177,6 +188,7 @@ public class RecipientDatabase extends Database {
String signalProfileName = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_NAME));
String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_AVATAR));
boolean profileSharing = cursor.getInt(cursor.getColumnIndexOrThrow(PROFILE_SHARING)) == 1;
String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL));
MaterialColor color;
byte[] profileKey = null;
@ -206,7 +218,8 @@ public class RecipientDatabase extends Database {
RegisteredState.fromId(registeredState),
profileKey, systemDisplayName, systemContactPhoto,
systemPhoneLabel, systemContactUri,
signalProfileName, signalProfileAvatar, profileSharing));
signalProfileName, signalProfileAvatar, profileSharing,
notificationChannel));
}
public BulkOperationsHandle resetAllSystemContactInfo() {
@ -324,6 +337,22 @@ public class RecipientDatabase extends Database {
recipient.setProfileSharing(enabled);
}
public void setNotificationChannel(@NonNull Recipient recipient, @Nullable String notificationChannel) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(NOTIFICATION_CHANNEL, notificationChannel);
updateOrInsert(recipient.getAddress(), contentValues);
recipient.setNotificationChannel(notificationChannel);
}
public boolean isNotificationChannelPresent(@NonNull String notificationChannel) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
try (Cursor cursor = database.query(TABLE_NAME, new String[] { ID }, NOTIFICATION_CHANNEL + " = ?",
new String[] { notificationChannel }, null, null, null, null)) {
return cursor != null && cursor.moveToFirst();
}
}
public Set<Address> getAllAddresses() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Set<Address> results = new HashSet<>();
@ -472,6 +501,7 @@ public class RecipientDatabase extends Database {
private final String signalProfileName;
private final String signalProfileAvatar;
private final boolean profileSharing;
private final String notificationChannel;
RecipientSettings(boolean blocked, long muteUntil,
@NonNull VibrateState messageVibrateState,
@ -490,7 +520,8 @@ public class RecipientDatabase extends Database {
@Nullable String systemContactUri,
@Nullable String signalProfileName,
@Nullable String signalProfileAvatar,
boolean profileSharing)
boolean profileSharing,
@Nullable String notificationChannel)
{
this.blocked = blocked;
this.muteUntil = muteUntil;
@ -511,6 +542,7 @@ public class RecipientDatabase extends Database {
this.signalProfileName = signalProfileName;
this.signalProfileAvatar = signalProfileAvatar;
this.profileSharing = profileSharing;
this.notificationChannel = notificationChannel;
}
public @Nullable MaterialColor getColor() {
@ -588,14 +620,18 @@ public class RecipientDatabase extends Database {
public boolean isProfileSharing() {
return profileSharing;
}
public @Nullable String getNotificationChannel() {
return notificationChannel;
}
}
public static class BlockedReader {
public static class RecipientReader implements Closeable {
private final Context context;
private final Cursor cursor;
private final Cursor cursor;
BlockedReader(Context context, Cursor cursor) {
RecipientReader(Context context, Cursor cursor) {
this.context = context;
this.cursor = cursor;
}
@ -612,6 +648,10 @@ public class RecipientDatabase extends Database {
return getCurrent();
}
public void close() {
cursor.close();
}
}
private static class PendingContactInfo {

View File

@ -4,9 +4,12 @@ package org.thoughtcrime.securesms.database.helpers;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.logging.Log;
import net.sqlcipher.database.SQLiteDatabase;
@ -31,6 +34,7 @@ import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -51,8 +55,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int FULL_TEXT_SEARCH = 9;
private static final int BAD_IMPORT_CLEANUP = 10;
private static final int QUOTE_MISSING = 11;
private static final int NOTIFICATION_CHANNELS = 12;
private static final int DATABASE_VERSION = 11;
private static final int DATABASE_VERSION = 12;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
@ -240,6 +245,30 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE mms ADD COLUMN quote_missing INTEGER DEFAULT 0");
}
if (oldVersion < NOTIFICATION_CHANNELS) {
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN notification_channel TEXT DEFAULT NULL");
try (Cursor cursor = db.rawQuery("SELECT recipient_ids, system_display_name, signal_profile_name, notification, vibrate FROM recipient_preferences WHERE notification NOT NULL OR vibrate != 0", null)) {
while (cursor != null && cursor.moveToNext()) {
String addressString = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
Address address = Address.fromExternal(context, addressString);
String systemName = cursor.getString(cursor.getColumnIndexOrThrow("system_display_name"));
String profileName = cursor.getString(cursor.getColumnIndexOrThrow("signal_profile_name"));
String messageSound = cursor.getString(cursor.getColumnIndexOrThrow("notification"));
Uri messageSoundUri = messageSound != null ? Uri.parse(messageSound) : null;
int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow("vibrate"));
String displayName = NotificationChannels.getChannelDisplayNameFor(systemName, profileName, address);
boolean vibrateEnabled = vibrateState == 0 ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == 1;
String channelId = NotificationChannels.createChannelFor(context, address, displayName, messageSoundUri, vibrateEnabled);
ContentValues values = new ContentValues(1);
values.put("notification_channel", channelId);
db.update("recipient_preferences", values, "recipient_ids = ?", new String[] { addressString });
}
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();

View File

@ -5,7 +5,7 @@ import android.content.Context;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.BlockedReader;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientReader;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
@ -45,18 +45,20 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje
throws IOException, UntrustedIdentityException
{
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
BlockedReader reader = database.readerForBlocked(database.getBlocked());
List<String> blocked = new LinkedList<>();
Recipient recipient;
try (RecipientReader reader = database.readerForBlocked(database.getBlocked())) {
List<String> blocked = new LinkedList<>();
while ((recipient = reader.getNext()) != null) {
if (!recipient.isGroupRecipient()) {
blocked.add(recipient.getAddress().serialize());
Recipient recipient;
while ((recipient = reader.getNext()) != null) {
if (!recipient.isGroupRecipient()) {
blocked.add(recipient.getAddress().serialize());
}
}
}
messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blocked)));
messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blocked)));
}
}
@Override

View File

@ -136,7 +136,7 @@ public class PushDecryptJob extends ContextJob {
if (TextSecurePreferences.getNeedsSqlCipherMigration(context)) {
Log.w(TAG, "Skipping job, waiting for sqlcipher migration...");
NotificationManagerCompat.from(context).notify(494949,
new NotificationCompat.Builder(context, NotificationChannels.MESSAGES)
new NotificationCompat.Builder(context, NotificationChannels.getMessagesChannel(context))
.setSmallIcon(R.drawable.icon_notification)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)

View File

@ -31,7 +31,7 @@ public abstract class AbstractNotificationBuilder extends NotificationCompat.Bui
this.context = context;
this.privacy = privacy;
setChannelId(NotificationChannels.MESSAGES);
setChannelId(NotificationChannels.getMessagesChannel(context));
setLed();
}

View File

@ -1,25 +1,51 @@
package org.thoughtcrime.securesms.notifications;
import android.annotation.TargetApi;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.logging.Log;
import java.util.Arrays;
public class NotificationChannels {
public static String MESSAGES = "messages";
public static String CALLS = "calls";
public static String FAILURES = "failures";
public static String APP_UPDATES = "app_updates";
public static String BACKUPS = "backups";
public static String LOCKED_STATUS = "locked_status";
public static String OTHER = "other";
private static final String TAG = NotificationChannels.class.getSimpleName();
private static final int VERSION_MESSAGES_CATEGORY = 2;
private static final int VERSION = 2;
private static final String CATEGORY_MESSAGES = "messages";
private static final String CONTACT_PREFIX = "contact_";
private static final String MESSAGES_PREFIX = "messages_";
public static final String CALLS = "calls_v2";
public static final String FAILURES = "failures";
public static final String APP_UPDATES = "app_updates";
public static final String BACKUPS = "backups_v2";
public static final String LOCKED_STATUS = "locked_status_v2";
public static final String OTHER = "other_v2";
/**
* Ensures all of the notification channels are created. No harm in repeat calls. Call is safely
@ -30,14 +56,193 @@ public class NotificationChannels {
return;
}
NotificationChannel messages = new NotificationChannel(MESSAGES, context.getString(R.string.NotificationChannel_messages), NotificationManager.IMPORTANCE_HIGH);
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
if (notificationManager == null) {
Log.w(TAG, "Unable to retrieve notification manager. Can't setup channels.");
return;
}
int oldVersion = TextSecurePreferences.getNotificationChannelVersion(context);
if (oldVersion != VERSION) {
onUpgrade(notificationManager, oldVersion, VERSION);
TextSecurePreferences.setNotificationChannelVersion(context, VERSION);
}
onCreate(context, notificationManager);
}
/**
* @return The channel ID for the default messages channel. Prefer
* {@link Recipient#getNotificationChannel(Context)} if you know the recipient.
*/
public static @NonNull String getMessagesChannel(@NonNull Context context) {
return getMessagesChannelId(TextSecurePreferences.getNotificationMessagesChannelVersion(context));
}
/**
* @return Whether or not notification channels are supported.
*/
public static boolean supported() {
return Build.VERSION.SDK_INT >= 26;
}
public static String getChannelDisplayNameFor(@Nullable String systemName, @Nullable String profileName, @NonNull Address address) {
return TextUtils.isEmpty(systemName) ? (TextUtils.isEmpty(profileName) ? address.serialize() : profileName) : systemName;
}
/**
* Creates a channel for the specified recipient.
* @return The channel ID for the newly-created channel.
*/
public static String createChannelFor(@NonNull Context context, @NonNull Recipient recipient) {
VibrateState vibrateState = recipient.getMessageVibrate();
boolean vibrationEnabled = vibrateState == VibrateState.DEFAULT ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == VibrateState.ENABLED;
String displayName = getChannelDisplayNameFor(recipient.getName(), recipient.getProfileName(), recipient.getAddress());
return createChannelFor(context, recipient.getAddress(), displayName, recipient.getMessageRingtone(), vibrationEnabled);
}
/**
* More verbose version of {@link #createChannelFor(Context, Recipient)}.
*/
public static String createChannelFor(@NonNull Context context,
@NonNull Address address,
@NonNull String displayName,
@Nullable Uri messageSound,
boolean vibrationEnabled)
{
if (!supported()) {
return getMessagesChannel(context);
}
String channelId = generateChannelIdFor(address);
NotificationChannel channel = new NotificationChannel(channelId, displayName, NotificationManager.IMPORTANCE_HIGH);
setLedPreference(channel, TextSecurePreferences.getNotificationLedColor(context));
channel.setGroup(CATEGORY_MESSAGES);
channel.enableVibration(vibrationEnabled);
channel.setSound(messageSound, new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT)
.build());
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
if (notificationManager == null) {
Log.w(TAG, "Unable to retrieve notification manager. Cannot create channel for recipient.");
return channelId;
}
notificationManager.createNotificationChannel(channel);
return channelId;
}
/**
* Deletes the channel generated for the provided recipient. Safe to call even if there was never
* a channel made for that recipient.
*/
public static void deleteChannelFor(@NonNull Context context, @NonNull Recipient recipient) {
if (!supported()) {
return;
}
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
if (notificationManager == null) {
Log.w(TAG, "Unable to retrieve notification manager. Cannot delete channel.");
return;
}
String channel = recipient.getNotificationChannel(context);
if (!TextUtils.isEmpty(channel) && !getMessagesChannel(context).equals(channel)) {
notificationManager.deleteNotificationChannel(channel);
}
}
/**
* Navigates the user to the system settings for the desired notification channel.
*/
public static void openChannelSettings(@NonNull Context context, @NonNull String channelId) {
if (!supported()) {
return;
}
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
context.startActivity(intent);
}
/**
* Updates the LED color for message notifications and all contact-specific message notification
* channels. Performs database operations and should therefore be invoked on a background thread.
*/
@WorkerThread
public static void updateMessagesLedColor(@NonNull Context context, @NonNull String color) {
if (!supported()) {
return;
}
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
if (notificationManager == null) {
Log.w(TAG, "Unable to retrieve notification manager. Cannot update led color.");
return;
}
updateMessageChannelLedColor(context, notificationManager, color);
updateAllRecipientChannelLedColors(context, notificationManager, color);
}
/**
* 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.
*/
public static void updateContactChannelName(@NonNull Context context, @NonNull Recipient recipient) {
if (!supported() || !recipient.hasCustomNotifications()) {
return;
}
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
if (notificationManager == null) {
Log.w(TAG, "Unable to retrieve notification manager. Cannot update channel name.");
return;
}
if (notificationManager.getNotificationChannel(recipient.getNotificationChannel(context)) == null) {
Log.w(TAG, "Tried to update the name of a channel, but that channel doesn't exist.");
return;
}
NotificationChannel channel = new NotificationChannel(recipient.getNotificationChannel(context),
getChannelDisplayNameFor(recipient.getName(), recipient.getProfileName(), recipient.getAddress()),
NotificationManager.IMPORTANCE_HIGH);
channel.setGroup(CATEGORY_MESSAGES);
notificationManager.createNotificationChannel(channel);
}
@TargetApi(26)
private static void onCreate(@NonNull Context context, @NonNull NotificationManager notificationManager) {
NotificationChannelGroup messagesGroup = new NotificationChannelGroup(CATEGORY_MESSAGES, context.getResources().getString(R.string.NotificationChannel_group_messages));
notificationManager.createNotificationChannelGroup(messagesGroup);
NotificationChannel messages = new NotificationChannel(getMessagesChannel(context), context.getString(R.string.NotificationChannel_messages), NotificationManager.IMPORTANCE_HIGH);
NotificationChannel calls = new NotificationChannel(CALLS, context.getString(R.string.NotificationChannel_calls), NotificationManager.IMPORTANCE_LOW);
NotificationChannel failures = new NotificationChannel(FAILURES, context.getString(R.string.NotificationChannel_failures), NotificationManager.IMPORTANCE_HIGH);
NotificationChannel backups = new NotificationChannel(BACKUPS, context.getString(R.string.NotificationChannel_backups), NotificationManager.IMPORTANCE_LOW);
NotificationChannel lockedStatus = new NotificationChannel(LOCKED_STATUS, context.getString(R.string.NotificationChannel_locked_status), NotificationManager.IMPORTANCE_LOW);
NotificationChannel other = new NotificationChannel(OTHER, context.getString(R.string.NotificationChannel_other), NotificationManager.IMPORTANCE_LOW);
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
messages.setGroup(CATEGORY_MESSAGES);
messages.enableVibration(TextSecurePreferences.isNotificationVibrateEnabled(context));
messages.setSound(TextSecurePreferences.getNotificationRingtone(context), new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT)
.build());
setLedPreference(messages, TextSecurePreferences.getNotificationLedColor(context));
calls.setShowBadge(false);
backups.setShowBadge(false);
lockedStatus.setShowBadge(false);
other.setShowBadge(false);
notificationManager.createNotificationChannels(Arrays.asList(messages, calls, failures, backups, lockedStatus, other));
if (BuildConfig.PLAY_STORE_DISABLED) {
@ -48,7 +253,90 @@ public class NotificationChannels {
}
}
public static boolean supported() {
return Build.VERSION.SDK_INT >= 26;
@TargetApi(26)
private static void onUpgrade(@NonNull NotificationManager notificationManager, int oldVersion, int newVersion) {
Log.i(TAG, "Upgrading channels from " + oldVersion + " to " + newVersion);
if (oldVersion < VERSION_MESSAGES_CATEGORY) {
notificationManager.deleteNotificationChannel("messages");
notificationManager.deleteNotificationChannel("calls");
notificationManager.deleteNotificationChannel("locked_status");
notificationManager.deleteNotificationChannel("backups");
notificationManager.deleteNotificationChannel("other");
}
}
@TargetApi(26)
private static void setLedPreference(@NonNull NotificationChannel channel, @NonNull String ledColor) {
if ("none".equals(ledColor)) {
channel.enableLights(false);
} else {
channel.enableLights(true);
channel.setLightColor(Color.parseColor(ledColor));
}
}
private static @NonNull String generateChannelIdFor(@NonNull Address address) {
return CONTACT_PREFIX + address.serialize() + "_" + System.currentTimeMillis();
}
@TargetApi(26)
private static @NonNull NotificationChannel copyChannel(@NonNull NotificationChannel original, @NonNull String id) {
NotificationChannel copy = new NotificationChannel(id, original.getName(), original.getImportance());
copy.setGroup(original.getGroup());
copy.setSound(original.getSound(), original.getAudioAttributes());
copy.setBypassDnd(original.canBypassDnd());
copy.enableVibration(original.shouldVibrate());
copy.setVibrationPattern(original.getVibrationPattern());
copy.setLockscreenVisibility(original.getLockscreenVisibility());
copy.setShowBadge(original.canShowBadge());
copy.setLightColor(original.getLightColor());
copy.enableLights(original.shouldShowLights());
return copy;
}
private static String getMessagesChannelId(int version) {
return MESSAGES_PREFIX + version;
}
@TargetApi(26)
private static void updateMessageChannelLedColor(@NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull String color) {
int existingVersion = TextSecurePreferences.getNotificationMessagesChannelVersion(context);
NotificationChannel existingChannel = notificationManager.getNotificationChannel(getMessagesChannelId(existingVersion));
notificationManager.deleteNotificationChannel(existingChannel.getId());
int newVersion = existingVersion + 1;
NotificationChannel newChannel = copyChannel(existingChannel, getMessagesChannelId(newVersion));
setLedPreference(newChannel, color);
notificationManager.createNotificationChannel(newChannel);
TextSecurePreferences.setNotificationMessagesChannelVersion(context, newVersion);
}
@WorkerThread
@TargetApi(26)
private static void updateAllRecipientChannelLedColors(@NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull String color) {
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
try (RecipientDatabase.RecipientReader recipients = database.getRecipientsWithNotificationChannels()) {
Recipient recipient;
while ((recipient = recipients.getNext()) != null) {
NotificationChannel existingChannel = notificationManager.getNotificationChannel(recipient.getNotificationChannel(context));
notificationManager.deleteNotificationChannel(existingChannel.getId());
NotificationChannel newChannel = copyChannel(existingChannel, generateChannelIdFor(recipient.getAddress()));
newChannel.setGroup(CATEGORY_MESSAGES);
setLedPreference(newChannel, color);
database.setNotificationChannel(recipient, newChannel.getId());
notificationManager.createNotificationChannel(newChannel);
}
}
}
}

View File

@ -60,6 +60,8 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
}
public void setThread(@NonNull Recipient recipient) {
setChannelId(recipient.getNotificationChannel(context));
if (privacy.isDisplayContact()) {
setContentTitle(recipient.toShortString());

View File

@ -19,10 +19,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.preferences.widgets.SignalListPreference;
import org.thoughtcrime.securesms.preferences.widgets.SignalPreference;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.w3c.dom.Text;
import static android.app.Activity.RESULT_OK;
@ -31,16 +28,38 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
@SuppressWarnings("unused")
private static final String TAG = NotificationsPreferenceFragment.class.getSimpleName();
private static final String PREF_SYSTEM_SETTINGS = "pref_key_system_notification_settings";
@Override
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
Preference ledBlinkPref = this.findPreference(TextSecurePreferences.LED_BLINK_PREF);
Preference messageTonePref = this.findPreference(TextSecurePreferences.RINGTONE_PREF);
Preference vibratePref = this.findPreference(TextSecurePreferences.VIBRATE_PREF);
Preference systemPref = this.findPreference(PREF_SYSTEM_SETTINGS);
if (NotificationChannels.supported()) {
ledBlinkPref.setVisible(false);
messageTonePref.setVisible(false);
vibratePref.setVisible(false);
systemPref.setOnPreferenceClickListener(p -> {
NotificationChannels.openChannelSettings(getContext(), NotificationChannels.getMessagesChannel(getContext()));
return true;
});
} else {
systemPref.setVisible(false);
ledBlinkPref.setOnPreferenceChangeListener(new ListSummaryListener());
messageTonePref.setOnPreferenceChangeListener(new RingtoneSummaryListener());
initializeListSummary((ListPreference) ledBlinkPref);
initializeRingtoneSummary(messageTonePref);
}
this.findPreference(TextSecurePreferences.LED_COLOR_PREF)
.setOnPreferenceChangeListener(new ListSummaryListener());
this.findPreference(TextSecurePreferences.LED_BLINK_PREF)
.setOnPreferenceChangeListener(new ListSummaryListener());
this.findPreference(TextSecurePreferences.RINGTONE_PREF)
.setOnPreferenceChangeListener(new RingtoneSummaryListener());
.setOnPreferenceChangeListener(new LedColorChangeListener());
this.findPreference(TextSecurePreferences.REPEAT_ALERTS_PREF)
.setOnPreferenceChangeListener(new ListSummaryListener());
this.findPreference(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF)
@ -83,17 +102,14 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
});
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.LED_COLOR_PREF));
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.LED_BLINK_PREF));
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.REPEAT_ALERTS_PREF));
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF));
if (NotificationChannels.supported()) {
((SignalListPreference) this.findPreference(TextSecurePreferences.NOTIFICATION_PRIORITY_PREF)).disableDialog();
this.findPreference(TextSecurePreferences.NOTIFICATION_PRIORITY_PREF)
.setOnPreferenceClickListener(preference -> {
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_CHANNEL_ID, NotificationChannels.MESSAGES);
intent.putExtra(Settings.EXTRA_CHANNEL_ID, NotificationChannels.getMessagesChannel(getContext()));
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getContext().getPackageName());
startActivity(intent);
return true;
@ -102,7 +118,6 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.NOTIFICATION_PRIORITY_PREF));
}
initializeRingtoneSummary(findPreference(TextSecurePreferences.RINGTONE_PREF));
initializeCallRingtoneSummary(findPreference(TextSecurePreferences.CALL_RINGTONE_PREF));
initializeCallVibrateSummary((SwitchPreferenceCompat)findPreference(TextSecurePreferences.CALL_VIBRATE_PREF));
}
@ -201,6 +216,22 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
return super.onPreferenceChange(preference, value);
}
}
@SuppressLint("StaticFieldLeak")
private class LedColorChangeListener extends ListSummaryListener {
@Override
public boolean onPreferenceChange(Preference preference, Object value) {
if (NotificationChannels.supported()) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
NotificationChannels.updateMessagesLedColor(getActivity(), (String) value);
return null;
}
}.execute();
}
return super.onPreferenceChange(preference, value);
}
}
}

View File

@ -5,6 +5,7 @@ import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.util.TypedValue;
@ -16,9 +17,9 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class SignalListPreference extends ListPreference {
private TextView rightSummary;
private CharSequence summary;
private boolean dialogDisabled;
private TextView rightSummary;
private CharSequence summary;
private OnPreferenceClickListener clickListener;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public SignalListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
@ -65,13 +66,15 @@ public class SignalListPreference extends ListPreference {
}
}
public void disableDialog() {
dialogDisabled = true;
@Override
public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) {
super.setOnPreferenceClickListener(onPreferenceClickListener);
this.clickListener = onPreferenceClickListener;
}
@Override
protected void onClick() {
if (!dialogDisabled) {
if (clickListener == null || !clickListener.onPreferenceClick(this)) {
super.onClick();
}
}

View File

@ -17,6 +17,7 @@
*/
package org.thoughtcrime.securesms.recipients;
import android.app.NotificationChannel;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@ -43,6 +44,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails;
import org.thoughtcrime.securesms.util.FutureTaskListener;
import org.thoughtcrime.securesms.util.ListenableFutureTask;
@ -90,6 +92,7 @@ public class Recipient implements RecipientModifiedListener {
private @Nullable String profileName;
private @Nullable String profileAvatar;
private boolean profileSharing;
private String notificationChannel;
@SuppressWarnings("ConstantConditions")
@ -135,6 +138,7 @@ public class Recipient implements RecipientModifiedListener {
this.seenInviteReminder = stale.seenInviteReminder;
this.defaultSubscriptionId = stale.defaultSubscriptionId;
this.registered = stale.registered;
this.notificationChannel = stale.notificationChannel;
this.profileKey = stale.profileKey;
this.profileName = stale.profileName;
this.profileAvatar = stale.profileAvatar;
@ -158,6 +162,7 @@ public class Recipient implements RecipientModifiedListener {
this.seenInviteReminder = details.get().seenInviteReminder;
this.defaultSubscriptionId = details.get().defaultSubscriptionId;
this.registered = details.get().registered;
this.notificationChannel = details.get().notificationChannel;
this.profileKey = details.get().profileKey;
this.profileName = details.get().profileName;
this.profileAvatar = details.get().profileAvatar;
@ -187,6 +192,7 @@ public class Recipient implements RecipientModifiedListener {
Recipient.this.seenInviteReminder = result.seenInviteReminder;
Recipient.this.defaultSubscriptionId = result.defaultSubscriptionId;
Recipient.this.registered = result.registered;
Recipient.this.notificationChannel = result.notificationChannel;
Recipient.this.profileKey = result.profileKey;
Recipient.this.profileName = result.profileName;
Recipient.this.profileAvatar = result.profileAvatar;
@ -233,6 +239,7 @@ public class Recipient implements RecipientModifiedListener {
this.seenInviteReminder = details.seenInviteReminder;
this.defaultSubscriptionId = details.defaultSubscriptionId;
this.registered = details.registered;
this.notificationChannel = details.notificationChannel;
this.profileKey = details.profileKey;
this.profileName = details.profileName;
this.profileAvatar = details.profileAvatar;
@ -581,6 +588,30 @@ public class Recipient implements RecipientModifiedListener {
if (notify) notifyListeners();
}
public synchronized @NonNull String getNotificationChannel(@NonNull Context context) {
if (!NotificationChannels.supported() || notificationChannel == null) {
return NotificationChannels.getMessagesChannel(context);
}
return notificationChannel;
}
public void setNotificationChannel(@Nullable String value) {
boolean notify = false;
synchronized (this) {
if (!Util.equals(this.notificationChannel, value)) {
this.notificationChannel = value;
notify = true;
}
}
if (notify) notifyListeners();
}
public synchronized boolean hasCustomNotifications() {
return NotificationChannels.supported() && notificationChannel != null;
}
public synchronized @Nullable byte[] getProfileKey() {
return profileKey;
}

View File

@ -174,6 +174,7 @@ class RecipientProvider {
@Nullable final String profileAvatar;
final boolean profileSharing;
final boolean systemContact;
@Nullable final String notificationChannel;
RecipientDetails(@Nullable String name, @Nullable Long groupAvatarId,
boolean systemContact, @Nullable RecipientSettings settings,
@ -200,6 +201,7 @@ class RecipientProvider {
this.profileAvatar = settings != null ? settings.getProfileAvatar() : null;
this.profileSharing = settings != null && settings.isProfileSharing();
this.systemContact = systemContact;
this.notificationChannel = settings != null ? settings.getNotificationChannel() : null;
if (name == null && settings != null) this.name = settings.getSystemDisplayName();
else this.name = name;

View File

@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.push.AccountManagerFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
@ -182,6 +183,14 @@ public class DirectoryHelper {
handle.finish();
}
if (NotificationChannels.supported()) {
try (RecipientDatabase.RecipientReader recipients = DatabaseFactory.getRecipientDatabase(context).getRecipientsWithNotificationChannels()) {
Recipient recipient;
while ((recipient = recipients.getNext()) != null) {
NotificationChannels.updateContactChannelName(context, recipient);
}
}
}
} catch (RemoteException | OperationApplicationException e) {
Log.w(TAG, e);
}

View File

@ -54,7 +54,7 @@ public class TextSecurePreferences {
private static final String LAST_EXPERIENCE_VERSION_PREF = "last_experience_version_code";
private static final String EXPERIENCE_DISMISSED_PREF = "experience_dismissed";
public static final String RINGTONE_PREF = "pref_key_ringtone";
private static final String VIBRATE_PREF = "pref_key_vibrate";
public static final String VIBRATE_PREF = "pref_key_vibrate";
private static final String NOTIFICATION_PREF = "pref_key_enable_notifications";
public static final String LED_COLOR_PREF = "pref_led_color";
public static final String LED_BLINK_PREF = "pref_led_blink";
@ -159,6 +159,9 @@ public class TextSecurePreferences {
private static final String LOG_ENCRYPTED_SECRET = "pref_log_encrypted_secret";
private static final String LOG_UNENCRYPTED_SECRET = "pref_log_unencrypted_secret";
private static final String NOTIFICATION_CHANNEL_VERSION = "pref_notification_channel_version";
private static final String NOTIFICATION_MESSAGES_CHANNEL_VERSION = "pref_notification_messages_channel_version";
public static boolean isScreenLockEnabled(@NonNull Context context) {
return getBooleanPreference(context, SCREEN_LOCK, false);
}
@ -960,6 +963,22 @@ public class TextSecurePreferences {
return getStringPreference(context, LOG_UNENCRYPTED_SECRET, null);
}
public static int getNotificationChannelVersion(Context context) {
return getIntegerPreference(context, NOTIFICATION_CHANNEL_VERSION, 1);
}
public static void setNotificationChannelVersion(Context context, int version) {
setIntegerPrefrence(context, NOTIFICATION_CHANNEL_VERSION, version);
}
public static int getNotificationMessagesChannelVersion(Context context) {
return getIntegerPreference(context, NOTIFICATION_MESSAGES_CHANNEL_VERSION, 1);
}
public static void setNotificationMessagesChannelVersion(Context context, int version) {
setIntegerPrefrence(context, NOTIFICATION_MESSAGES_CHANNEL_VERSION, version);
}
public static void setBooleanPreference(Context context, String key, boolean value) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply();
}