diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index 10c71386fb..5be339cf17 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -41,6 +41,9 @@ import android.widget.Toast; import com.bumptech.glide.load.engine.DiskCacheStrategy; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; import org.thoughtcrime.securesms.components.RatingManager; import org.thoughtcrime.securesms.components.SearchToolbar; import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto; @@ -52,6 +55,7 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; import org.thoughtcrime.securesms.lock.RegistrationLockDialog; import org.thoughtcrime.securesms.loki.AddPublicChatActivity; import org.thoughtcrime.securesms.loki.JazzIdenticonDrawable; +import org.thoughtcrime.securesms.loki.RecipientAvatarModifiedEvent; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.notifications.MarkReadReceiver; import org.thoughtcrime.securesms.notifications.MessageNotifier; @@ -134,6 +138,18 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit super.onDestroy(); } + @Override + public void onStart() { + super.onStart(); + EventBus.getDefault().register(this); + } + + @Override + public void onStop() { + EventBus.getDefault().unregister(this); + super.onStop(); + } + @Override public boolean onPrepareOptionsMenu(Menu menu) { MenuInflater inflater = this.getMenuInflater(); @@ -214,7 +230,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit Drawable fallback = primaryRecipient.getFallbackContactPhotoDrawable(this, false); GlideApp.with(this) - .load(new ProfileContactPhoto(primaryRecipient.getAddress(), String.valueOf(TextSecurePreferences.getProfileAvatarId(this)))) + .load(primaryRecipient.getContactPhoto()) .fallback(fallback) .error(fallback) .diskCacheStrategy(DiskCacheStrategy.ALL) @@ -321,4 +337,14 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit private void addNewPublicChat() { startActivity(new Intent(this, AddPublicChatActivity.class)); } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onAvatarModified(RecipientAvatarModifiedEvent event) { + Recipient recipient = event.getRecipient(); + String ourMasterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this); + boolean isOurMasterDevice = ourMasterHexEncodedPublicKey != null && ourMasterHexEncodedPublicKey.equals(recipient.getAddress().serialize()); + if (recipient.isLocalNumber() || isOurMasterDevice) { + initializeProfileIcon(recipient); + } + } } diff --git a/src/org/thoughtcrime/securesms/CreateProfileActivity.java b/src/org/thoughtcrime/securesms/CreateProfileActivity.java index cf3a2c3482..5fdbce2c8e 100644 --- a/src/org/thoughtcrime/securesms/CreateProfileActivity.java +++ b/src/org/thoughtcrime/securesms/CreateProfileActivity.java @@ -427,7 +427,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje return false; } - ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceProfileKeyUpdateJob()); + // ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceProfileKeyUpdateJob()); return true; } diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 32dd6c9567..8465d82f10 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -724,6 +724,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType { handleUnknownGroupMessage(content, message.getMessage().getGroupInfo().get()); } + String ourMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context); + boolean isSenderMasterDevice = ourMasterDevice != null && ourMasterDevice.equals(content.getSender()); if (message.getMessage().getProfileKey().isPresent()) { Recipient recipient = null; @@ -734,6 +736,16 @@ public class PushDecryptJob extends BaseJob implements InjectableType { if (recipient != null && !recipient.isSystemContact() && !recipient.isProfileSharing()) { DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true); } + + // Loki - If we received a sync message from our master device then we need to extract the avatar url + if (isSenderMasterDevice) { + handleProfileKey(content, message.getMessage()); + } + } + + // Loki - Update display name from master device + if (isSenderMasterDevice && content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) { + TextSecurePreferences.setProfileName(context, content.senderDisplayName.get()); } if (threadId != null) { @@ -1142,7 +1154,10 @@ public class PushDecryptJob extends BaseJob implements InjectableType { if (content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) { TextSecurePreferences.setProfileName(context, content.senderDisplayName.get()); } - + // Profile avatar updates + if (content.getDataMessage().isPresent()) { + handleProfileKey(content, content.getDataMessage().get()); + } // Contact sync if (content.getSyncMessage().isPresent() && content.getSyncMessage().get().getContacts().isPresent()) { handleSynchronizeContactMessage(content.getSyncMessage().get().getContacts().get()); @@ -1413,8 +1428,14 @@ public class PushDecryptJob extends BaseJob implements InjectableType { private void handleProfileKey(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) { + if (!message.getProfileKey().isPresent()) { return; } + + /* + If we get a profile key then we don't need to map it to the primary device. + For now a profile key is mapped one-to-one to avoid secondary devices setting the incorrect avatar for a primary device. + */ RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context); - Recipient recipient = getPrimaryDeviceRecipient(content.getSender()); + Recipient recipient = Recipient.from(context, Address.fromSerialized(content.getSender()), false); if (recipient.getProfileKey() == null || !MessageDigest.isEqual(recipient.getProfileKey(), message.getProfileKey().get())) { database.setProfileKey(recipient, message.getProfileKey().get()); diff --git a/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt b/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt index e4f7550ad3..d3abb182dd 100644 --- a/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt @@ -11,6 +11,8 @@ import nl.komponents.kovenant.toFailVoid import nl.komponents.kovenant.ui.successUi import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.crypto.PreKeyUtil +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil import org.thoughtcrime.securesms.database.Address import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.logging.Log @@ -114,12 +116,16 @@ fun shouldAutomaticallyBecomeFriendsWithDevice(publicKey: String, context: Conte fun sendPairingAuthorisationMessage(context: Context, contactHexEncodedPublicKey: String, authorisation: PairingAuthorisation): Promise { val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() val address = SignalServiceAddress(contactHexEncodedPublicKey) - val message = SignalServiceDataMessage.newBuilder().withBody(null).withPairingAuthorisation(authorisation) + val message = SignalServiceDataMessage.newBuilder().withPairingAuthorisation(authorisation) // A REQUEST should always act as a friend request. A GRANT should always be replying back as a normal message. if (authorisation.type == PairingAuthorisation.Type.REQUEST) { val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.number) message.asFriendRequest(true).withPreKeyBundle(preKeyBundle) + } else { + // Send over our profile key so that our linked device can get our profile picture + message.withProfileKey(ProfileKeyUtil.getProfileKey(context)) } + return try { Log.d("Loki", "Sending authorisation message to: $contactHexEncodedPublicKey.") val result = messageSender.sendMessage(0, address, Optional.absent(), message.build()) diff --git a/src/org/thoughtcrime/securesms/loki/RecipientAvatarModifiedEvent.kt b/src/org/thoughtcrime/securesms/loki/RecipientAvatarModifiedEvent.kt new file mode 100644 index 0000000000..4faf84714b --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/RecipientAvatarModifiedEvent.kt @@ -0,0 +1,5 @@ +package org.thoughtcrime.securesms.loki + +import org.thoughtcrime.securesms.recipients.Recipient + +data class RecipientAvatarModifiedEvent(val recipient: Recipient) \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java b/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java index 73371622f8..d0fc8e96d3 100644 --- a/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java +++ b/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java @@ -20,6 +20,7 @@ import android.widget.Toast; import com.bumptech.glide.load.engine.DiskCacheStrategy; +import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.loki.MnemonicUtilities; @@ -80,13 +81,14 @@ public class ProfilePreference extends Preference { public void refresh() { if (profileNumberView == null) return; - String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext()); - String primaryDevicePublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(getContext()); + Context context = getContext(); + String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context); + String primaryDevicePublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context); String publicKey = primaryDevicePublicKey != null ? primaryDevicePublicKey : userHexEncodedPublicKey; final Address localAddress = Address.fromSerialized(publicKey); - final String profileName = TextSecurePreferences.getProfileName(getContext()); + final Recipient recipient = Recipient.from(context, localAddress, false); + final String profileName = TextSecurePreferences.getProfileName(context); - Context context = getContext(); containerView.setOnLongClickListener(v -> { ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("Public Key", publicKey); @@ -104,9 +106,9 @@ public class ProfilePreference extends Preference { }); avatarView.setClipToOutline(true); - Drawable fallback = Recipient.from(context, localAddress, false).getFallbackContactPhotoDrawable(context, false); + Drawable fallback = recipient.getFallbackContactPhotoDrawable(context, false); GlideApp.with(getContext().getApplicationContext()) - .load(new ProfileContactPhoto(localAddress, String.valueOf(TextSecurePreferences.getProfileAvatarId(getContext())))) + .load(recipient.getContactPhoto()) .fallback(fallback) .error(fallback) .circleCrop() diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java index 75f1a90e4b..0528206438 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java @@ -26,6 +26,7 @@ import android.text.TextUtils; import com.annimon.stream.function.Consumer; +import org.greenrobot.eventbus.EventBus; import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.contacts.avatars.ContactColors; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; @@ -45,6 +46,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessM import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.loki.JazzIdenticonContactPhoto; +import org.thoughtcrime.securesms.loki.RecipientAvatarModifiedEvent; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails; import org.thoughtcrime.securesms.util.FutureTaskListener; @@ -394,6 +396,7 @@ public class Recipient implements RecipientModifiedListener { } notifyListeners(); + EventBus.getDefault().post(new RecipientAvatarModifiedEvent(this)); } public synchronized boolean isProfileSharing() { @@ -469,7 +472,7 @@ public class Recipient implements RecipientModifiedListener { } public synchronized @Nullable ContactPhoto getContactPhoto() { - if (isLocalNumber) return null; + if (isLocalNumber) return new ProfileContactPhoto(address, String.valueOf(TextSecurePreferences.getProfileAvatarId(context))); else if (isGroupRecipient() && groupAvatarId != null) return new GroupRecordContactPhoto(address, groupAvatarId); else if (systemContactPhoto != null) return new SystemContactPhoto(address, systemContactPhoto, 0); else if (profileAvatar != null) return new ProfileContactPhoto(address, profileAvatar);