Multi-device profile avatar.

This commit is contained in:
Mikunj 2019-11-28 09:25:00 +11:00
parent 9337a1d44a
commit 6e7b21e8b4
7 changed files with 75 additions and 12 deletions

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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());

View File

@ -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<Unit, Exception> {
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<UnidentifiedAccessPair>(), message.build())

View File

@ -0,0 +1,5 @@
package org.thoughtcrime.securesms.loki
import org.thoughtcrime.securesms.recipients.Recipient
data class RecipientAvatarModifiedEvent(val recipient: Recipient)

View File

@ -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()

View File

@ -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);