diff --git a/res/layout/profile_create_activity.xml b/res/layout/profile_create_activity.xml index c9b105c593..02347896d3 100644 --- a/res/layout/profile_create_activity.xml +++ b/res/layout/profile_create_activity.xml @@ -43,7 +43,6 @@ android:id="@+id/avatar_background" android:layout_width="80dp" android:layout_height="80dp" - android:visibility="gone" android:layout_marginStart="32dp" android:layout_marginTop="4dp" android:src="@drawable/circle_tintable" @@ -56,7 +55,6 @@ android:id="@+id/avatar_placeholder" android:layout_width="0dp" android:layout_height="0dp" - android:visibility="gone" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:src="@drawable/ic_profile_default" @@ -71,7 +69,6 @@ android:id="@+id/avatar" android:layout_width="0dp" android:layout_height="0dp" - android:visibility="gone" app:layout_constraintBottom_toBottomOf="@+id/avatar_background" app:layout_constraintEnd_toEndOf="@+id/avatar_background" app:layout_constraintStart_toStartOf="@+id/avatar_background" @@ -81,7 +78,6 @@ android:id="@+id/camera_icon" android:layout_width="60dp" android:layout_height="60dp" - android:visibility="gone" android:layout_marginStart="35dp" android:layout_marginTop="35dp" android:cropToPadding="false" @@ -94,7 +90,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginStart="49dp" + android:layout_marginStart="16dp" android:layout_marginTop="24dp" android:layout_marginEnd="4dp" android:layout_weight="1" @@ -105,7 +101,7 @@ app:layout_constraintBottom_toTopOf="@+id/description_text" app:layout_constraintEnd_toStartOf="@+id/emoji_toggle" app:layout_constraintHorizontal_bias="0.5" - app:layout_constraintStart_toStartOf="@+id/avatar_background" + app:layout_constraintStart_toEndOf="@+id/avatar_background" app:layout_constraintTop_toBottomOf="@+id/title" /> handleDisplaySettings()); } diff --git a/src/org/thoughtcrime/securesms/avatar/AvatarSelection.java b/src/org/thoughtcrime/securesms/avatar/AvatarSelection.java index c4f0ca3d64..6886b27c8d 100644 --- a/src/org/thoughtcrime/securesms/avatar/AvatarSelection.java +++ b/src/org/thoughtcrime/securesms/avatar/AvatarSelection.java @@ -5,6 +5,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.Build; import android.provider.MediaStore; import android.support.annotation.Nullable; import android.support.annotation.StringRes; @@ -43,7 +44,7 @@ public final class AvatarSelection { CropImage.activity(inputFile) .setGuidelines(CropImageView.Guidelines.ON) .setAspectRatio(1, 1) - .setCropShape(CropImageView.CropShape.OVAL) + .setCropShape(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? CropImageView.CropShape.RECTANGLE : CropImageView.CropShape.OVAL) .setOutputUri(outputFile) .setAllowRotation(true) .setAllowFlipping(true) diff --git a/src/org/thoughtcrime/securesms/components/AvatarImageView.java b/src/org/thoughtcrime/securesms/components/AvatarImageView.java index ce11690664..29d71720bd 100644 --- a/src/org/thoughtcrime/securesms/components/AvatarImageView.java +++ b/src/org/thoughtcrime/securesms/components/AvatarImageView.java @@ -11,22 +11,23 @@ import android.provider.ContactsContract; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.AppCompatImageView; -import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import android.view.ViewOutlineProvider; -import org.thoughtcrime.securesms.color.MaterialColor; +import com.bumptech.glide.load.engine.DiskCacheStrategy; + import org.thoughtcrime.securesms.contacts.avatars.ContactColors; -import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto; +import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; +import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto; import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.loki.JazzIdenticonDrawable; +import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientExporter; -import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.ThemeUtil; -import org.whispersystems.libsignal.util.guava.Optional; + +import java.util.Objects; import network.loki.messenger.R; @@ -52,7 +53,9 @@ public class AvatarImageView extends AppCompatImageView { private boolean inverted; private Paint outlinePaint; private OnClickListener listener; - private Recipient recipient; + + private @Nullable RecipientContactPhoto recipientContactPhoto; + private @NonNull Drawable unknownRecipientDrawable; public AvatarImageView(Context context) { super(context); @@ -75,23 +78,27 @@ public class AvatarImageView extends AppCompatImageView { outlinePaint = ThemeUtil.isDarkTheme(getContext()) ? DARK_THEME_OUTLINE_PAINT : LIGHT_THEME_OUTLINE_PAINT; setOutlineProvider(new ViewOutlineProvider() { - @Override public void getOutline(View view, Outline outline) { outline.setOval(0, 0, view.getWidth(), view.getHeight()); } }); setClipToOutline(true); + + unknownRecipientDrawable = new ResourceContactPhoto(R.drawable.ic_profile_default).asDrawable(getContext(), ContactColors.UNKNOWN_COLOR.toConversationColor(getContext()), inverted); } @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); - float cx = canvas.getWidth() / 2; - float cy = canvas.getHeight() / 2; - float radius = (canvas.getWidth() / 2) - (outlinePaint.getStrokeWidth() / 2); - + float width = getWidth() - getPaddingRight() - getPaddingLeft(); + float height = getHeight() - getPaddingBottom() - getPaddingTop(); + float cx = width / 2f; + float cy = height / 2f; + float radius = Math.min(cx, cy) - (outlinePaint.getStrokeWidth() / 2f); + + canvas.translate(getPaddingLeft(), getPaddingTop()); canvas.drawCircle(cx, cy, radius, outlinePaint); } @@ -104,36 +111,46 @@ public class AvatarImageView extends AppCompatImageView { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - updateImage(w, h); + if (recipientContactPhoto != null) { updateAvatar(recipientContactPhoto.recipient); } } public void update(String hexEncodedPublicKey) { Address address = Address.fromSerialized(hexEncodedPublicKey); - if (recipient == null || !address.equals(recipient.getAddress())) { - this.recipient = Recipient.from(getContext(), address, false); - updateImage(); - } + Recipient recipient = Recipient.from(getContext(), address, false); + updateAvatar(recipient); + } + + private void updateAvatar(Recipient recipient) { + setAvatar(GlideApp.with(getContext()), recipient, false); } public void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient recipient, boolean quickContactEnabled) { - if (this.recipient == null || !this.recipient.equals(recipient)) { - this.recipient = recipient; - updateImage(); - } - /* if (recipient != null) { - requestManager.load(recipient.getContactPhoto()) - .fallback(recipient.getFallbackContactPhotoDrawable(getContext(), inverted)) - .error(recipient.getFallbackContactPhotoDrawable(getContext(), inverted)) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .circleCrop() - .into(this); - setAvatarClickHandler(recipient, quickContactEnabled); + RecipientContactPhoto photo = new RecipientContactPhoto(recipient); + if (!photo.equals(recipientContactPhoto)) { + requestManager.clear(this); + recipientContactPhoto = photo; + + Drawable fallbackContactPhotoDrawable = photo.recipient.getFallbackContactPhotoDrawable(getContext(), inverted); + + if (photo.contactPhoto != null) { + requestManager.load(photo.contactPhoto) + .fallback(fallbackContactPhotoDrawable) + .error(fallbackContactPhotoDrawable) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .circleCrop() + .into(this); + } else { + setImageDrawable(fallbackContactPhotoDrawable); + } + } + setAvatarClickHandler(recipient, quickContactEnabled); } else { - setImageDrawable(new ResourceContactPhoto(R.drawable.ic_profile_default).asDrawable(getContext(), ContactColors.UNKNOWN_COLOR.toConversationColor(getContext()), inverted)); + recipientContactPhoto = null; + requestManager.clear(this); + setImageDrawable(unknownRecipientDrawable); super.setOnClickListener(listener); } - */ } public void clear(@NonNull GlideRequests glideRequests) { @@ -154,32 +171,25 @@ public class AvatarImageView extends AppCompatImageView { } } - private void updateImage() { updateImage(getWidth(), getHeight()); } + private static class RecipientContactPhoto { - private void updateImage(int w, int h) { - if (w == 0 || h == 0 || recipient == null) { return; } + private final @NonNull Recipient recipient; + private final @Nullable ContactPhoto contactPhoto; + private final boolean ready; - Drawable image; - Context context = this.getContext(); - - if (recipient.isGroupRecipient()) { - String name = Optional.fromNullable(recipient.getName()).or(Optional.fromNullable(TextSecurePreferences.getProfileName(context))).or(""); - MaterialColor fallbackColor = recipient.getColor(); - - if (fallbackColor == ContactColors.UNKNOWN_COLOR && !TextUtils.isEmpty(name)) { - fallbackColor = ContactColors.generateFor(name); - } - - image = new GeneratedContactPhoto(name, R.drawable.ic_profile_default).asDrawable(context, fallbackColor.toAvatarColor(context)); - } else { - // Default to primary device image - String ourPublicKey = TextSecurePreferences.getLocalNumber(context); - String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context); - String recipientAddress = recipient.getAddress().serialize(); - String profileAddress = (ourPrimaryDevice != null && ourPublicKey.equals(recipientAddress)) ? ourPrimaryDevice : recipientAddress; - image = new JazzIdenticonDrawable(w, h, profileAddress.toLowerCase()); + RecipientContactPhoto(@NonNull Recipient recipient) { + this.recipient = recipient; + this.ready = !recipient.isResolving(); + this.contactPhoto = recipient.getContactPhoto(); } - setImageDrawable(image); - } + public boolean equals(@Nullable RecipientContactPhoto other) { + if (other == null) return false; + + return other.recipient.equals(recipient) && + other.recipient.getColor().equals(recipient.getColor()) && + other.ready == ready && + Objects.equals(other.contactPhoto, contactPhoto); + } + } } diff --git a/src/org/thoughtcrime/securesms/loki/JazzIdenticonContactPhoto.kt b/src/org/thoughtcrime/securesms/loki/JazzIdenticonContactPhoto.kt new file mode 100644 index 0000000000..979f7e3e88 --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/JazzIdenticonContactPhoto.kt @@ -0,0 +1,22 @@ +package org.thoughtcrime.securesms.loki + +import android.content.Context +import android.graphics.drawable.Drawable +import android.support.v7.content.res.AppCompatResources +import network.loki.messenger.R +import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto + +class JazzIdenticonContactPhoto(val hexEncodedPublicKey: String) : FallbackContactPhoto { + override fun asDrawable(context: Context, color: Int): Drawable { + return asDrawable(context, color, false) + } + + override fun asDrawable(context: Context, color: Int, inverted: Boolean): Drawable { + val targetSize = context.resources.getDimensionPixelSize(R.dimen.contact_photo_target_size) + return JazzIdenticonDrawable(targetSize, targetSize, hexEncodedPublicKey) + } + + override fun asCallCard(context: Context): Drawable? { + return AppCompatResources.getDrawable(context, R.drawable.ic_person_large) + } +} \ 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 5cd135a943..73371622f8 100644 --- a/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java +++ b/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java @@ -5,6 +5,7 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.graphics.Outline; +import android.graphics.drawable.Drawable; import android.os.Build; import android.support.annotation.RequiresApi; import android.support.v7.preference.Preference; @@ -13,14 +14,17 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import android.view.ViewOutlineProvider; -import android.view.ViewTreeObserver; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; +import com.bumptech.glide.load.engine.DiskCacheStrategy; + +import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto; import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.loki.JazzIdenticonDrawable; import org.thoughtcrime.securesms.loki.MnemonicUtilities; +import org.thoughtcrime.securesms.mms.GlideApp; +import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.signalservice.loki.crypto.MnemonicCodec; @@ -99,28 +103,16 @@ public class ProfilePreference extends Preference { } }); avatarView.setClipToOutline(true); - avatarView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - int width = avatarView.getWidth(); - int height = avatarView.getHeight(); - if (width == 0 || height == 0) return true; - avatarView.getViewTreeObserver().removeOnPreDrawListener(this); - JazzIdenticonDrawable identicon = new JazzIdenticonDrawable(width, height, publicKey.toLowerCase()); - avatarView.setImageDrawable(identicon); - return true; - } - }); - - /* + Drawable fallback = Recipient.from(context, localAddress, false).getFallbackContactPhotoDrawable(context, false); GlideApp.with(getContext().getApplicationContext()) .load(new ProfileContactPhoto(localAddress, String.valueOf(TextSecurePreferences.getProfileAvatarId(getContext())))) - .error(new ResourceContactPhoto(R.drawable.ic_camera_alt_white_24dp).asDrawable(getContext(), getContext().getResources().getColor(R.color.grey_400))) + .fallback(fallback) + .error(fallback) .circleCrop() .diskCacheStrategy(DiskCacheStrategy.ALL) .into(avatarView); - */ + if (!TextUtils.isEmpty(profileName)) { profileNameView.setText(profileName); diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java index 0c5689cf2d..75f1a90e4b 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java @@ -44,10 +44,12 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState; import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.JazzIdenticonContactPhoto; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails; import org.thoughtcrime.securesms.util.FutureTaskListener; import org.thoughtcrime.securesms.util.ListenableFutureTask; +import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.util.guava.Optional; @@ -455,11 +457,15 @@ public class Recipient implements RecipientModifiedListener { } public synchronized @NonNull FallbackContactPhoto getFallbackContactPhoto() { - if (isLocalNumber) return new ResourceContactPhoto(R.drawable.ic_note_to_self); if (isResolving()) return new TransparentContactPhoto(); - else if (isGroupRecipient()) return new ResourceContactPhoto(R.drawable.ic_group_white_24dp, R.drawable.ic_group_large); - else if (!TextUtils.isEmpty(name)) return new GeneratedContactPhoto(name, R.drawable.ic_profile_default); - else return new ResourceContactPhoto(R.drawable.ic_profile_default, R.drawable.ic_person_large); + else if (isGroupRecipient()) return new GeneratedContactPhoto(name, R.drawable.ic_profile_default); + else { + String currentUser = TextSecurePreferences.getLocalNumber(context); + String recipientAddress = address.serialize(); + String primaryAddress = TextSecurePreferences.getMasterHexEncodedPublicKey(context); + String profileAddress = (recipientAddress.equalsIgnoreCase(currentUser) && primaryAddress != null) ? primaryAddress : recipientAddress; + return new JazzIdenticonContactPhoto(profileAddress); + } } public synchronized @Nullable ContactPhoto getContactPhoto() { diff --git a/src/org/thoughtcrime/securesms/util/FileProviderUtil.java b/src/org/thoughtcrime/securesms/util/FileProviderUtil.java index 5cf06dad75..930290e689 100644 --- a/src/org/thoughtcrime/securesms/util/FileProviderUtil.java +++ b/src/org/thoughtcrime/securesms/util/FileProviderUtil.java @@ -11,7 +11,7 @@ import java.io.File; public class FileProviderUtil { - private static final String AUTHORITY = "org.thoughtcrime.securesms.fileprovider"; + private static final String AUTHORITY = "network.loki.securesms.fileprovider"; public static Uri getUriFor(@NonNull Context context, @NonNull File file) { if (Build.VERSION.SDK_INT >= 24) return FileProvider.getUriForFile(context, AUTHORITY, file);