diff --git a/src/org/thoughtcrime/securesms/GroupCreateActivity.java b/src/org/thoughtcrime/securesms/GroupCreateActivity.java index 2dea4f85f5..9d16504ae7 100644 --- a/src/org/thoughtcrime/securesms/GroupCreateActivity.java +++ b/src/org/thoughtcrime/securesms/GroupCreateActivity.java @@ -54,8 +54,7 @@ import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; -import org.thoughtcrime.securesms.database.NotInDirectoryException; -import org.thoughtcrime.securesms.database.TextSecureDirectory; +import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.groups.GroupManager; import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult; @@ -170,11 +169,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity } private static boolean isActiveInDirectory(Context context, Recipient recipient) { - try { - return TextSecureDirectory.getInstance(context).isSecureTextSupported(recipient.getAddress()); - } catch (NotInDirectoryException e) { - return false; - } + Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipient.getAddress()); + return preferences.isPresent() && preferences.get().isRegistered(); } private void addSelectedContacts(@NonNull Recipient... recipients) { diff --git a/src/org/thoughtcrime/securesms/contacts/ContactAccessor.java b/src/org/thoughtcrime/securesms/contacts/ContactAccessor.java index 098b9bc68e..2c6480432a 100644 --- a/src/org/thoughtcrime/securesms/contacts/ContactAccessor.java +++ b/src/org/thoughtcrime/securesms/contacts/ContactAccessor.java @@ -27,15 +27,18 @@ import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.PhoneLookup; import android.telephony.PhoneNumberUtils; +import android.text.TextUtils; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.TextSecureDirectory; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; @@ -61,6 +64,20 @@ public class ContactAccessor { return instance; } + public Set
getAllContactsWithNumbers(Context context) { + Set
results = new HashSet<>(); + + try (Cursor cursor = context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER}, null ,null, null)) { + while (cursor != null && cursor.moveToNext()) { + if (!TextUtils.isEmpty(cursor.getString(0))) { + results.add(Address.fromExternal(context, cursor.getString(0))); + } + } + } + + return results; + } + public boolean isSystemContact(Context context, String number) { Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); String[] projection = new String[]{PhoneLookup.DISPLAY_NAME, PhoneLookup.LOOKUP_KEY, @@ -82,16 +99,17 @@ public class ContactAccessor { final ContentResolver resolver = context.getContentResolver(); final String[] inProjection = new String[]{PhoneLookup._ID, PhoneLookup.DISPLAY_NAME}; - List pushNumbers = TextSecureDirectory.getInstance(context).getActiveNumbers(); - final Collection lookupData = new ArrayList<>(pushNumbers.size()); + final List
registeredAddresses = DatabaseFactory.getRecipientPreferenceDatabase(context).getRegistered(); + final Collection lookupData = new ArrayList<>(registeredAddresses.size()); - for (String pushNumber : pushNumbers) { - Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(pushNumber)); + for (Address registeredAddress : registeredAddresses) { + Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(registeredAddress.serialize())); Cursor lookupCursor = resolver.query(uri, inProjection, null, null, null); + try { if (lookupCursor != null && lookupCursor.moveToFirst()) { final ContactData contactData = new ContactData(lookupCursor.getLong(0), lookupCursor.getString(1)); - contactData.numbers.add(new NumberData("TextSecure", pushNumber)); + contactData.numbers.add(new NumberData("TextSecure", registeredAddress.serialize())); lookupData.add(contactData); } } finally { @@ -99,6 +117,7 @@ public class ContactAccessor { lookupCursor.close(); } } + return lookupData; } diff --git a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java index 0a02af005d..04720986d0 100644 --- a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java +++ b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java @@ -101,7 +101,8 @@ public class DatabaseFactory { private static final int SANIFY_ATTACHMENT_DOWNLOAD = 36; private static final int NO_MORE_CANONICAL_ADDRESS_DATABASE = 37; private static final int NO_MORE_RECIPIENTS_PLURAL = 38; - private static final int DATABASE_VERSION = 38; + private static final int INTERNAL_DIRECTORY = 39; + private static final int DATABASE_VERSION = 39; private static final String DATABASE_NAME = "messages.db"; private static final Object lock = new Object(); @@ -1273,6 +1274,24 @@ public class DatabaseFactory { if (cursor != null) cursor.close(); } + if (oldVersion < INTERNAL_DIRECTORY) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN registered INTEGER DEFAULT 0"); + + DatabaseHelper directoryDatabaseHelper = new DatabaseHelper(context, "whisper_directory.db", null, 5); + SQLiteDatabase directoryDatabase = directoryDatabaseHelper.getReadableDatabase(); + + Cursor cursor = directoryDatabase.query("directory", new String[] {"number", "registered"}, null, null, null, null, null); + + while (cursor != null && cursor.moveToNext()) { + String address = new NumberMigrator(TextSecurePreferences.getLocalNumber(context)).migrate(cursor.getString(0)); + ContentValues contentValues = new ContentValues(1); + + contentValues.put("recipient_ids", address); + contentValues.put("registered", cursor.getInt(1) == 1); + db.replace("recipient_preferences", null, contentValues); + } + } + db.setTransactionSuccessful(); db.endTransaction(); } diff --git a/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java b/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java index 77f33bf154..ebf231d32a 100644 --- a/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java +++ b/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java @@ -18,7 +18,10 @@ import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.whispersystems.libsignal.util.guava.Optional; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; +import java.util.Set; public class RecipientPreferenceDatabase extends Database { @@ -36,9 +39,10 @@ public class RecipientPreferenceDatabase extends Database { private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder"; private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id"; private static final String EXPIRE_MESSAGES = "expire_messages"; + private static final String REGISTERED = "registered"; private static final String[] RECIPIENT_PROJECTION = new String[] { - BLOCK, NOTIFICATION, VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES + BLOCK, NOTIFICATION, VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED }; static final List TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION) @@ -74,7 +78,8 @@ public class RecipientPreferenceDatabase extends Database { COLOR + " TEXT DEFAULT NULL, " + SEEN_INVITE_REMINDER + " INTEGER DEFAULT 0, " + DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " + - EXPIRE_MESSAGES + " INTEGER DEFAULT 0);"; + EXPIRE_MESSAGES + " INTEGER DEFAULT 0, " + + REGISTERED + " INTEGER DEFAULT 0);"; public RecipientPreferenceDatabase(Context context, SQLiteOpenHelper databaseHelper) { super(context, databaseHelper); @@ -122,6 +127,7 @@ public class RecipientPreferenceDatabase extends Database { boolean seenInviteReminder = cursor.getInt(cursor.getColumnIndexOrThrow(SEEN_INVITE_REMINDER)) == 1; int defaultSubscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(DEFAULT_SUBSCRIPTION_ID)); int expireMessages = cursor.getInt(cursor.getColumnIndexOrThrow(EXPIRE_MESSAGES)); + boolean registered = cursor.getInt(cursor.getColumnIndexOrThrow(REGISTERED)) == 1; MaterialColor color; @@ -135,7 +141,7 @@ public class RecipientPreferenceDatabase extends Database { return new RecipientsPreferences(blocked, muteUntil, VibrateState.fromId(vibrateState), notificationUri, color, seenInviteReminder, - defaultSubscriptionId, expireMessages); + defaultSubscriptionId, expireMessages, registered); } public void setColor(Recipient recipient, MaterialColor color) { @@ -190,6 +196,56 @@ public class RecipientPreferenceDatabase extends Database { updateOrInsert(recipient, values); } + public Set
getAllRecipients() { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Set
results = new HashSet<>(); + + try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, null, null, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + results.add(Address.fromExternal(context, cursor.getString(0))); + } + } + + return results; + } + + public void setRegistered(@NonNull List
activeAddresses, + @NonNull List
inactiveAddresses) + { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + + for (Address activeAddress : activeAddresses) { + ContentValues contentValues = new ContentValues(2); + contentValues.put(ADDRESS, activeAddress.serialize()); + contentValues.put(REGISTERED, 1); + + db.replace(TABLE_NAME, null, contentValues); + } + + for (Address inactiveAddress : inactiveAddresses) { + ContentValues contentValues = new ContentValues(2); + contentValues.put(ADDRESS, inactiveAddress.serialize()); + contentValues.put(REGISTERED, 0); + + db.replace(TABLE_NAME, null, contentValues); + } + + context.getContentResolver().notifyChange(Uri.parse(RECIPIENT_PREFERENCES_URI), null); + } + + public List
getRegistered() { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + List
results = new LinkedList<>(); + + try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, REGISTERED + " = ?", new String[] {"1"}, null, null, null)) { + while (cursor != null && cursor.moveToNext()) { + results.add(Address.fromSerialized(cursor.getString(0))); + } + } + + return results; + } + private void updateOrInsert(Recipient recipient, ContentValues contentValues) { SQLiteDatabase database = databaseHelper.getWritableDatabase(); @@ -218,6 +274,7 @@ public class RecipientPreferenceDatabase extends Database { private final boolean seenInviteReminder; private final int defaultSubscriptionId; private final int expireMessages; + private final boolean registered; RecipientsPreferences(boolean blocked, long muteUntil, @NonNull VibrateState vibrateState, @@ -225,7 +282,8 @@ public class RecipientPreferenceDatabase extends Database { @Nullable MaterialColor color, boolean seenInviteReminder, int defaultSubscriptionId, - int expireMessages) + int expireMessages, + boolean registered) { this.blocked = blocked; this.muteUntil = muteUntil; @@ -235,6 +293,7 @@ public class RecipientPreferenceDatabase extends Database { this.seenInviteReminder = seenInviteReminder; this.defaultSubscriptionId = defaultSubscriptionId; this.expireMessages = expireMessages; + this.registered = registered; } public @Nullable MaterialColor getColor() { @@ -268,6 +327,10 @@ public class RecipientPreferenceDatabase extends Database { public int getExpireMessages() { return expireMessages; } + + public boolean isRegistered() { + return registered; + } } public static class BlockedReader { diff --git a/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java b/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java deleted file mode 100644 index 6ba71f17c6..0000000000 --- a/src/org/thoughtcrime/securesms/database/TextSecureDirectory.java +++ /dev/null @@ -1,295 +0,0 @@ -package org.thoughtcrime.securesms.database; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.net.Uri; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.support.annotation.NonNull; -import android.text.TextUtils; -import android.util.Log; - -import org.whispersystems.signalservice.api.push.ContactTokenDetails; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class TextSecureDirectory { - - private static final int INTRODUCED_CHANGE_FROM_TOKEN_TO_E164_NUMBER = 2; - private static final int INTRODUCED_VOICE_COLUMN = 4; - private static final int INTRODUCED_VIDEO_COLUMN = 5; - - private static final String DATABASE_NAME = "whisper_directory.db"; - private static final int DATABASE_VERSION = 5; - - private static final String TABLE_NAME = "directory"; - private static final String ID = "_id"; - private static final String NUMBER = "number"; - private static final String REGISTERED = "registered"; - private static final String RELAY = "relay"; - private static final String TIMESTAMP = "timestamp"; - private static final String VOICE = "voice"; - private static final String VIDEO = "video"; - - private static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + ID + " INTEGER PRIMARY KEY, " + - NUMBER + " TEXT UNIQUE, " + - REGISTERED + " INTEGER, " + - RELAY + " TEXT, " + - TIMESTAMP + " INTEGER, " + - VOICE + " INTEGER, " + - VIDEO + " INTEGER);"; - - private static final Object instanceLock = new Object(); - private static volatile TextSecureDirectory instance; - - public static TextSecureDirectory getInstance(Context context) { - if (instance == null) { - synchronized (instanceLock) { - if (instance == null) { - instance = new TextSecureDirectory(context.getApplicationContext()); - } - } - } - - return instance; - } - - private final DatabaseHelper databaseHelper; - private final Context context; - - private TextSecureDirectory(Context context) { - this.context = context; - this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - public boolean isSecureTextSupported(@NonNull Address address) throws NotInDirectoryException { - if (address.isEmail()) return false; - if (address.isGroup()) return true; - - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - - try { - cursor = db.query(TABLE_NAME, - new String[]{REGISTERED}, NUMBER + " = ?", - new String[] {address.serialize()}, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - return cursor.getInt(0) == 1; - } else { - throw new NotInDirectoryException(); - } - - } finally { - if (cursor != null) - cursor.close(); - } - } - -// public boolean isSecureVoiceSupported(String e164number) throws NotInDirectoryException { -// if (TextUtils.isEmpty(e164number)) { -// return false; -// } -// -// SQLiteDatabase db = databaseHelper.getReadableDatabase(); -// Cursor cursor = null; -// -// try { -// cursor = db.query(TABLE_NAME, -// new String[]{VOICE}, NUMBER + " = ?", -// new String[] {e164number}, null, null, null); -// -// if (cursor != null && cursor.moveToFirst()) { -// return cursor.getInt(0) == 1; -// } else { -// throw new NotInDirectoryException(); -// } -// -// } finally { -// if (cursor != null) -// cursor.close(); -// } -// } - -// public boolean isSecureVideoSupported(String e164number) throws NotInDirectoryException { -// if (TextUtils.isEmpty(e164number)) { -// return false; -// } -// -// SQLiteDatabase db = databaseHelper.getReadableDatabase(); -// Cursor cursor = null; -// -// try { -// cursor = db.query(TABLE_NAME, -// new String[]{VIDEO}, NUMBER + " = ?", -// new String[] {e164number}, null, null, null); -// -// if (cursor != null && cursor.moveToFirst()) { -// return cursor.getInt(0) == 1; -// } else { -// throw new NotInDirectoryException(); -// } -// -// } finally { -// if (cursor != null) -// cursor.close(); -// } -// } - - public String getRelay(String e164number) { - SQLiteDatabase database = databaseHelper.getReadableDatabase(); - Cursor cursor = null; - - try { - cursor = database.query(TABLE_NAME, null, NUMBER + " = ?", new String[]{e164number}, null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - return cursor.getString(cursor.getColumnIndexOrThrow(RELAY)); - } - - return null; - } finally { - if (cursor != null) - cursor.close(); - } - } - - public void setNumber(ContactTokenDetails token, boolean active) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - ContentValues values = new ContentValues(); - values.put(NUMBER, token.getNumber()); - values.put(REGISTERED, active ? 1 : 0); - values.put(TIMESTAMP, System.currentTimeMillis()); - values.put(RELAY, token.getRelay()); - values.put(VOICE, token.isVoice()); - values.put(VIDEO, token.isVideo()); - db.replace(TABLE_NAME, null, values); - } - - public void setNumbers(List activeTokens, Collection
inactiveAddresses) { - long timestamp = System.currentTimeMillis(); - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.beginTransaction(); - - try { - for (ContactTokenDetails token : activeTokens) { - Log.w("Directory", "Adding active token: " + token.getNumber() + ", " + token.getToken() + ", video: " + token.isVideo()); - ContentValues values = new ContentValues(); - values.put(NUMBER, token.getNumber()); - values.put(REGISTERED, 1); - values.put(TIMESTAMP, timestamp); - values.put(RELAY, token.getRelay()); - values.put(VOICE, token.isVoice()); - values.put(VIDEO, token.isVideo()); - db.replace(TABLE_NAME, null, values); - } - - for (Address address : inactiveAddresses) { - ContentValues values = new ContentValues(); - values.put(NUMBER, address.serialize()); - values.put(REGISTERED, 0); - values.put(TIMESTAMP, timestamp); - db.replace(TABLE_NAME, null, values); - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - - public Set
getPushEligibleContactNumbers() { - final Uri uri = Phone.CONTENT_URI; - final Set
results = new HashSet<>(); - Cursor cursor = null; - - try { - cursor = context.getContentResolver().query(uri, new String[] {Phone.NUMBER}, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - final String rawNumber = cursor.getString(0); - if (!TextUtils.isEmpty(rawNumber)) { - results.add(Address.fromExternal(context, rawNumber)); - } - } - - if (cursor != null) - cursor.close(); - - final SQLiteDatabase readableDb = databaseHelper.getReadableDatabase(); - if (readableDb != null) { - cursor = readableDb.query(TABLE_NAME, new String[]{NUMBER}, - null, null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - results.add(Address.fromSerialized(cursor.getString(0))); - } - } - - return results; - } finally { - if (cursor != null) - cursor.close(); - } - } - - public List getActiveNumbers() { - final List results = new ArrayList<>(); - Cursor cursor = null; - try { - cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[]{NUMBER}, - REGISTERED + " = 1", null, null, null, null); - - while (cursor != null && cursor.moveToNext()) { - results.add(cursor.getString(0)); - } - return results; - } finally { - if (cursor != null) - cursor.close(); - } - } - - private static class DatabaseHelper extends SQLiteOpenHelper { - - public DatabaseHelper(Context context, String name, - SQLiteDatabase.CursorFactory factory, - int version) - { - super(context, name, factory, version); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL(CREATE_TABLE); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion < INTRODUCED_CHANGE_FROM_TOKEN_TO_E164_NUMBER) { - db.execSQL("DROP TABLE directory;"); - db.execSQL("CREATE TABLE directory ( _id INTEGER PRIMARY KEY, " + - "number TEXT UNIQUE, " + - "registered INTEGER, " + - "relay TEXT, " + - "supports_sms INTEGER, " + - "timestamp INTEGER);"); - } - - if (oldVersion < INTRODUCED_VOICE_COLUMN) { - db.execSQL("ALTER TABLE directory ADD COLUMN voice INTEGER;"); - } - - if (oldVersion < INTRODUCED_VIDEO_COLUMN) { - db.execSQL("ALTER TABLE directory ADD COLUMN video INTEGER;"); - } - } - } - -} diff --git a/src/org/thoughtcrime/securesms/jobs/PushContentReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/PushContentReceiveJob.java index f1ef2fefb5..11e41c86ee 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushContentReceiveJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushContentReceiveJob.java @@ -3,16 +3,10 @@ package org.thoughtcrime.securesms.jobs; import android.content.Context; import android.util.Log; -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.jobqueue.JobManager; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.libsignal.InvalidVersionException; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.thoughtcrime.securesms.database.TextSecureDirectory; -import org.thoughtcrime.securesms.database.NotInDirectoryException; -import org.whispersystems.signalservice.api.push.ContactTokenDetails; import java.io.IOException; diff --git a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java index 3af1212491..8ab37f6403 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java @@ -7,15 +7,17 @@ import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; -import org.thoughtcrime.securesms.database.NotInDirectoryException; -import org.thoughtcrime.securesms.database.TextSecureDirectory; +import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.service.KeyCachingService; +import org.thoughtcrime.securesms.util.Util; import org.whispersystems.jobqueue.JobManager; import org.whispersystems.jobqueue.JobParameters; +import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.push.ContactTokenDetails; + +import java.util.LinkedList; public abstract class PushReceivedJob extends ContextJob { @@ -29,12 +31,7 @@ public abstract class PushReceivedJob extends ContextJob { Address source = Address.fromExternal(context, envelope.getSource()); if (!isActiveNumber(context, source)) { - TextSecureDirectory directory = TextSecureDirectory.getInstance(context); - ContactTokenDetails contactTokenDetails = new ContactTokenDetails(); - contactTokenDetails.setNumber(envelope.getSource()); - - directory.setNumber(contactTokenDetails, true); - + DatabaseFactory.getRecipientPreferenceDatabase(context).setRegistered(Util.asList(source), new LinkedList<>()); Recipient recipient = RecipientFactory.getRecipientFor(context, source, false); ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, KeyCachingService.getMasterSecret(context), recipient)); } @@ -73,15 +70,8 @@ public abstract class PushReceivedJob extends ContextJob { } private boolean isActiveNumber(Context context, Address address) { - boolean isActiveNumber; - - try { - isActiveNumber = TextSecureDirectory.getInstance(context).isSecureTextSupported(address); - } catch (NotInDirectoryException e) { - isActiveNumber = false; - } - - return isActiveNumber; + Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(address); + return preferences.isPresent() && preferences.get().isRegistered(); } diff --git a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java index f17baceba6..c709155c45 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -10,7 +10,6 @@ import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.TextSecureDirectory; import org.thoughtcrime.securesms.events.PartProgressEvent; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.mms.PartAuthority; @@ -62,7 +61,8 @@ public abstract class PushSendJob extends SendJob { } protected SignalServiceAddress getPushAddress(Address address) { - String relay = TextSecureDirectory.getInstance(context).getRelay(address.toPhoneString()); +// String relay = TextSecureDirectory.getInstance(context).getRelay(address.toPhoneString()); + String relay = null; return new SignalServiceAddress(address.toPhoneString(), Optional.fromNullable(relay)); } diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java index 4d0fe5decf..672b278425 100644 --- a/src/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java @@ -27,9 +27,9 @@ import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.EncryptingSmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.NotInDirectoryException; +import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase; +import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.TextSecureDirectory; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.jobs.MmsSendJob; @@ -50,6 +50,7 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.push.ContactTokenDetails; import java.io.IOException; +import java.util.LinkedList; public class MessageSender { @@ -264,23 +265,21 @@ public class MessageSender { } private static boolean isPushDestination(Context context, Address destination) { - TextSecureDirectory directory = TextSecureDirectory.getInstance(context); + RecipientPreferenceDatabase recipientsDatabase = DatabaseFactory.getRecipientPreferenceDatabase(context); + Optional recipientPreferences = recipientsDatabase.getRecipientsPreferences(destination); - try { - return directory.isSecureTextSupported(destination); - } catch (NotInDirectoryException e) { + if (recipientPreferences.isPresent()) { + return recipientPreferences.get().isRegistered(); + } else { try { SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); Optional registeredUser = accountManager.getContact(destination.serialize()); if (!registeredUser.isPresent()) { - registeredUser = Optional.of(new ContactTokenDetails()); - registeredUser.get().setNumber(destination.serialize()); - directory.setNumber(registeredUser.get(), false); + recipientsDatabase.setRegistered(new LinkedList<>(), Util.asList(destination)); return false; } else { - registeredUser.get().setNumber(destination.toPhoneString()); - directory.setNumber(registeredUser.get(), true); + recipientsDatabase.setRegistered(Util.asList(destination), new LinkedList<>()); return true; } } catch (IOException e1) { diff --git a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java index 56de76eeee..4eba1e216e 100644 --- a/src/org/thoughtcrime/securesms/util/DirectoryHelper.java +++ b/src/org/thoughtcrime/securesms/util/DirectoryHelper.java @@ -12,15 +12,19 @@ import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; +import com.annimon.stream.Collectors; +import com.annimon.stream.Stream; + import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.contacts.ContactAccessor; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.SessionUtil; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; -import org.thoughtcrime.securesms.database.NotInDirectoryException; -import org.thoughtcrime.securesms.database.TextSecureDirectory; +import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase; +import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.push.AccountManagerFactory; @@ -44,6 +48,7 @@ public class DirectoryHelper { public static final UserCapabilities UNKNOWN = new UserCapabilities(Capability.UNKNOWN, Capability.UNKNOWN, Capability.UNKNOWN); public static final UserCapabilities UNSUPPORTED = new UserCapabilities(Capability.UNSUPPORTED, Capability.UNSUPPORTED, Capability.UNSUPPORTED); + public static final UserCapabilities SUPPORTED = new UserCapabilities(Capability.SUPPORTED, Capability.SUPPORTED, Capability.SUPPORTED); public enum Capability { UNKNOWN, SUPPORTED, UNSUPPORTED @@ -96,29 +101,31 @@ public class DirectoryHelper { throws IOException { if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) { - return new RefreshResult(new LinkedList
(), false); + return new RefreshResult(new LinkedList<>(), false); } - TextSecureDirectory directory = TextSecureDirectory.getInstance(context); - Set
eligibleContactNumbers = directory.getPushEligibleContactNumbers(); - Set serializedAddresses = new HashSet<>(); + RecipientPreferenceDatabase recipientPreferenceDatabase = DatabaseFactory.getRecipientPreferenceDatabase(context); + Set
eligibleContactNumbers = recipientPreferenceDatabase.getAllRecipients(); + eligibleContactNumbers.addAll(ContactAccessor.getInstance().getAllContactsWithNumbers(context)); - for (Address address : eligibleContactNumbers) { - serializedAddresses.add(address.serialize()); - } - - List activeTokens = accountManager.getContacts(serializedAddresses); + Set serializedAddress = Stream.of(eligibleContactNumbers).map(Address::serialize).collect(Collectors.toSet()); + List activeTokens = accountManager.getContacts(serializedAddress); if (activeTokens != null) { + List
activeAddresses = new LinkedList<>(); + Set
inactiveAddresses = new HashSet<>(eligibleContactNumbers); + for (ContactTokenDetails activeToken : activeTokens) { - eligibleContactNumbers.remove(Address.fromSerialized(activeToken.getNumber())); + Address activeAddress = Address.fromSerialized(activeToken.getNumber()); + activeAddresses.add(activeAddress); + inactiveAddresses.remove(activeAddress); } - directory.setNumbers(activeTokens, eligibleContactNumbers); + recipientPreferenceDatabase.setRegistered(activeAddresses, new LinkedList<>(inactiveAddresses)); return updateContactsDatabase(context, activeTokens, true); } - return new RefreshResult(new LinkedList
(), false); + return new RefreshResult(new LinkedList<>(), false); } public static UserCapabilities refreshDirectoryFor(@NonNull Context context, @@ -126,13 +133,13 @@ public class DirectoryHelper { @NonNull Recipient recipient) throws IOException { - TextSecureDirectory directory = TextSecureDirectory.getInstance(context); - SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); - String number = recipient.getAddress().serialize(); - Optional details = accountManager.getContact(number); + RecipientPreferenceDatabase recipientDatabase = DatabaseFactory.getRecipientPreferenceDatabase(context); + SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context); + String number = recipient.getAddress().serialize(); + Optional details = accountManager.getContact(number); if (details.isPresent()) { - directory.setNumber(details.get(), true); + recipientDatabase.setRegistered(Util.asList(recipient.getAddress()), new LinkedList<>()); RefreshResult result = updateContactsDatabase(context, details.get()); @@ -148,9 +155,7 @@ public class DirectoryHelper { details.get().isVoice() ? Capability.SUPPORTED : Capability.UNSUPPORTED, details.get().isVideo() ? Capability.SUPPORTED : Capability.UNSUPPORTED); } else { - ContactTokenDetails absent = new ContactTokenDetails(); - absent.setNumber(number); - directory.setNumber(absent, false); + recipientDatabase.setRegistered(new LinkedList<>(), Util.asList(recipient.getAddress())); return UserCapabilities.UNSUPPORTED; } } @@ -158,34 +163,28 @@ public class DirectoryHelper { public static @NonNull UserCapabilities getUserCapabilities(@NonNull Context context, @Nullable Recipient recipient) { - try { - if (recipient == null) { - return UserCapabilities.UNSUPPORTED; - } - - if (!TextSecurePreferences.isPushRegistered(context)) { - return UserCapabilities.UNSUPPORTED; - } - - if (recipient.isMmsGroupRecipient()) { - return UserCapabilities.UNSUPPORTED; - } - - if (recipient.isPushGroupRecipient()) { - return new UserCapabilities(Capability.SUPPORTED, Capability.UNSUPPORTED, Capability.UNSUPPORTED); - } - - final Address address = recipient.getAddress(); - - boolean secureText = TextSecureDirectory.getInstance(context).isSecureTextSupported(address); - - return new UserCapabilities(secureText ? Capability.SUPPORTED : Capability.UNSUPPORTED, - secureText ? Capability.SUPPORTED : Capability.UNSUPPORTED, - secureText ? Capability.SUPPORTED : Capability.UNSUPPORTED); - - } catch (NotInDirectoryException e) { - return UserCapabilities.UNKNOWN; + if (recipient == null) { + return UserCapabilities.UNSUPPORTED; } + + if (!TextSecurePreferences.isPushRegistered(context)) { + return UserCapabilities.UNSUPPORTED; + } + + if (recipient.isMmsGroupRecipient()) { + return UserCapabilities.UNSUPPORTED; + } + + if (recipient.isPushGroupRecipient()) { + return new UserCapabilities(Capability.SUPPORTED, Capability.UNSUPPORTED, Capability.UNSUPPORTED); + } + + final RecipientPreferenceDatabase recipientDatabase = DatabaseFactory.getRecipientPreferenceDatabase(context); + final Optional recipientPreferences = recipientDatabase.getRecipientsPreferences(recipient.getAddress()); + + if (recipientPreferences.isPresent() && recipientPreferences.get().isRegistered()) return UserCapabilities.SUPPORTED; + else if (recipientPreferences.isPresent()) return UserCapabilities.UNSUPPORTED; + else return UserCapabilities.UNKNOWN; } private static @NonNull RefreshResult updateContactsDatabase(@NonNull Context context, diff --git a/src/org/thoughtcrime/securesms/util/Util.java b/src/org/thoughtcrime/securesms/util/Util.java index 22c2bb46c2..2f247966fe 100644 --- a/src/org/thoughtcrime/securesms/util/Util.java +++ b/src/org/thoughtcrime/securesms/util/Util.java @@ -75,6 +75,12 @@ public class Util { public static Handler handler = new Handler(Looper.getMainLooper()); + public static List asList(T... elements) { + List result = new LinkedList<>(); + Collections.addAll(result, elements); + return result; + } + public static String join(String[] list, String delimiter) { return join(Arrays.asList(list), delimiter); }