Compare commits

..

12 Commits

Author SHA1 Message Date
Greyson Parrelli
1c75f375c3 Bump version to 4.25.10 2018-09-07 17:59:33 -07:00
Greyson Parrelli
7c5e1db6a2 Fix NPE in MultiDeviceContactUpdateJob.
Fixes #8180
2018-09-07 17:52:50 -07:00
Greyson Parrelli
82c0ea792a Fix animation crash.
Shoutout to @bomba1990 for fixing this around the same time as me :)

Fixes #8182
2018-09-07 17:52:50 -07:00
Greyson Parrelli
70eecb754e Address bugs in notification channel backup restore. 2018-09-07 17:49:15 -07:00
Greyson Parrelli
ad036b0d6a Fix backup restore issues from restoring newer Signal backups.
Fixes #8184
2018-09-07 16:08:45 -07:00
Greyson Parrelli
15b4517e35 Prevent restoring newer backups into older versions of Signal.
Relates to #8184
2018-09-07 15:54:38 -07:00
Greyson Parrelli
d2a8abe769 Bump version to 4.25.9 2018-09-04 18:12:13 -07:00
Greyson Parrelli
8d43fb850d Make group notifications use the latest sender's ringtone. 2018-09-04 18:10:47 -07:00
Greyson Parrelli
11d34512a0 Fix double notifications in O+. 2018-09-04 18:10:42 -07:00
Greyson Parrelli
604e5d788e Bump version to 4.25.8 2018-09-04 11:13:54 -07:00
Greyson Parrelli
73b18fc1dd Fix NotificationChannel backup import.
We were recreating the channels before the database upgrade. We
have to do it after.

Fixes #8174
2018-09-04 10:57:33 -07:00
Greyson Parrelli
d9ba6962c7 Fixed NPE during channel update. 2018-09-03 17:48:55 -07:00
12 changed files with 132 additions and 76 deletions

View File

@@ -250,8 +250,8 @@ android {
}
defaultConfig {
versionCode 401
versionName "4.25.7"
versionCode 404
versionName "4.25.10"
minSdkVersion 14
targetSdkVersion 25

View File

@@ -1335,6 +1335,7 @@
<string name="preferences_chats__create_backup">Create backup</string>
<string name="RegistrationActivity_enter_backup_passphrase">Enter backup passphrase</string>
<string name="RegistrationActivity_restore">Restore</string>
<string name="RegistrationActivity_backup_failure_downgrade">Cannot import backups from newer versions of Signal</string>
<string name="RegistrationActivity_incorrect_backup_passphrase">Incorrect backup passphrase</string>
<string name="RegistrationActivity_checking">Checking...</string>
<string name="RegistrationActivity_d_messages_so_far">%d messages so far...</string>

View File

@@ -364,7 +364,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
mutePreference.setChecked(recipient.isMuted());
ringtoneMessagePreference.setSummary(ringtoneMessagePreference.isEnabled() ? getRingtoneSummary(getContext(), recipient.getMessageRingtone(getContext())) : "");
ringtoneMessagePreference.setSummary(ringtoneMessagePreference.isEnabled() ? getRingtoneSummary(getContext(), recipient.getMessageRingtone()) : "");
ringtoneCallPreference.setSummary(getRingtoneSummary(getContext(), recipient.getCallRingtone()));
Pair<String, Integer> vibrateMessageSummary = getVibrateSummary(getContext(), recipient.getMessageVibrate());
@@ -512,7 +512,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
current = recipient.getCallRingtone();
defaultUri = TextSecurePreferences.getCallNotificationRingtone(getContext());
} else {
current = recipient.getMessageRingtone(getContext());
current = recipient.getMessageRingtone();
defaultUri = TextSecurePreferences.getNotificationRingtone(getContext());
}
@@ -754,17 +754,5 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
return true;
}
}
private class NotificationSettingsClickedListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
String channel = recipient.getNotificationChannel();
if (channel != null) {
NotificationChannels.openChannelSettings(getActivity(), channel);
}
return true;
}
}
}
}

View File

@@ -69,6 +69,7 @@ import org.thoughtcrime.securesms.database.NoExternalStorageException;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.jobs.GcmRefreshJob;
import org.thoughtcrime.securesms.lock.RegistrationLockReminders;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.push.AccountManagerFactory;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
@@ -365,9 +366,9 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
restoreButton.setIndeterminateProgressMode(true);
restoreButton.setProgress(50);
new AsyncTask<Void, Void, Boolean>() {
new AsyncTask<Void, Void, BackupImportResult>() {
@Override
protected Boolean doInBackground(Void... voids) {
protected BackupImportResult doInBackground(Void... voids) {
try {
Context context = RegistrationActivity.this;
String passphrase = prompt.getText().toString();
@@ -378,26 +379,36 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
database, backup.getFile(), passphrase);
DatabaseFactory.upgradeRestored(context, database);
NotificationChannels.restoreContactNotificationChannels(context);
TextSecurePreferences.setBackupEnabled(context, true);
TextSecurePreferences.setBackupPassphrase(context, passphrase);
return true;
return BackupImportResult.SUCCESS;
} catch (FullBackupImporter.DatabaseDowngradeException e) {
Log.w(TAG, "Failed due to the backup being from a newer version of Signal.", e);
return BackupImportResult.FAILURE_VERSION_DOWNGRADE;
} catch (IOException e) {
Log.w(TAG, e);
return false;
return BackupImportResult.FAILURE_UNKNOWN;
}
}
@Override
protected void onPostExecute(@NonNull Boolean result) {
protected void onPostExecute(@NonNull BackupImportResult result) {
restoreButton.setIndeterminateProgressMode(false);
restoreButton.setProgress(0);
restoreBackupProgress.setText("");
if (result) {
displayInitialView(true);
} else {
Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_incorrect_backup_passphrase, Toast.LENGTH_LONG).show();
switch (result) {
case SUCCESS:
displayInitialView(true);
break;
case FAILURE_VERSION_DOWNGRADE:
Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_backup_failure_downgrade, Toast.LENGTH_LONG).show();
break;
case FAILURE_UNKNOWN:
Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_incorrect_backup_passphrase, Toast.LENGTH_LONG).show();
break;
}
}
}.execute();
@@ -487,8 +498,6 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
return new Pair<>(password, gcmToken);
} catch (IOException e) {
Log.w(TAG, "Error during account registration", e);
createButton.setIndeterminateProgressMode(false);
createButton.setProgress(0);
return null;
}
}
@@ -496,6 +505,8 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
protected void onPostExecute(@Nullable Pair<String, Optional<String>> result) {
if (result == null) {
Toast.makeText(RegistrationActivity.this, R.string.RegistrationActivity_unable_to_connect_to_service, Toast.LENGTH_LONG).show();
createButton.setIndeterminateProgressMode(false);
createButton.setProgress(0);
return;
}
@@ -1113,4 +1124,8 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
this.gcmToken = previous.gcmToken;
}
}
private enum BackupImportResult {
SUCCESS, FAILURE_VERSION_DOWNGRADE, FAILURE_UNKNOWN
}
}

View File

@@ -89,7 +89,6 @@ public class FullBackupImporter extends FullBackupBase {
}
trimEntriesForExpiredMessages(context, db);
restoreNotificationChannels(context);
db.setTransactionSuccessful();
} finally {
@@ -99,7 +98,11 @@ public class FullBackupImporter extends FullBackupBase {
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.FINISHED, count));
}
private static void processVersion(@NonNull SQLiteDatabase db, DatabaseVersion version) {
private static void processVersion(@NonNull SQLiteDatabase db, DatabaseVersion version) throws IOException {
if (version.getVersion() > db.getVersion()) {
throw new DatabaseDowngradeException(db.getVersion(), version.getVersion());
}
db.setVersion(version.getVersion());
}
@@ -190,20 +193,6 @@ public class FullBackupImporter extends FullBackupBase {
}
}
private static void restoreNotificationChannels(@NonNull Context context) {
if (!NotificationChannels.supported()) {
return;
}
RecipientDatabase db = DatabaseFactory.getRecipientDatabase(context);
try (RecipientDatabase.RecipientReader reader = db.getRecipientsWithNotificationChannels()) {
Recipient recipient;
while ((recipient = reader.getNext()) != null) {
NotificationChannels.createChannelFor(context, recipient);
}
}
}
private static class BackupRecordInputStream extends BackupStream {
@@ -343,4 +332,9 @@ public class FullBackupImporter extends FullBackupBase {
}
}
public static class DatabaseDowngradeException extends IOException {
DatabaseDowngradeException(int currentVersion, int backupVersion) {
super("Tried to import a backup with version " + backupVersion + " into a database with version " + currentVersion);
}
}
}

View File

@@ -241,11 +241,13 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
}
}
if (oldVersion < QUOTE_MISSING) {
// Note: This column only being checked due to upgrade issues as described in #8184
if (oldVersion < QUOTE_MISSING && !columnExists(db, "mms", "quote_missing")) {
db.execSQL("ALTER TABLE mms ADD COLUMN quote_missing INTEGER DEFAULT 0");
}
if (oldVersion < NOTIFICATION_CHANNELS) {
// Note: The column only being checked due to upgrade issues as described in #8184
if (oldVersion < NOTIFICATION_CHANNELS && !columnExists(db, "recipient_preferences", "notification_channel")) {
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN notification_channel TEXT DEFAULT NULL");
NotificationChannels.create(context);
@@ -309,5 +311,19 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(statement);
}
private static boolean columnExists(@NonNull SQLiteDatabase db, @NonNull String table, @NonNull String column) {
try (Cursor cursor = db.rawQuery("PRAGMA table_info(" + table + ")", null)) {
int nameColumnIndex = cursor.getColumnIndexOrThrow("name");
while (cursor.moveToNext()) {
String name = cursor.getString(nameColumnIndex);
if (name.equals(column)) {
return true;
}
}
}
return false;
}
}

View File

@@ -240,6 +240,10 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
Uri displayPhotoUri = Uri.withAppendedPath(uri, ContactsContract.Contacts.Photo.DISPLAY_PHOTO);
AssetFileDescriptor fd = context.getContentResolver().openAssetFileDescriptor(displayPhotoUri, "r");
if (fd == null) {
return Optional.absent();
}
return Optional.of(SignalServiceAttachment.newStreamBuilder()
.withStream(fd.createInputStream())
.withContentType("image/*")

View File

@@ -33,6 +33,7 @@ import android.os.AsyncTask;
import android.os.Build;
import android.service.notification.StatusBarNotification;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.text.TextUtils;
import org.thoughtcrime.securesms.logging.Log;
@@ -306,6 +307,7 @@ public class MessageNotifier {
builder.setGroup(NOTIFICATION_GROUP);
builder.setDeleteIntent(notificationState.getDeleteIntent(context));
builder.setOnlyAlertOnce(!signal);
builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY);
long timestamp = notifications.get(0).getTimestamp();
if (timestamp != 0) builder.setWhen(timestamp);
@@ -349,6 +351,7 @@ public class MessageNotifier {
builder.setGroup(NOTIFICATION_GROUP);
builder.setDeleteIntent(notificationState.getDeleteIntent(context));
builder.setOnlyAlertOnce(!signal);
builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY);
long timestamp = notifications.get(0).getTimestamp();
if (timestamp != 0) builder.setWhen(timestamp);
@@ -378,7 +381,10 @@ public class MessageNotifier {
return;
}
Uri uri = recipient != null ? recipient.resolve().getMessageRingtone(context) : null;
Uri uri = null;
if (recipient != null) {
uri = NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context, recipient) : recipient.getMessageRingtone();
}
if (uri == null) {
uri = NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context) : TextSecurePreferences.getNotificationRingtone(context);

View File

@@ -49,6 +49,10 @@ public class MultipleRecipientNotificationBuilder extends AbstractNotificationBu
setContentText(context.getString(R.string.MessageNotifier_most_recent_from_s,
recipient.toShortString()));
}
if (recipient.getNotificationChannel() != null) {
setChannelId(recipient.getNotificationChannel());
}
}
public void addActions(PendingIntent markAsReadIntent) {

View File

@@ -67,6 +67,30 @@ public class NotificationChannels {
onCreate(context, notificationManager);
}
/**
* Recreates all notification channels for contacts with custom notifications enabled. Should be
* safe to call repeatedly. Needs to be executed on a background thread.
*/
@WorkerThread
public static void restoreContactNotificationChannels(@NonNull Context context) {
if (!NotificationChannels.supported()) {
return;
}
RecipientDatabase db = DatabaseFactory.getRecipientDatabase(context);
try (RecipientDatabase.RecipientReader reader = db.getRecipientsWithNotificationChannels()) {
Recipient recipient;
while ((recipient = reader.getNext()) != null) {
NotificationManager notificationManager = getNotificationManager(context);
if (!channelExists(notificationManager.getNotificationChannel(recipient.getNotificationChannel()))) {
String id = createChannelFor(context, recipient);
db.setNotificationChannel(recipient, id);
}
}
}
}
/**
* @return The channel ID for the default messages channel.
*/
@@ -103,7 +127,7 @@ public class NotificationChannels {
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;
Uri messageRingtone = recipient.getMessageRingtone(context) != null ? recipient.getMessageRingtone(context) : getMessageRingtone(context);
Uri messageRingtone = recipient.getMessageRingtone() != null ? recipient.getMessageRingtone() : getMessageRingtone(context);
String displayName = getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.getAddress());
return createChannelFor(context, recipient.getAddress(), displayName, messageRingtone, vibrationEnabled);
@@ -245,14 +269,13 @@ public class NotificationChannels {
}
Log.i(TAG, "Updating recipient message ringtone with URI: " + String.valueOf(uri));
String newChannelId = generateChannelIdFor(recipient.getAddress());
String newChannelId = generateChannelIdFor(recipient.getAddress());
boolean success = updateExistingChannel(getNotificationManager(context),
recipient.getNotificationChannel(),
generateChannelIdFor(recipient.getAddress()),
channel -> channel.setSound(uri == null ? Settings.System.DEFAULT_NOTIFICATION_URI : uri, getRingtoneAudioAttributes()));
updateExistingChannel(getNotificationManager(context),
recipient.getNotificationChannel(),
generateChannelIdFor(recipient.getAddress()),
channel -> channel.setSound(uri == null ? Settings.System.DEFAULT_NOTIFICATION_URI : uri, getRingtoneAudioAttributes()));
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, newChannelId);
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, success ? newChannelId : null);
}
/**
@@ -313,13 +336,12 @@ public class NotificationChannels {
boolean enabled = vibrateState == VibrateState.DEFAULT ? getMessageVibrate(context) : vibrateState == VibrateState.ENABLED;
String newChannelId = generateChannelIdFor(recipient.getAddress());
boolean success = updateExistingChannel(getNotificationManager(context),
recipient.getNotificationChannel(),
newChannelId,
channel -> channel.enableVibration(enabled));
updateExistingChannel(getNotificationManager(context),
recipient.getNotificationChannel(),
newChannelId,
channel -> channel.enableVibration(enabled));
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, newChannelId);
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, success ? newChannelId : null);
}
/**
@@ -437,9 +459,10 @@ public class NotificationChannels {
while ((recipient = recipients.getNext()) != null) {
assert recipient.getNotificationChannel() != null;
String newChannelId = generateChannelIdFor(recipient.getAddress());
updateExistingChannel(notificationManager, recipient.getNotificationChannel(), newChannelId, channel -> setLedPreference(channel, color));
database.setNotificationChannel(recipient, newChannelId);
String newChannelId = generateChannelIdFor(recipient.getAddress());
boolean success = updateExistingChannel(notificationManager, recipient.getNotificationChannel(), newChannelId, channel -> setLedPreference(channel, color));
database.setNotificationChannel(recipient, success ? newChannelId : null);
}
}
}
@@ -451,23 +474,31 @@ public class NotificationChannels {
int newVersion = existingVersion + 1;
Log.i(TAG, "Updating message channel from version " + existingVersion + " to " + newVersion);
updateExistingChannel(notificationManager, getMessagesChannelId(existingVersion), getMessagesChannelId(newVersion), updater);
TextSecurePreferences.setNotificationMessagesChannelVersion(context, newVersion);
if (updateExistingChannel(notificationManager, getMessagesChannelId(existingVersion), getMessagesChannelId(newVersion), updater)) {
TextSecurePreferences.setNotificationMessagesChannelVersion(context, newVersion);
} else {
onCreate(context, notificationManager);
}
}
@TargetApi(26)
private static void updateExistingChannel(@NonNull NotificationManager notificationManager,
@NonNull String channelId,
@NonNull String newChannelId,
@NonNull ChannelUpdater updater)
private static boolean updateExistingChannel(@NonNull NotificationManager notificationManager,
@NonNull String channelId,
@NonNull String newChannelId,
@NonNull ChannelUpdater updater)
{
NotificationChannel existingChannel = notificationManager.getNotificationChannel(channelId);
if (existingChannel == null) {
Log.w(TAG, "Tried to update a channel, but it didn't exist.");
return false;
}
notificationManager.deleteNotificationChannel(existingChannel.getId());
NotificationChannel newChannel = copyChannel(existingChannel, newChannelId);
updater.update(newChannel);
notificationManager.createNotificationChannel(newChannel);
return true;
}
@TargetApi(21)

View File

@@ -50,7 +50,8 @@ public class NotificationState {
Recipient recipient = notifications.getFirst().getRecipient();
if (recipient != null) {
return recipient.resolve().getMessageRingtone(context);
return NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context, recipient)
: recipient.resolve().getMessageRingtone();
}
}

View File

@@ -463,15 +463,11 @@ public class Recipient implements RecipientModifiedListener {
if (notify) notifyListeners();
}
public synchronized @Nullable Uri getMessageRingtone(@NonNull Context context) {
public synchronized @Nullable Uri getMessageRingtone() {
if (messageRingtone != null && messageRingtone.getScheme() != null && messageRingtone.getScheme().startsWith("file")) {
return null;
}
if (NotificationChannels.supported()) {
return NotificationChannels.getMessageRingtone(context, this);
}
return messageRingtone;
}