From 141cab1105a300af3aabcc19da9048800fc6a4ce Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 11 Jan 2021 22:33:11 -0500 Subject: [PATCH] Perfom a migration to notify users of new contacts. --- .../securesms/database/ThreadDatabase.java | 15 ++ .../securesms/jobs/JobManagerFactories.java | 2 + .../migrations/ApplicationMigrations.java | 7 +- .../UserNotificationMigrationJob.java | 129 ++++++++++++++++++ .../notifications/NotificationIds.java | 15 +- app/src/main/res/values/strings.xml | 5 + 6 files changed, 165 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/migrations/UserNotificationMigrationJob.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java index 036a71ee7f..f05582e221 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -1196,6 +1196,21 @@ public class ThreadDatabase extends Database { return Objects.requireNonNull(getThreadRecord(getThreadIdFor(recipient))); } + public @NonNull Set getAllThreadRecipients() { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Set ids = new HashSet<>(); + + + try (Cursor cursor = db.query(TABLE_NAME, new String[] { RECIPIENT_ID }, null, null, null, null, null)) { + while (cursor.moveToNext()) { + ids.add(RecipientId.from(CursorUtil.requireString(cursor, RECIPIENT_ID))); + } + } + + return ids; + } + + @NonNull MergeResult merge(@NonNull RecipientId primaryRecipientId, @NonNull RecipientId secondaryRecipientId) { if (!databaseHelper.getWritableDatabase().inTransaction()) { throw new IllegalStateException("Must be in a transaction!"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index cae2b3a41e..2585a02a33 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.migrations.StickerLaunchMigrationJob; import org.thoughtcrime.securesms.migrations.StorageCapabilityMigrationJob; import org.thoughtcrime.securesms.migrations.StorageServiceMigrationJob; import org.thoughtcrime.securesms.migrations.TrimByLengthSettingsMigrationJob; +import org.thoughtcrime.securesms.migrations.UserNotificationMigrationJob; import org.thoughtcrime.securesms.migrations.UuidMigrationJob; import java.util.Arrays; @@ -160,6 +161,7 @@ public final class JobManagerFactories { put(StorageCapabilityMigrationJob.KEY, new StorageCapabilityMigrationJob.Factory()); put(StorageServiceMigrationJob.KEY, new StorageServiceMigrationJob.Factory()); put(TrimByLengthSettingsMigrationJob.KEY, new TrimByLengthSettingsMigrationJob.Factory()); + put(UserNotificationMigrationJob.KEY, new UserNotificationMigrationJob.Factory()); put(UuidMigrationJob.KEY, new UuidMigrationJob.Factory()); // Dead jobs diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java index 4668aad8a5..2756343e12 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -40,7 +40,7 @@ public class ApplicationMigrations { private static final int LEGACY_CANONICAL_VERSION = 455; - public static final int CURRENT_VERSION = 24; + public static final int CURRENT_VERSION = 25; private static final class Version { static final int LEGACY = 1; @@ -67,6 +67,7 @@ public class ApplicationMigrations { static final int CDS = 22; static final int BACKUP_NOTIFICATION = 23; static final int GV1_MIGRATION = 24; + static final int USER_NOTIFICATION = 25; } /** @@ -281,6 +282,10 @@ public class ApplicationMigrations { jobs.put(Version.GV1_MIGRATION, new AttributesMigrationJob()); } + if (lastSeenVersion < Version.USER_NOTIFICATION) { + jobs.put(Version.USER_NOTIFICATION, new UserNotificationMigrationJob()); + } + return jobs; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/UserNotificationMigrationJob.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/UserNotificationMigrationJob.java new file mode 100644 index 0000000000..ed2d795687 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/UserNotificationMigrationJob.java @@ -0,0 +1,129 @@ +package org.thoughtcrime.securesms.migrations; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Intent; + +import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; +import androidx.core.app.TaskStackBuilder; + +import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.MainActivity; +import org.thoughtcrime.securesms.NewConversationActivity; +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.jobmanager.Data; +import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.notifications.NotificationChannels; +import org.thoughtcrime.securesms.notifications.NotificationIds; +import org.thoughtcrime.securesms.recipients.RecipientId; +import org.thoughtcrime.securesms.util.SetUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; + +import java.util.List; +import java.util.Set; + +/** + * Show a user that contacts are newly available. Only for users that recently installed. + */ +public class UserNotificationMigrationJob extends MigrationJob { + + private static final String TAG = Log.tag(UserNotificationMigrationJob.class); + + public static final String KEY = "UserNotificationMigration"; + + UserNotificationMigrationJob() { + this(new Parameters.Builder().build()); + } + + private UserNotificationMigrationJob(Parameters parameters) { + super(parameters); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @Override + boolean isUiBlocking() { + return false; + } + + @Override + void performMigration() { + if (!TextSecurePreferences.isPushRegistered(context) || + TextSecurePreferences.getLocalNumber(context) == null || + TextSecurePreferences.getLocalUuid(context) == null) + { + Log.w(TAG, "Not registered! Skipping."); + return; + } + + if (!TextSecurePreferences.isNewContactsNotificationEnabled(context)) { + Log.w(TAG, "New contact notifications disabled! Skipping."); + return; + } + + if (TextSecurePreferences.getFirstInstallVersion(context) < 759) { + Log.w(TAG, "Install is older than v5.0.8. Skipping."); + return; + } + + ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); + + int threadCount = threadDatabase.getUnarchivedConversationListCount() + + threadDatabase.getArchivedConversationListCount(); + + if (threadCount >= 3) { + Log.w(TAG, "Already have 3 or more threads. Skipping."); + return; + } + + List registered = DatabaseFactory.getRecipientDatabase(context).getRegistered(); + List systemContacts = DatabaseFactory.getRecipientDatabase(context).getSystemContacts(); + Set registeredSystemContacts = SetUtil.intersection(registered, systemContacts); + Set threadRecipients = threadDatabase.getAllThreadRecipients(); + + if (threadRecipients.containsAll(registeredSystemContacts)) { + Log.w(TAG, "Threads already exist for all relevant contacts. Skipping."); + return; + } + + String message = context.getResources().getQuantityString(R.plurals.UserNotificationMigrationJob_d_contacts_are_on_signal, + registeredSystemContacts.size(), + registeredSystemContacts.size()); + + Intent mainActivityIntent = new Intent(context, MainActivity.class); + Intent newConversationIntent = new Intent(context, NewConversationActivity.class); + PendingIntent pendingIntent = TaskStackBuilder.create(context) + .addNextIntent(mainActivityIntent) + .addNextIntent(newConversationIntent) + .getPendingIntent(0, 0); + + Notification notification = new NotificationCompat.Builder(context, NotificationChannels.getMessagesChannel(context)) + .setSmallIcon(R.drawable.ic_notification) + .setContentText(message) + .setContentIntent(pendingIntent) + .build(); + + NotificationManagerCompat.from(context) + .notify(NotificationIds.USER_NOTIFICATION_MIGRATION, notification); + } + + @Override + boolean shouldRetry(@NonNull Exception e) { + return false; + } + + public static final class Factory implements Job.Factory { + + @Override + public @NonNull UserNotificationMigrationJob create(@NonNull Parameters parameters, @NonNull Data data) { + return new UserNotificationMigrationJob(parameters); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java index 2488587e42..cafa860843 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java @@ -2,13 +2,14 @@ package org.thoughtcrime.securesms.notifications; public final class NotificationIds { - public static final int FCM_FAILURE = 12; - public static final int PENDING_MESSAGES = 1111; - public static final int MESSAGE_SUMMARY = 1338; - public static final int APPLICATION_MIGRATION = 4242; - public static final int SMS_IMPORT_COMPLETE = 31337; - public static final int PRE_REGISTRATION_SMS = 5050; - public static final int THREAD = 50000; + public static final int FCM_FAILURE = 12; + public static final int PENDING_MESSAGES = 1111; + public static final int MESSAGE_SUMMARY = 1338; + public static final int APPLICATION_MIGRATION = 4242; + public static final int SMS_IMPORT_COMPLETE = 31337; + public static final int PRE_REGISTRATION_SMS = 5050; + public static final int THREAD = 50000; + public static final int USER_NOTIFICATION_MIGRATION = 525600; private NotificationIds() { } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4328bcc708..46512c27bd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1687,6 +1687,11 @@ Usernames must be between %1$d and %2$d characters. Usernames on Signal are optional. If you choose to create a username, other Signal users will be able to find you by this username and contact you without knowing your phone number. + + %d contact is on Signal! + %d contacts are on Signal! + + Your contact is running an old version of Signal. Please ask them to update before verifying your safety number. Your contact is running a newer version of Signal with an incompatible QR code format. Please update to compare.