This commit is contained in:
Niels Andriesse 2020-02-12 16:42:33 +11:00
parent 45d78825a0
commit 31350adcf7
11 changed files with 68 additions and 77 deletions

View File

@ -30,7 +30,7 @@
android:textAlignment="center" android:textAlignment="center"
android:text="Users can share their Session ID by going into their account settings and tapping "Share Session ID", or by sharing their QR code." /> android:text="Users can share their Session ID by going into their account settings and tapping "Share Session ID", or by sharing their QR code." />
<org.thoughtcrime.securesms.loki.redesign.views.SeparatorView <org.thoughtcrime.securesms.loki.redesign.views.LabeledSeparatorView
android:id="@+id/separatorView" android:id="@+id/separatorView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="32dp" android:layout_height="32dp"

View File

@ -107,7 +107,7 @@
</RelativeLayout> </RelativeLayout>
<org.thoughtcrime.securesms.loki.redesign.views.SeparatorView <org.thoughtcrime.securesms.loki.redesign.views.LabeledSeparatorView
android:id="@+id/separatorView" android:id="@+id/separatorView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="32dp" android:layout_height="32dp"

View File

@ -30,7 +30,7 @@
android:textAlignment="center" android:textAlignment="center"
android:text="Users can share their Session ID by going into their account settings and tapping &quot;Share Session ID&quot;, or by sharing their QR code." /> android:text="Users can share their Session ID by going into their account settings and tapping &quot;Share Session ID&quot;, or by sharing their QR code." />
<org.thoughtcrime.securesms.loki.redesign.views.SeparatorView <org.thoughtcrime.securesms.loki.redesign.views.LabeledSeparatorView
android:id="@+id/separatorView" android:id="@+id/separatorView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="32dp" android:layout_height="32dp"

View File

@ -135,7 +135,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiAPIDatabase.getCreateGroupChatAuthTokenTableCommand()); db.execSQL(LokiAPIDatabase.getCreateGroupChatAuthTokenTableCommand());
db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand()); db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand());
db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand()); db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand());
db.execSQL(LokiAPIDatabase.getCreatePairingAuthorisationTableCommand()); db.execSQL(LokiAPIDatabase.getCreateDeviceLinkTableCommand());
db.execSQL(LokiAPIDatabase.getCreateUserCountTableCommand()); db.execSQL(LokiAPIDatabase.getCreateUserCountTableCommand());
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand()); db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand()); db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
@ -518,7 +518,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
} }
if (oldVersion < lokiV3) { if (oldVersion < lokiV3) {
db.execSQL(LokiAPIDatabase.getCreatePairingAuthorisationTableCommand()); db.execSQL(LokiAPIDatabase.getCreateDeviceLinkTableCommand());
db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand()); db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand());
db.execSQL("ALTER TABLE groups ADD COLUMN avatar_url TEXT"); db.execSQL("ALTER TABLE groups ADD COLUMN avatar_url TEXT");

View File

@ -104,9 +104,9 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), MemberC
val recipients = selectedMembers.map { val recipients = selectedMembers.map {
Recipient.from(this, Address.fromSerialized(it), false) Recipient.from(this, Address.fromSerialized(it), false)
}.toSet() }.toSet()
val ourNumber = TextSecurePreferences.getMasterHexEncodedPublicKey(this) ?: TextSecurePreferences.getLocalNumber(this) val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this) ?: TextSecurePreferences.getLocalNumber(this)
val local = Recipient.from(this, Address.fromSerialized(ourNumber), false) val admin = Recipient.from(this, Address.fromSerialized(masterHexEncodedPublicKey), false)
CreateClosedGroupTask(WeakReference(this), null, name.toString(), recipients, setOf(local)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) CreateClosedGroupTask(WeakReference(this), null, name.toString(), recipients, setOf( admin )).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
} }
private fun handleOpenConversation(threadId: Long, recipient: Recipient) { private fun handleOpenConversation(threadId: Long, recipient: Recipient) {
@ -122,7 +122,7 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), MemberC
// region Tasks // region Tasks
internal class CreateClosedGroupTask( internal class CreateClosedGroupTask(
private val activity: WeakReference<CreateClosedGroupActivity>, private val activity: WeakReference<CreateClosedGroupActivity>,
private val avatar: Bitmap?, private val profilePicture: Bitmap?,
private val name: String?, private val name: String?,
private val members: Set<Recipient>, private val members: Set<Recipient>,
private val admins: Set<Recipient> private val admins: Set<Recipient>
@ -130,24 +130,18 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), MemberC
override fun doInBackground(vararg params: Void?): Optional<GroupManager.GroupActionResult> { override fun doInBackground(vararg params: Void?): Optional<GroupManager.GroupActionResult> {
val activity = activity.get() ?: return Optional.absent() val activity = activity.get() ?: return Optional.absent()
return Optional.of(GroupManager.createGroup(activity, members, avatar, name, false, admins)) return Optional.of(GroupManager.createGroup(activity, members, profilePicture, name, false, admins))
} }
override fun onPostExecute(result: Optional<GroupManager.GroupActionResult>) { override fun onPostExecute(result: Optional<GroupManager.GroupActionResult>) {
val activity = activity.get() val activity = activity.get() ?: return super.onPostExecute(result)
if (activity == null) {
super.onPostExecute(result)
return
}
if (result.isPresent && result.get().threadId > -1) { if (result.isPresent && result.get().threadId > -1) {
if (!activity.isFinishing) { if (!activity.isFinishing) {
activity.handleOpenConversation(result.get().threadId, result.get().groupRecipient) activity.handleOpenConversation(result.get().threadId, result.get().groupRecipient)
} }
} else { } else {
super.onPostExecute(result) super.onPostExecute(result)
Toast.makeText(activity.applicationContext, Toast.makeText(activity.applicationContext, "One of the members of your group has an invalid Session ID.", Toast.LENGTH_LONG).show()
R.string.GroupCreateActivity_contacts_invalid_number, Toast.LENGTH_LONG).show()
} }
} }
} }

View File

@ -118,7 +118,7 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager
private fun unlinkDevice(slaveDeviceHexEncodedPublicKey: String) { private fun unlinkDevice(slaveDeviceHexEncodedPublicKey: String) {
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this) val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this)
val database = DatabaseFactory.getLokiAPIDatabase(this) val database = DatabaseFactory.getLokiAPIDatabase(this)
database.removePairingAuthorisation(userHexEncodedPublicKey, slaveDeviceHexEncodedPublicKey) database.removeDeviceLink(userHexEncodedPublicKey, slaveDeviceHexEncodedPublicKey)
LokiFileServerAPI.shared.updateUserDeviceLinks().success { LokiFileServerAPI.shared.updateUserDeviceLinks().success {
MessageSender.sendUnpairRequest(this, slaveDeviceHexEncodedPublicKey) MessageSender.sendUnpairRequest(this, slaveDeviceHexEncodedPublicKey)
} }

View File

@ -25,7 +25,7 @@ import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListener { class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListener {
private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) } private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) }
private lateinit var contentView: View private lateinit var contentView: View
private var authorization: DeviceLink? = null private var deviceLink: DeviceLink? = null
var delegate: LinkDeviceMasterModeDialogDelegate? = null var delegate: LinkDeviceMasterModeDialogDelegate? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
@ -45,10 +45,10 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
return result return result
} }
override fun requestUserAuthorization(authorization: DeviceLink) { override fun requestUserAuthorization(deviceLink: DeviceLink) {
if (authorization.type != DeviceLink.Type.REQUEST || authorization.masterHexEncodedPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.authorization != null) { return } if (deviceLink.type != DeviceLink.Type.REQUEST || deviceLink.masterHexEncodedPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.deviceLink != null) { return }
Util.runOnMain { Util.runOnMain {
this.authorization = authorization this.deviceLink = deviceLink
contentView.qrCodeImageView.visibility = View.GONE contentView.qrCodeImageView.visibility = View.GONE
val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams
titleTextViewLayoutParams.topMargin = toPx(8, resources) titleTextViewLayoutParams.topMargin = toPx(8, resources)
@ -56,13 +56,13 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
contentView.titleTextView.text = "Linking Request Received" contentView.titleTextView.text = "Linking Request Received"
contentView.explanationTextView.text = "Please check that the words below match those shown on your other device" contentView.explanationTextView.text = "Please check that the words below match those shown on your other device"
contentView.mnemonicTextView.visibility = View.VISIBLE contentView.mnemonicTextView.visibility = View.VISIBLE
contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), authorization.slaveHexEncodedPublicKey) contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), deviceLink.slaveHexEncodedPublicKey)
contentView.authorizeButton.visibility = View.VISIBLE contentView.authorizeButton.visibility = View.VISIBLE
} }
} }
private fun authorizeDeviceLink() { private fun authorizeDeviceLink() {
val authorization = this.authorization ?: return val authorization = this.deviceLink ?: return
delegate?.onDeviceLinkRequestAuthorized(authorization) delegate?.onDeviceLinkRequestAuthorized(authorization)
DeviceLinkingSession.shared.stopListeningForLinkingRequests() DeviceLinkingSession.shared.stopListeningForLinkingRequests()
DeviceLinkingSession.shared.removeListener(this) DeviceLinkingSession.shared.removeListener(this)
@ -72,8 +72,8 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
private fun onDeviceLinkCanceled() { private fun onDeviceLinkCanceled() {
DeviceLinkingSession.shared.stopListeningForLinkingRequests() DeviceLinkingSession.shared.stopListeningForLinkingRequests()
DeviceLinkingSession.shared.removeListener(this) DeviceLinkingSession.shared.removeListener(this)
if (authorization != null) { if (deviceLink != null) {
DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(authorization!!.slaveHexEncodedPublicKey) DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(deviceLink!!.slaveHexEncodedPublicKey)
} }
dismiss() dismiss()
delegate?.onDeviceLinkCanceled() delegate?.onDeviceLinkCanceled()

View File

@ -23,7 +23,7 @@ import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener { class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener {
private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) } private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) }
private lateinit var contentView: View private lateinit var contentView: View
private var authorization: DeviceLink? = null private var deviceLink: DeviceLink? = null
var delegate: LinkDeviceSlaveModeDialogDelegate? = null var delegate: LinkDeviceSlaveModeDialogDelegate? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
@ -40,10 +40,10 @@ class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener
return result return result
} }
override fun onDeviceLinkRequestAuthorized(authorization: DeviceLink) { override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
if (authorization.type != DeviceLink.Type.AUTHORIZATION || authorization.slaveHexEncodedPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.authorization != null) { return } if (deviceLink.type != DeviceLink.Type.AUTHORIZATION || deviceLink.slaveHexEncodedPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.deviceLink != null) { return }
Util.runOnMain { Util.runOnMain {
this.authorization = authorization this.deviceLink = deviceLink
DeviceLinkingSession.shared.stopListeningForLinkingRequests() DeviceLinkingSession.shared.stopListeningForLinkingRequests()
DeviceLinkingSession.shared.removeListener(this) DeviceLinkingSession.shared.removeListener(this)
contentView.spinner.visibility = View.GONE contentView.spinner.visibility = View.GONE
@ -56,7 +56,7 @@ class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener
contentView.cancelButton.visibility = View.GONE contentView.cancelButton.visibility = View.GONE
Handler().postDelayed({ Handler().postDelayed({
dismiss() dismiss()
delegate?.onDeviceLinkRequestAuthorized(authorization) delegate?.onDeviceLinkRequestAuthorized(deviceLink)
}, 4000) }, 4000)
} }
} }

View File

@ -48,14 +48,14 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
private val lastDeletionServerIDCacheIndex = "loki_api_last_deletion_server_id_cache_index" private val lastDeletionServerIDCacheIndex = "loki_api_last_deletion_server_id_cache_index"
private val lastDeletionServerID = "last_deletion_server_id" private val lastDeletionServerID = "last_deletion_server_id"
@JvmStatic val createLastDeletionServerIDTableCommand = "CREATE TABLE $lastDeletionServerIDCache ($lastDeletionServerIDCacheIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0);" @JvmStatic val createLastDeletionServerIDTableCommand = "CREATE TABLE $lastDeletionServerIDCache ($lastDeletionServerIDCacheIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0);"
// Pairing authorisation cache // Device link cache
private val pairingAuthorisationCache = "loki_pairing_authorisation_cache" private val deviceLinkCache = "loki_pairing_authorisation_cache"
private val primaryDevicePublicKey = "primary_device" private val masterHexEncodedPublicKey = "primary_device"
private val secondaryDevicePublicKey = "secondary_device" private val slaveHexEncodedPublicKey = "secondary_device"
private val requestSignature = "request_signature" private val requestSignature = "request_signature"
private val grantSignature = "grant_signature" private val authorizationSignature = "grant_signature"
@JvmStatic val createPairingAuthorisationTableCommand = "CREATE TABLE $pairingAuthorisationCache ($primaryDevicePublicKey TEXT, $secondaryDevicePublicKey TEXT, " + @JvmStatic val createDeviceLinkTableCommand = "CREATE TABLE $deviceLinkCache ($masterHexEncodedPublicKey TEXT, $slaveHexEncodedPublicKey TEXT, " +
"$requestSignature TEXT NULLABLE DEFAULT NULL, $grantSignature TEXT NULLABLE DEFAULT NULL, PRIMARY KEY ($primaryDevicePublicKey, $secondaryDevicePublicKey));" "$requestSignature TEXT NULLABLE DEFAULT NULL, $authorizationSignature TEXT NULLABLE DEFAULT NULL, PRIMARY KEY ($masterHexEncodedPublicKey, $slaveHexEncodedPublicKey));"
// User count cache // User count cache
private val userCountCache = "loki_user_count_cache" private val userCountCache = "loki_user_count_cache"
private val publicChatID = "public_chat_id" private val publicChatID = "public_chat_id"
@ -181,33 +181,33 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
override fun getDeviceLinks(hexEncodedPublicKey: String): Set<DeviceLink> { override fun getDeviceLinks(hexEncodedPublicKey: String): Set<DeviceLink> {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.getAll(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) { cursor -> return database.getAll(deviceLinkCache, "$masterHexEncodedPublicKey = ? OR $slaveHexEncodedPublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) { cursor ->
val primaryDevicePubKey = cursor.getString(primaryDevicePublicKey) val masterHexEncodedPublicKey = cursor.getString(masterHexEncodedPublicKey)
val secondaryDevicePubKey = cursor.getString(secondaryDevicePublicKey) val slaveHexEncodedPublicKey = cursor.getString(slaveHexEncodedPublicKey)
val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature) val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature)
val grantSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(grantSignature))) null else cursor.getBase64EncodedData(grantSignature) val authorizationSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(authorizationSignature))) null else cursor.getBase64EncodedData(authorizationSignature)
DeviceLink(primaryDevicePubKey, secondaryDevicePubKey, requestSignature, grantSignature) DeviceLink(masterHexEncodedPublicKey, slaveHexEncodedPublicKey, requestSignature, authorizationSignature)
}.toSet() }.toSet()
} }
override fun addDeviceLink(deviceLink: DeviceLink) { override fun addDeviceLink(deviceLink: DeviceLink) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val values = ContentValues() val values = ContentValues()
values.put(primaryDevicePublicKey, deviceLink.masterHexEncodedPublicKey) values.put(masterHexEncodedPublicKey, deviceLink.masterHexEncodedPublicKey)
values.put(secondaryDevicePublicKey, deviceLink.slaveHexEncodedPublicKey) values.put(slaveHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey)
if (deviceLink.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(deviceLink.requestSignature)) } if (deviceLink.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(deviceLink.requestSignature)) }
if (deviceLink.authorizationSignature != null) { values.put(grantSignature, Base64.encodeBytes(deviceLink.authorizationSignature)) } if (deviceLink.authorizationSignature != null) { values.put(authorizationSignature, Base64.encodeBytes(deviceLink.authorizationSignature)) }
database.insertOrUpdate(pairingAuthorisationCache, values, "$primaryDevicePublicKey = ? AND $secondaryDevicePublicKey = ?", arrayOf( deviceLink.masterHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey )) database.insertOrUpdate(deviceLinkCache, values, "$masterHexEncodedPublicKey = ? AND $slaveHexEncodedPublicKey = ?", arrayOf( deviceLink.masterHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey ))
} }
override fun clearDeviceLinks(hexEncodedPublicKey: String) { override fun clearDeviceLinks(hexEncodedPublicKey: String) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
database.delete(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) database.delete(deviceLinkCache, "$masterHexEncodedPublicKey = ? OR $slaveHexEncodedPublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey ))
} }
fun removePairingAuthorisation(primaryDevicePublicKey: String, secondaryDevicePublicKey: String) { fun removeDeviceLink(masterHexEncodedPublicKey: String, slaveHexEncodedPublicKey: String) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
database.delete(pairingAuthorisationCache, "${Companion.primaryDevicePublicKey} = ? OR ${Companion.secondaryDevicePublicKey} = ?", arrayOf( primaryDevicePublicKey, secondaryDevicePublicKey )) database.delete(deviceLinkCache, "${Companion.masterHexEncodedPublicKey} = ? OR ${Companion.slaveHexEncodedPublicKey} = ?", arrayOf( masterHexEncodedPublicKey, slaveHexEncodedPublicKey ))
} }
fun getUserCount(group: Long, server: String): Int? { fun getUserCount(group: Long, server: String): Int? {

View File

@ -152,36 +152,36 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
fun pollForNewMessages() { fun pollForNewMessages() {
fun processIncomingMessage(message: LokiPublicChatMessage) { fun processIncomingMessage(message: LokiPublicChatMessage) {
// If the sender of the current message is not a secondary device, we need to set the display name in the database // If the sender of the current message is not a slave device, set the display name in the database
val primaryDevice = LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(message.hexEncodedPublicKey).get() val masterHexEncodedPublicKey = LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(message.hexEncodedPublicKey).get()
if (primaryDevice == null) { if (masterHexEncodedPublicKey == null) {
val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})" val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})"
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName) DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName)
} }
val senderPublicKey = primaryDevice ?: message.hexEncodedPublicKey val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.hexEncodedPublicKey
val serviceDataMessage = getDataMessage(message) val serviceDataMessage = getDataMessage(message)
val serviceContent = SignalServiceContent(serviceDataMessage, senderPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false, false) val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false, false)
if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) { if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) {
PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID)) PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
} else { } else {
PushDecryptJob(context).handleTextMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID)) PushDecryptJob(context).handleTextMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
} }
// Update profile avatar if needed // Update profile picture if needed
val senderRecipient = Recipient.from(context, Address.fromSerialized(senderPublicKey), false) val senderAsRecipient = Recipient.from(context, Address.fromSerialized(senderHexEncodedPublicKey), false)
if (message.profilePicture != null && message.profilePicture!!.url.isNotEmpty()) { if (message.profilePicture != null && message.profilePicture!!.url.isNotEmpty()) {
val profileKey = message.profilePicture!!.profileKey val profileKey = message.profilePicture!!.profileKey
val url = message.profilePicture!!.url val url = message.profilePicture!!.url
if (senderRecipient.profileKey == null || !MessageDigest.isEqual(senderRecipient.profileKey, profileKey)) { if (senderAsRecipient.profileKey == null || !MessageDigest.isEqual(senderAsRecipient.profileKey, profileKey)) {
val database = DatabaseFactory.getRecipientDatabase(context) val database = DatabaseFactory.getRecipientDatabase(context)
database.setProfileKey(senderRecipient, profileKey) database.setProfileKey(senderAsRecipient, profileKey)
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderRecipient, url)) ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderAsRecipient, url))
} }
} else if (senderRecipient.profileAvatar.orEmpty().isNotEmpty()) { } else if (senderAsRecipient.profileAvatar.orEmpty().isNotEmpty()) {
// Unset the avatar if we had an avatar before and we're not friends with the person // Clear the profile picture if we had a profile picture before and we're not friends with the person
val threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(senderRecipient) val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(senderAsRecipient)
val friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId) val friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID)
if (friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) { if (friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) {
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderRecipient, "")) ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderAsRecipient, ""))
} }
} }
} }
@ -190,16 +190,16 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
val isDuplicate = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID) != null val isDuplicate = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID) != null
if (isDuplicate) { return } if (isDuplicate) { return }
if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return } if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return }
val localNumber = TextSecurePreferences.getLocalNumber(context) val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
val dataMessage = getDataMessage(message) val dataMessage = getDataMessage(message)
val transcript = SentTranscriptMessage(localNumber, dataMessage.timestamp, dataMessage, dataMessage.expiresInSeconds.toLong(), Collections.singletonMap(localNumber, false)) val transcript = SentTranscriptMessage(userHexEncodedPublicKey, dataMessage.timestamp, dataMessage, dataMessage.expiresInSeconds.toLong(), Collections.singletonMap(userHexEncodedPublicKey, false))
transcript.messageServerID = messageServerID transcript.messageServerID = messageServerID
if (dataMessage.quote.isPresent || (dataMessage.attachments.isPresent && dataMessage.attachments.get().size > 0) || dataMessage.previews.isPresent) { if (dataMessage.quote.isPresent || (dataMessage.attachments.isPresent && dataMessage.attachments.get().size > 0) || dataMessage.previews.isPresent) {
PushDecryptJob(context).handleSynchronizeSentMediaMessage(transcript) PushDecryptJob(context).handleSynchronizeSentMediaMessage(transcript)
} else { } else {
PushDecryptJob(context).handleSynchronizeSentTextMessage(transcript) PushDecryptJob(context).handleSynchronizeSentTextMessage(transcript)
} }
// If we got a message from our master device then make sure our mappings stay in sync // If we got a message from our master device then make sure our mapping stays in sync
val recipient = Recipient.from(context, Address.fromSerialized(message.hexEncodedPublicKey), false) val recipient = Recipient.from(context, Address.fromSerialized(message.hexEncodedPublicKey), false)
if (recipient.isOurMasterDevice && message.profilePicture != null) { if (recipient.isOurMasterDevice && message.profilePicture != null) {
val profileKey = message.profilePicture!!.profileKey val profileKey = message.profilePicture!!.profileKey
@ -222,7 +222,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
api.getMessages(group.channel, group.server) api.getMessages(group.channel, group.server)
}.bind { messages -> }.bind { messages ->
if (messages.isNotEmpty()) { if (messages.isNotEmpty()) {
// We need to fetch device mappings for all the devices we don't have // We need to fetch the device mapping for any devices we don't have
uniqueDevices = messages.map { it.hexEncodedPublicKey }.toSet() uniqueDevices = messages.map { it.hexEncodedPublicKey }.toSet()
val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && LokiFileServerAPI.shared.hasDeviceLinkCacheExpired(hexEncodedPublicKey = it) } val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && LokiFileServerAPI.shared.hasDeviceLinkCacheExpired(hexEncodedPublicKey = it) }
if (devicesToUpdate.isNotEmpty()) { if (devicesToUpdate.isNotEmpty()) {
@ -231,14 +231,11 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
} }
Promise.of(messages) Promise.of(messages)
}.successBackground { }.successBackground {
// Get the set of primary device pubKeys FROM the secondary devices in uniqueDevices
val newDisplayNameUpdatees = uniqueDevices.mapNotNull { val newDisplayNameUpdatees = uniqueDevices.mapNotNull {
// This will return null if current device is primary // This will return null if the current device is a master device
// So if it's non-null then we know the device is a secondary device LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(it).get()
val primaryDevice = LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(it).get()
primaryDevice
}.toSet() }.toSet()
// Fetch the display names of the primary devices // Fetch the display names of the master devices
displayNameUpdatees = displayNameUpdatees.union(newDisplayNameUpdatees) displayNameUpdatees = displayNameUpdatees.union(newDisplayNameUpdatees)
}.successBackground { messages -> }.successBackground { messages ->
// Process messages in the background // Process messages in the background

View File

@ -12,7 +12,7 @@ import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.getColorWithID import org.thoughtcrime.securesms.loki.getColorWithID
import org.thoughtcrime.securesms.loki.toPx import org.thoughtcrime.securesms.loki.toPx
class SeparatorView : RelativeLayout { class LabeledSeparatorView : RelativeLayout {
private val path = Path() private val path = Path()