diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 60879fcd83..85895cc9be 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -30,26 +30,25 @@ import androidx.multidex.MultiDexApplication; import org.conscrypt.Conscrypt; import org.session.libsession.avatars.AvatarHelper; import org.session.libsession.messaging.MessagingModuleConfiguration; -import org.session.libsession.messaging.mentions.MentionsManager; +import org.session.libsession.messaging.contacts.Contact; import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier; import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2; import org.session.libsession.messaging.sending_receiving.pollers.Poller; import org.session.libsession.snode.SnodeModule; import org.session.libsession.utilities.Address; -import org.session.libsession.utilities.IdentityKeyUtil; -import org.session.libsession.utilities.ProfileKeyUtil; import org.session.libsession.utilities.ProfilePictureUtilities; import org.session.libsession.utilities.SSKEnvironment; import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.Util; import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageContextWrapper; import org.session.libsession.utilities.dynamiclanguage.LocaleParser; -import org.session.libsignal.database.LokiAPIDatabaseProtocol; +import org.session.libsession.utilities.recipients.Recipient; import org.session.libsignal.utilities.Log; import org.session.libsignal.utilities.ThreadUtils; import org.signal.aesgcmprovider.AesGcmProvider; import org.thoughtcrime.securesms.components.TypingStatusSender; import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule; import org.thoughtcrime.securesms.jobmanager.DependencyInjector; @@ -65,8 +64,10 @@ import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker; import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager; import org.thoughtcrime.securesms.loki.api.OpenGroupManager; import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase; -import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; +import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; +import org.thoughtcrime.securesms.loki.database.SessionContactDatabase; import org.thoughtcrime.securesms.loki.utilities.Broadcaster; +import org.thoughtcrime.securesms.loki.utilities.ContactUtilities; import org.thoughtcrime.securesms.loki.utilities.FcmUtils; import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities; import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier; @@ -84,14 +85,12 @@ import org.webrtc.PeerConnectionFactory; import org.webrtc.PeerConnectionFactory.InitializationOptions; import org.webrtc.voiceengine.WebRtcAudioManager; import org.webrtc.voiceengine.WebRtcAudioUtils; - import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.security.Security; import java.util.Date; import java.util.HashSet; import java.util.Set; - import dagger.ObjectGraph; import kotlin.Unit; import kotlinx.coroutines.Job; @@ -152,8 +151,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc initializeDependencyInjection(); NotificationChannels.create(this); ProcessLifecycleOwner.get().getLifecycle().addObserver(this); - // Loki - // ======== AppContext.INSTANCE.configureKovenant(); messageNotifier = new OptimizedMessageNotifier(new DefaultMessageNotifier()); broadcaster = new Broadcaster(this); @@ -161,16 +158,14 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); String userPublicKey = TextSecurePreferences.getLocalNumber(this); MessagingModuleConfiguration.Companion.configure(this, - DatabaseFactory.getStorage(this), - DatabaseFactory.getAttachmentProvider(this)); + DatabaseFactory.getStorage(this), + DatabaseFactory.getAttachmentProvider(this)); SnodeModule.Companion.configure(apiDB, broadcaster); resubmitProfilePictureIfNeeded(); if (userPublicKey != null) { registerForFCMIfNeeded(false); } - // Set application UI mode (day/night theme) to the user selected one. UiModeUtilities.setupUiModeToUserSelected(this); - // ======== initializeExpiringMessageManager(); initializeTypingStatusRepository(); initializeTypingStatusSender(); @@ -188,12 +183,33 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc isAppVisible = true; Log.i(TAG, "App is now visible."); KeyCachingService.onAppForegrounded(this); - // Loki + + boolean hasPerformedContactMigration = TextSecurePreferences.INSTANCE.hasPerformedContactMigration(this); + if (!hasPerformedContactMigration) { + TextSecurePreferences.INSTANCE.setPerformedContactMigration(this); + Set allContacts = ContactUtilities.getAllContacts(this); + SessionContactDatabase contactDB = DatabaseFactory.getSessionContactDatabase(this); + LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this); + for (Recipient recipient : allContacts) { + if (recipient.isGroupRecipient()) { continue; } + String sessionID = recipient.getAddress().serialize(); + Contact contact = contactDB.getContactWithSessionID(sessionID); + if (contact == null) { + contact = new Contact(sessionID); + String name = userDB.getDisplayName(sessionID); + contact.setName(name); + contact.setProfilePictureURL(recipient.getProfileAvatar()); + contact.setProfilePictureEncryptionKey(recipient.getProfileKey()); + contact.setTrusted(true); + } + contactDB.setContact(contact); + } + } + if (poller != null) { poller.setCaughtUp(false); } startPollingIfNeeded(); - OpenGroupManager.INSTANCE.setAllCaughtUp(false); OpenGroupManager.INSTANCE.startPolling(); } @@ -204,7 +220,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc Log.i(TAG, "App is no longer visible."); KeyCachingService.onAppBackgrounded(this); messageNotifier.setVisibleThread(-1); - // Loki if (poller != null) { poller.stopIfNeeded(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java b/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java index d2682e7ac0..9a87fa8730 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java @@ -29,6 +29,7 @@ import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase; import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase; import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; +import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.SessionJobDatabase; import org.thoughtcrime.securesms.loki.database.SessionContactDatabase; @@ -52,16 +53,13 @@ public class DatabaseFactory { private final GroupReceiptDatabase groupReceiptDatabase; private final SearchDatabase searchDatabase; private final JobDatabase jobDatabase; - - // Loki private final LokiAPIDatabase lokiAPIDatabase; private final LokiMessageDatabase lokiMessageDatabase; private final LokiThreadDatabase lokiThreadDatabase; + private final LokiUserDatabase lokiUserDatabase; private final LokiBackupFilesDatabase lokiBackupFilesDatabase; private final SessionJobDatabase sessionJobDatabase; private final SessionContactDatabase sessionContactDatabase; - - // Refactor private final Storage storage; private final DatabaseAttachmentProvider attachmentProvider; @@ -143,6 +141,10 @@ public class DatabaseFactory { return getInstance(context).lokiThreadDatabase; } + public static LokiUserDatabase getLokiUserDatabase(Context context) { + return getInstance(context).lokiUserDatabase; + } + public static LokiBackupFilesDatabase getLokiBackupFilesDatabase(Context context) { return getInstance(context).lokiBackupFilesDatabase; } @@ -194,6 +196,7 @@ public class DatabaseFactory { this.lokiAPIDatabase = new LokiAPIDatabase(context, databaseHelper); this.lokiMessageDatabase = new LokiMessageDatabase(context, databaseHelper); this.lokiThreadDatabase = new LokiThreadDatabase(context, databaseHelper); + this.lokiUserDatabase = new LokiUserDatabase(context, databaseHelper); this.lokiBackupFilesDatabase = new LokiBackupFilesDatabase(context, databaseHelper); this.storage = new Storage(context, databaseHelper); this.attachmentProvider = new DatabaseAttachmentProvider(context, databaseHelper); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index c405283b81..349f7a082b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase; import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase; import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; +import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.SessionContactDatabase; import org.thoughtcrime.securesms.loki.database.SessionJobDatabase; import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsMigration; @@ -124,6 +125,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand()); db.execSQL(LokiThreadDatabase.getCreateSessionResetTableCommand()); db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand()); + db.execSQL(LokiUserDatabase.getCreateDisplayNameTableCommand()); db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand()); db.execSQL(SessionJobDatabase.getCreateSessionJobTableCommand()); db.execSQL(LokiMessageDatabase.getUpdateMessageIDTableForType()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt index e69de29bb2..fba0d64b36 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt @@ -0,0 +1,40 @@ +package org.thoughtcrime.securesms.loki.database + +import android.content.Context +import org.thoughtcrime.securesms.database.Database +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper +import org.thoughtcrime.securesms.loki.utilities.get +import org.session.libsession.utilities.TextSecurePreferences + +class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) { + + companion object { + // Shared + private val displayName = "display_name" + // Display name cache + private val displayNameTable = "loki_user_display_name_database" + private val publicKey = "hex_encoded_public_key" + @JvmStatic val createDisplayNameTableCommand = "CREATE TABLE $displayNameTable ($publicKey TEXT PRIMARY KEY, $displayName TEXT);" + // Server display name cache + private val serverDisplayNameTable = "loki_user_server_display_name_database" + private val serverID = "server_id" + @JvmStatic val createServerDisplayNameTableCommand = "CREATE TABLE $serverDisplayNameTable ($publicKey TEXT, $serverID TEXT, $displayName TEXT, PRIMARY KEY ($publicKey, $serverID));" + } + + fun getDisplayName(publicKey: String): String? { + if (publicKey == TextSecurePreferences.getLocalNumber(context)) { + return TextSecurePreferences.getProfileName(context) + } else { + val database = databaseHelper.readableDatabase + val result = database.get(displayNameTable, "${Companion.publicKey} = ?", arrayOf( publicKey )) { cursor -> + cursor.getString(cursor.getColumnIndexOrThrow(displayName)) + } ?: return null + val suffix = " (...${publicKey.substring(publicKey.count() - 8)})" + if (result.endsWith(suffix)) { + return result.substring(0..(result.count() - suffix.count())) + } else { + return result + } + } + } +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt index 37f2bacd0c..9aa9b2ac09 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt @@ -764,12 +764,12 @@ object TextSecurePreferences { fun shouldUpdateProfile(context: Context, profileUpdateTime: Long) = profileUpdateTime > getLongPreference(context, LAST_PROFILE_UPDATE_TIME, 0) - fun hasSeenFileServerInstabilityNotification(context: Context): Boolean { - return getBooleanPreference(context, "has_seen_file_server_instability_notification", false) + fun hasPerformedContactMigration(context: Context): Boolean { + return getBooleanPreference(context, "has_performed_contact_migration", false) } - fun setHasSeenFileServerInstabilityNotification(context: Context) { - setBooleanPreference(context, "has_seen_file_server_instability_notification", true) + fun setPerformedContactMigration(context: Context) { + setBooleanPreference(context, "has_performed_contact_migration", true) } // endregion } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/utilities/recipients/Recipient.java b/libsession/src/main/java/org/session/libsession/utilities/recipients/Recipient.java index bc636b6f33..767ca02da2 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/recipients/Recipient.java +++ b/libsession/src/main/java/org/session/libsession/utilities/recipients/Recipient.java @@ -26,6 +26,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.annimon.stream.function.Consumer; +import com.esotericsoftware.kryo.util.Null; import org.greenrobot.eventbus.EventBus; import org.session.libsession.database.StorageProtocol;