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: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:layout_width="match_parent"
android:layout_height="32dp"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,7 +23,7 @@ import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener {
private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) }
private lateinit var contentView: View
private var authorization: DeviceLink? = null
private var deviceLink: DeviceLink? = null
var delegate: LinkDeviceSlaveModeDialogDelegate? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
@ -40,10 +40,10 @@ class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener
return result
}
override fun onDeviceLinkRequestAuthorized(authorization: DeviceLink) {
if (authorization.type != DeviceLink.Type.AUTHORIZATION || authorization.slaveHexEncodedPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.authorization != null) { return }
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
if (deviceLink.type != DeviceLink.Type.AUTHORIZATION || deviceLink.slaveHexEncodedPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.deviceLink != null) { return }
Util.runOnMain {
this.authorization = authorization
this.deviceLink = deviceLink
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
DeviceLinkingSession.shared.removeListener(this)
contentView.spinner.visibility = View.GONE
@ -56,7 +56,7 @@ class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener
contentView.cancelButton.visibility = View.GONE
Handler().postDelayed({
dismiss()
delegate?.onDeviceLinkRequestAuthorized(authorization)
delegate?.onDeviceLinkRequestAuthorized(deviceLink)
}, 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 lastDeletionServerID = "last_deletion_server_id"
@JvmStatic val createLastDeletionServerIDTableCommand = "CREATE TABLE $lastDeletionServerIDCache ($lastDeletionServerIDCacheIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0);"
// Pairing authorisation cache
private val pairingAuthorisationCache = "loki_pairing_authorisation_cache"
private val primaryDevicePublicKey = "primary_device"
private val secondaryDevicePublicKey = "secondary_device"
// Device link cache
private val deviceLinkCache = "loki_pairing_authorisation_cache"
private val masterHexEncodedPublicKey = "primary_device"
private val slaveHexEncodedPublicKey = "secondary_device"
private val requestSignature = "request_signature"
private val grantSignature = "grant_signature"
@JvmStatic val createPairingAuthorisationTableCommand = "CREATE TABLE $pairingAuthorisationCache ($primaryDevicePublicKey TEXT, $secondaryDevicePublicKey TEXT, " +
"$requestSignature TEXT NULLABLE DEFAULT NULL, $grantSignature TEXT NULLABLE DEFAULT NULL, PRIMARY KEY ($primaryDevicePublicKey, $secondaryDevicePublicKey));"
private val authorizationSignature = "grant_signature"
@JvmStatic val createDeviceLinkTableCommand = "CREATE TABLE $deviceLinkCache ($masterHexEncodedPublicKey TEXT, $slaveHexEncodedPublicKey TEXT, " +
"$requestSignature TEXT NULLABLE DEFAULT NULL, $authorizationSignature TEXT NULLABLE DEFAULT NULL, PRIMARY KEY ($masterHexEncodedPublicKey, $slaveHexEncodedPublicKey));"
// User count cache
private val userCountCache = "loki_user_count_cache"
private val publicChatID = "public_chat_id"
@ -181,33 +181,33 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
override fun getDeviceLinks(hexEncodedPublicKey: String): Set<DeviceLink> {
val database = databaseHelper.readableDatabase
return database.getAll(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) { cursor ->
val primaryDevicePubKey = cursor.getString(primaryDevicePublicKey)
val secondaryDevicePubKey = cursor.getString(secondaryDevicePublicKey)
return database.getAll(deviceLinkCache, "$masterHexEncodedPublicKey = ? OR $slaveHexEncodedPublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) { cursor ->
val masterHexEncodedPublicKey = cursor.getString(masterHexEncodedPublicKey)
val slaveHexEncodedPublicKey = cursor.getString(slaveHexEncodedPublicKey)
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)
DeviceLink(primaryDevicePubKey, secondaryDevicePubKey, requestSignature, grantSignature)
val authorizationSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(authorizationSignature))) null else cursor.getBase64EncodedData(authorizationSignature)
DeviceLink(masterHexEncodedPublicKey, slaveHexEncodedPublicKey, requestSignature, authorizationSignature)
}.toSet()
}
override fun addDeviceLink(deviceLink: DeviceLink) {
val database = databaseHelper.writableDatabase
val values = ContentValues()
values.put(primaryDevicePublicKey, deviceLink.masterHexEncodedPublicKey)
values.put(secondaryDevicePublicKey, deviceLink.slaveHexEncodedPublicKey)
values.put(masterHexEncodedPublicKey, deviceLink.masterHexEncodedPublicKey)
values.put(slaveHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey)
if (deviceLink.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(deviceLink.requestSignature)) }
if (deviceLink.authorizationSignature != null) { values.put(grantSignature, Base64.encodeBytes(deviceLink.authorizationSignature)) }
database.insertOrUpdate(pairingAuthorisationCache, values, "$primaryDevicePublicKey = ? AND $secondaryDevicePublicKey = ?", arrayOf( deviceLink.masterHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey ))
if (deviceLink.authorizationSignature != null) { values.put(authorizationSignature, Base64.encodeBytes(deviceLink.authorizationSignature)) }
database.insertOrUpdate(deviceLinkCache, values, "$masterHexEncodedPublicKey = ? AND $slaveHexEncodedPublicKey = ?", arrayOf( deviceLink.masterHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey ))
}
override fun clearDeviceLinks(hexEncodedPublicKey: String) {
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
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? {

View File

@ -152,36 +152,36 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
fun pollForNewMessages() {
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
val primaryDevice = LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(message.hexEncodedPublicKey).get()
if (primaryDevice == null) {
// If the sender of the current message is not a slave device, set the display name in the database
val masterHexEncodedPublicKey = LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(message.hexEncodedPublicKey).get()
if (masterHexEncodedPublicKey == null) {
val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})"
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName)
}
val senderPublicKey = primaryDevice ?: message.hexEncodedPublicKey
val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.hexEncodedPublicKey
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) {
PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
} else {
PushDecryptJob(context).handleTextMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
}
// Update profile avatar if needed
val senderRecipient = Recipient.from(context, Address.fromSerialized(senderPublicKey), false)
// Update profile picture if needed
val senderAsRecipient = Recipient.from(context, Address.fromSerialized(senderHexEncodedPublicKey), false)
if (message.profilePicture != null && message.profilePicture!!.url.isNotEmpty()) {
val profileKey = message.profilePicture!!.profileKey
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)
database.setProfileKey(senderRecipient, profileKey)
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderRecipient, url))
database.setProfileKey(senderAsRecipient, profileKey)
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderAsRecipient, url))
}
} else if (senderRecipient.profileAvatar.orEmpty().isNotEmpty()) {
// Unset the avatar if we had an avatar before and we're not friends with the person
val threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(senderRecipient)
val friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId)
} else if (senderAsRecipient.profileAvatar.orEmpty().isNotEmpty()) {
// 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(senderAsRecipient)
val friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID)
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
if (isDuplicate) { 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 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
if (dataMessage.quote.isPresent || (dataMessage.attachments.isPresent && dataMessage.attachments.get().size > 0) || dataMessage.previews.isPresent) {
PushDecryptJob(context).handleSynchronizeSentMediaMessage(transcript)
} else {
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)
if (recipient.isOurMasterDevice && message.profilePicture != null) {
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)
}.bind { messages ->
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()
val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && LokiFileServerAPI.shared.hasDeviceLinkCacheExpired(hexEncodedPublicKey = it) }
if (devicesToUpdate.isNotEmpty()) {
@ -231,14 +231,11 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
}
Promise.of(messages)
}.successBackground {
// Get the set of primary device pubKeys FROM the secondary devices in uniqueDevices
val newDisplayNameUpdatees = uniqueDevices.mapNotNull {
// This will return null if current device is primary
// So if it's non-null then we know the device is a secondary device
val primaryDevice = LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(it).get()
primaryDevice
// This will return null if the current device is a master device
LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(it).get()
}.toSet()
// Fetch the display names of the primary devices
// Fetch the display names of the master devices
displayNameUpdatees = displayNameUpdatees.union(newDisplayNameUpdatees)
}.successBackground { messages ->
// 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.toPx
class SeparatorView : RelativeLayout {
class LabeledSeparatorView : RelativeLayout {
private val path = Path()