Migrate avatars to use recipientId filenames.
This commit is contained in:
parent
b7ce220600
commit
b89163bb14
17 changed files with 315 additions and 62 deletions
|
@ -41,6 +41,7 @@ message Sticker {
|
|||
|
||||
message Avatar {
|
||||
optional string name = 1;
|
||||
optional string recipientId = 3;
|
||||
optional uint32 length = 2;
|
||||
}
|
||||
|
||||
|
|
|
@ -188,7 +188,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
|||
Drawable fallback = new GeneratedContactPhoto(name, R.drawable.ic_profile_default).asDrawable(this, fallbackColor.toAvatarColor(this));
|
||||
|
||||
GlideApp.with(this)
|
||||
.load(new ProfileContactPhoto(recipient.requireAddress(), String.valueOf(TextSecurePreferences.getProfileAvatarId(this))))
|
||||
.load(new ProfileContactPhoto(recipient.getId(), String.valueOf(TextSecurePreferences.getProfileAvatarId(this))))
|
||||
.error(fallback)
|
||||
.circleCrop()
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
|
|
|
@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
|||
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints;
|
||||
import org.thoughtcrime.securesms.profiles.SystemProfileUtil;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
|
@ -263,14 +264,14 @@ public class CreateProfileActivity extends BaseActionBarActivity {
|
|||
}
|
||||
|
||||
private void initializeProfileAvatar(boolean excludeSystem) {
|
||||
Address ourAddress = Address.fromSerialized(TextSecurePreferences.getLocalNumber(this));
|
||||
RecipientId selfId = Recipient.self().getId();
|
||||
|
||||
if (AvatarHelper.getAvatarFile(this, ourAddress).exists() && AvatarHelper.getAvatarFile(this, ourAddress).length() > 0) {
|
||||
if (AvatarHelper.getAvatarFile(this, selfId).exists() && AvatarHelper.getAvatarFile(this, selfId).length() > 0) {
|
||||
new AsyncTask<Void, Void, byte[]>() {
|
||||
@Override
|
||||
protected byte[] doInBackground(Void... params) {
|
||||
try {
|
||||
return Util.readFully(AvatarHelper.getInputStreamFor(CreateProfileActivity.this, ourAddress));
|
||||
return Util.readFully(AvatarHelper.getInputStreamFor(CreateProfileActivity.this, selfId));
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return null;
|
||||
|
@ -373,7 +374,7 @@ public class CreateProfileActivity extends BaseActionBarActivity {
|
|||
|
||||
try {
|
||||
accountManager.setProfileAvatar(profileKey, avatar);
|
||||
AvatarHelper.setAvatar(CreateProfileActivity.this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), avatarBytes);
|
||||
AvatarHelper.setAvatar(CreateProfileActivity.this, Recipient.self().getId(), avatarBytes);
|
||||
TextSecurePreferences.setProfileAvatarId(CreateProfileActivity.this, new SecureRandom().nextInt());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
|
|
|
@ -206,7 +206,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
|||
}
|
||||
|
||||
private void setHeader(@NonNull Recipient recipient) {
|
||||
ContactPhoto contactPhoto = recipient.isLocalNumber() ? new ProfileContactPhoto(recipient.requireAddress(), String.valueOf(TextSecurePreferences.getProfileAvatarId(this)))
|
||||
ContactPhoto contactPhoto = recipient.isLocalNumber() ? new ProfileContactPhoto(recipient.getId(), String.valueOf(TextSecurePreferences.getProfileAvatarId(this)))
|
||||
: recipient.getContactPhoto();
|
||||
FallbackContactPhoto fallbackPhoto = recipient.isLocalNumber() ? new ResourceContactPhoto(R.drawable.ic_profile_default, R.drawable.ic_person_large)
|
||||
: recipient.getFallbackContactPhoto();
|
||||
|
|
|
@ -3475,6 +3475,21 @@ public final class BackupProtos {
|
|||
com.google.protobuf.ByteString
|
||||
getNameBytes();
|
||||
|
||||
// optional string recipientId = 3;
|
||||
/**
|
||||
* <code>optional string recipientId = 3;</code>
|
||||
*/
|
||||
boolean hasRecipientId();
|
||||
/**
|
||||
* <code>optional string recipientId = 3;</code>
|
||||
*/
|
||||
java.lang.String getRecipientId();
|
||||
/**
|
||||
* <code>optional string recipientId = 3;</code>
|
||||
*/
|
||||
com.google.protobuf.ByteString
|
||||
getRecipientIdBytes();
|
||||
|
||||
// optional uint32 length = 2;
|
||||
/**
|
||||
* <code>optional uint32 length = 2;</code>
|
||||
|
@ -3542,10 +3557,15 @@ public final class BackupProtos {
|
|||
break;
|
||||
}
|
||||
case 16: {
|
||||
bitField0_ |= 0x00000002;
|
||||
bitField0_ |= 0x00000004;
|
||||
length_ = input.readUInt32();
|
||||
break;
|
||||
}
|
||||
case 26: {
|
||||
bitField0_ |= 0x00000002;
|
||||
recipientId_ = input.readBytes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||
|
@ -3629,6 +3649,49 @@ public final class BackupProtos {
|
|||
}
|
||||
}
|
||||
|
||||
// optional string recipientId = 3;
|
||||
public static final int RECIPIENTID_FIELD_NUMBER = 3;
|
||||
private java.lang.Object recipientId_;
|
||||
/**
|
||||
* <code>optional string recipientId = 3;</code>
|
||||
*/
|
||||
public boolean hasRecipientId() {
|
||||
return ((bitField0_ & 0x00000002) == 0x00000002);
|
||||
}
|
||||
/**
|
||||
* <code>optional string recipientId = 3;</code>
|
||||
*/
|
||||
public java.lang.String getRecipientId() {
|
||||
java.lang.Object ref = recipientId_;
|
||||
if (ref instanceof java.lang.String) {
|
||||
return (java.lang.String) ref;
|
||||
} else {
|
||||
com.google.protobuf.ByteString bs =
|
||||
(com.google.protobuf.ByteString) ref;
|
||||
java.lang.String s = bs.toStringUtf8();
|
||||
if (bs.isValidUtf8()) {
|
||||
recipientId_ = s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>optional string recipientId = 3;</code>
|
||||
*/
|
||||
public com.google.protobuf.ByteString
|
||||
getRecipientIdBytes() {
|
||||
java.lang.Object ref = recipientId_;
|
||||
if (ref instanceof java.lang.String) {
|
||||
com.google.protobuf.ByteString b =
|
||||
com.google.protobuf.ByteString.copyFromUtf8(
|
||||
(java.lang.String) ref);
|
||||
recipientId_ = b;
|
||||
return b;
|
||||
} else {
|
||||
return (com.google.protobuf.ByteString) ref;
|
||||
}
|
||||
}
|
||||
|
||||
// optional uint32 length = 2;
|
||||
public static final int LENGTH_FIELD_NUMBER = 2;
|
||||
private int length_;
|
||||
|
@ -3636,7 +3699,7 @@ public final class BackupProtos {
|
|||
* <code>optional uint32 length = 2;</code>
|
||||
*/
|
||||
public boolean hasLength() {
|
||||
return ((bitField0_ & 0x00000002) == 0x00000002);
|
||||
return ((bitField0_ & 0x00000004) == 0x00000004);
|
||||
}
|
||||
/**
|
||||
* <code>optional uint32 length = 2;</code>
|
||||
|
@ -3647,6 +3710,7 @@ public final class BackupProtos {
|
|||
|
||||
private void initFields() {
|
||||
name_ = "";
|
||||
recipientId_ = "";
|
||||
length_ = 0;
|
||||
}
|
||||
private byte memoizedIsInitialized = -1;
|
||||
|
@ -3664,9 +3728,12 @@ public final class BackupProtos {
|
|||
if (((bitField0_ & 0x00000001) == 0x00000001)) {
|
||||
output.writeBytes(1, getNameBytes());
|
||||
}
|
||||
if (((bitField0_ & 0x00000002) == 0x00000002)) {
|
||||
if (((bitField0_ & 0x00000004) == 0x00000004)) {
|
||||
output.writeUInt32(2, length_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000002) == 0x00000002)) {
|
||||
output.writeBytes(3, getRecipientIdBytes());
|
||||
}
|
||||
getUnknownFields().writeTo(output);
|
||||
}
|
||||
|
||||
|
@ -3680,10 +3747,14 @@ public final class BackupProtos {
|
|||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeBytesSize(1, getNameBytes());
|
||||
}
|
||||
if (((bitField0_ & 0x00000002) == 0x00000002)) {
|
||||
if (((bitField0_ & 0x00000004) == 0x00000004)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeUInt32Size(2, length_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000002) == 0x00000002)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeBytesSize(3, getRecipientIdBytes());
|
||||
}
|
||||
size += getUnknownFields().getSerializedSize();
|
||||
memoizedSerializedSize = size;
|
||||
return size;
|
||||
|
@ -3802,8 +3873,10 @@ public final class BackupProtos {
|
|||
super.clear();
|
||||
name_ = "";
|
||||
bitField0_ = (bitField0_ & ~0x00000001);
|
||||
length_ = 0;
|
||||
recipientId_ = "";
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
length_ = 0;
|
||||
bitField0_ = (bitField0_ & ~0x00000004);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -3839,6 +3912,10 @@ public final class BackupProtos {
|
|||
if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
|
||||
to_bitField0_ |= 0x00000002;
|
||||
}
|
||||
result.recipientId_ = recipientId_;
|
||||
if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
|
||||
to_bitField0_ |= 0x00000004;
|
||||
}
|
||||
result.length_ = length_;
|
||||
result.bitField0_ = to_bitField0_;
|
||||
onBuilt();
|
||||
|
@ -3861,6 +3938,11 @@ public final class BackupProtos {
|
|||
name_ = other.name_;
|
||||
onChanged();
|
||||
}
|
||||
if (other.hasRecipientId()) {
|
||||
bitField0_ |= 0x00000002;
|
||||
recipientId_ = other.recipientId_;
|
||||
onChanged();
|
||||
}
|
||||
if (other.hasLength()) {
|
||||
setLength(other.getLength());
|
||||
}
|
||||
|
@ -3965,13 +4047,87 @@ public final class BackupProtos {
|
|||
return this;
|
||||
}
|
||||
|
||||
// optional string recipientId = 3;
|
||||
private java.lang.Object recipientId_ = "";
|
||||
/**
|
||||
* <code>optional string recipientId = 3;</code>
|
||||
*/
|
||||
public boolean hasRecipientId() {
|
||||
return ((bitField0_ & 0x00000002) == 0x00000002);
|
||||
}
|
||||
/**
|
||||
* <code>optional string recipientId = 3;</code>
|
||||
*/
|
||||
public java.lang.String getRecipientId() {
|
||||
java.lang.Object ref = recipientId_;
|
||||
if (!(ref instanceof java.lang.String)) {
|
||||
java.lang.String s = ((com.google.protobuf.ByteString) ref)
|
||||
.toStringUtf8();
|
||||
recipientId_ = s;
|
||||
return s;
|
||||
} else {
|
||||
return (java.lang.String) ref;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>optional string recipientId = 3;</code>
|
||||
*/
|
||||
public com.google.protobuf.ByteString
|
||||
getRecipientIdBytes() {
|
||||
java.lang.Object ref = recipientId_;
|
||||
if (ref instanceof String) {
|
||||
com.google.protobuf.ByteString b =
|
||||
com.google.protobuf.ByteString.copyFromUtf8(
|
||||
(java.lang.String) ref);
|
||||
recipientId_ = b;
|
||||
return b;
|
||||
} else {
|
||||
return (com.google.protobuf.ByteString) ref;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>optional string recipientId = 3;</code>
|
||||
*/
|
||||
public Builder setRecipientId(
|
||||
java.lang.String value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
bitField0_ |= 0x00000002;
|
||||
recipientId_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional string recipientId = 3;</code>
|
||||
*/
|
||||
public Builder clearRecipientId() {
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
recipientId_ = getDefaultInstance().getRecipientId();
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional string recipientId = 3;</code>
|
||||
*/
|
||||
public Builder setRecipientIdBytes(
|
||||
com.google.protobuf.ByteString value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
bitField0_ |= 0x00000002;
|
||||
recipientId_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
// optional uint32 length = 2;
|
||||
private int length_ ;
|
||||
/**
|
||||
* <code>optional uint32 length = 2;</code>
|
||||
*/
|
||||
public boolean hasLength() {
|
||||
return ((bitField0_ & 0x00000002) == 0x00000002);
|
||||
return ((bitField0_ & 0x00000004) == 0x00000004);
|
||||
}
|
||||
/**
|
||||
* <code>optional uint32 length = 2;</code>
|
||||
|
@ -3983,7 +4139,7 @@ public final class BackupProtos {
|
|||
* <code>optional uint32 length = 2;</code>
|
||||
*/
|
||||
public Builder setLength(int value) {
|
||||
bitField0_ |= 0x00000002;
|
||||
bitField0_ |= 0x00000004;
|
||||
length_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
|
@ -3992,7 +4148,7 @@ public final class BackupProtos {
|
|||
* <code>optional uint32 length = 2;</code>
|
||||
*/
|
||||
public Builder clearLength() {
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
bitField0_ = (bitField0_ & ~0x00000004);
|
||||
length_ = 0;
|
||||
onChanged();
|
||||
return this;
|
||||
|
@ -6692,19 +6848,19 @@ public final class BackupProtos {
|
|||
"\030\001 \001(\t\022\013\n\003key\030\002 \001(\t\022\r\n\005value\030\003 \001(\t\"A\n\nAt" +
|
||||
"tachment\022\r\n\005rowId\030\001 \001(\004\022\024\n\014attachmentId\030" +
|
||||
"\002 \001(\004\022\016\n\006length\030\003 \001(\r\"(\n\007Sticker\022\r\n\005rowI",
|
||||
"d\030\001 \001(\004\022\016\n\006length\030\002 \001(\r\"&\n\006Avatar\022\014\n\004nam" +
|
||||
"e\030\001 \001(\t\022\016\n\006length\030\002 \001(\r\"\"\n\017DatabaseVersi" +
|
||||
"on\022\017\n\007version\030\001 \001(\r\"\"\n\006Header\022\n\n\002iv\030\001 \001(" +
|
||||
"\014\022\014\n\004salt\030\002 \001(\014\"\245\002\n\013BackupFrame\022\036\n\006heade" +
|
||||
"r\030\001 \001(\0132\016.signal.Header\022\'\n\tstatement\030\002 \001" +
|
||||
"(\0132\024.signal.SqlStatement\022,\n\npreference\030\003" +
|
||||
" \001(\0132\030.signal.SharedPreference\022&\n\nattach" +
|
||||
"ment\030\004 \001(\0132\022.signal.Attachment\022(\n\007versio" +
|
||||
"n\030\005 \001(\0132\027.signal.DatabaseVersion\022\013\n\003end\030" +
|
||||
"\006 \001(\010\022\036\n\006avatar\030\007 \001(\0132\016.signal.Avatar\022 \n",
|
||||
"\007sticker\030\010 \001(\0132\017.signal.StickerB1\n!org.t" +
|
||||
"houghtcrime.securesms.backupB\014BackupProt" +
|
||||
"os"
|
||||
"d\030\001 \001(\004\022\016\n\006length\030\002 \001(\r\";\n\006Avatar\022\014\n\004nam" +
|
||||
"e\030\001 \001(\t\022\023\n\013recipientId\030\003 \001(\t\022\016\n\006length\030\002" +
|
||||
" \001(\r\"\"\n\017DatabaseVersion\022\017\n\007version\030\001 \001(\r" +
|
||||
"\"\"\n\006Header\022\n\n\002iv\030\001 \001(\014\022\014\n\004salt\030\002 \001(\014\"\245\002\n" +
|
||||
"\013BackupFrame\022\036\n\006header\030\001 \001(\0132\016.signal.He" +
|
||||
"ader\022\'\n\tstatement\030\002 \001(\0132\024.signal.SqlStat" +
|
||||
"ement\022,\n\npreference\030\003 \001(\0132\030.signal.Share" +
|
||||
"dPreference\022&\n\nattachment\030\004 \001(\0132\022.signal" +
|
||||
".Attachment\022(\n\007version\030\005 \001(\0132\027.signal.Da" +
|
||||
"tabaseVersion\022\013\n\003end\030\006 \001(\010\022\036\n\006avatar\030\007 \001",
|
||||
"(\0132\016.signal.Avatar\022 \n\007sticker\030\010 \001(\0132\017.si" +
|
||||
"gnal.StickerB1\n!org.thoughtcrime.secures" +
|
||||
"ms.backupB\014BackupProtos"
|
||||
};
|
||||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
||||
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
||||
|
@ -6746,7 +6902,7 @@ public final class BackupProtos {
|
|||
internal_static_signal_Avatar_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_signal_Avatar_descriptor,
|
||||
new java.lang.String[] { "Name", "Length", });
|
||||
new java.lang.String[] { "Name", "RecipientId", "Length", });
|
||||
internal_static_signal_DatabaseVersion_descriptor =
|
||||
getDescriptor().getMessageTypes().get(5);
|
||||
internal_static_signal_DatabaseVersion_fieldAccessorTable = new
|
||||
|
|
|
@ -326,7 +326,7 @@ public class FullBackupExporter extends FullBackupBase {
|
|||
public void write(@NonNull String avatarName, @NonNull InputStream in, long size) throws IOException {
|
||||
write(outputStream, BackupProtos.BackupFrame.newBuilder()
|
||||
.setAvatar(BackupProtos.Avatar.newBuilder()
|
||||
.setName(avatarName)
|
||||
.setRecipientId(avatarName)
|
||||
.setLength(Util.toIntExact(size))
|
||||
.build())
|
||||
.build());
|
||||
|
|
|
@ -27,6 +27,8 @@ import org.thoughtcrime.securesms.database.StickerDatabase;
|
|||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.Conversions;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.kdf.HKDFv3;
|
||||
|
@ -163,7 +165,10 @@ public class FullBackupImporter extends FullBackupBase {
|
|||
}
|
||||
|
||||
private static void processAvatar(@NonNull Context context, @NonNull BackupProtos.Avatar avatar, @NonNull BackupRecordInputStream inputStream) throws IOException {
|
||||
inputStream.readAttachmentTo(new FileOutputStream(AvatarHelper.getAvatarFile(context, Address.fromSerialized(PhoneNumberFormatter.get(context).format(avatar.getName())))), avatar.getLength());
|
||||
Recipient recipient = avatar.hasRecipientId() ? Recipient.resolved(RecipientId.from(avatar.getRecipientId()))
|
||||
: Recipient.external(context, avatar.getName());
|
||||
|
||||
inputStream.readAttachmentTo(new FileOutputStream(AvatarHelper.getAvatarFile(context, recipient.getId())), avatar.getLength());
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
|
|
|
@ -6,8 +6,9 @@ import android.net.Uri;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -16,22 +17,22 @@ import java.security.MessageDigest;
|
|||
|
||||
public class ProfileContactPhoto implements ContactPhoto {
|
||||
|
||||
private final @NonNull Address address;
|
||||
private final @NonNull RecipientId recipient;
|
||||
private final @NonNull String avatarObject;
|
||||
|
||||
public ProfileContactPhoto(@NonNull Address address, @NonNull String avatarObject) {
|
||||
this.address = address;
|
||||
public ProfileContactPhoto(@NonNull RecipientId recipient, @NonNull String avatarObject) {
|
||||
this.recipient = recipient;
|
||||
this.avatarObject = avatarObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openInputStream(Context context) throws IOException {
|
||||
return AvatarHelper.getInputStreamFor(context, address);
|
||||
public @NonNull InputStream openInputStream(Context context) throws IOException {
|
||||
return AvatarHelper.getInputStreamFor(context, recipient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Uri getUri(@NonNull Context context) {
|
||||
File avatarFile = AvatarHelper.getAvatarFile(context, address);
|
||||
File avatarFile = AvatarHelper.getAvatarFile(context, recipient);
|
||||
return avatarFile.exists() ? Uri.fromFile(avatarFile) : null;
|
||||
}
|
||||
|
||||
|
@ -42,7 +43,7 @@ public class ProfileContactPhoto implements ContactPhoto {
|
|||
|
||||
@Override
|
||||
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
|
||||
messageDigest.update(address.serialize().getBytes());
|
||||
messageDigest.update(recipient.serialize().getBytes());
|
||||
messageDigest.update(avatarObject.getBytes());
|
||||
}
|
||||
|
||||
|
@ -52,11 +53,11 @@ public class ProfileContactPhoto implements ContactPhoto {
|
|||
|
||||
ProfileContactPhoto that = (ProfileContactPhoto)other;
|
||||
|
||||
return this.address.equals(that.address) && this.avatarObject.equals(that.avatarObject);
|
||||
return this.recipient.equals(that.recipient) && this.avatarObject.equals(that.avatarObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return address.hashCode() ^ avatarObject.hashCode();
|
||||
return recipient.hashCode() ^ avatarObject.hashCode();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintOb
|
|||
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdFollowUpJobMigration;
|
||||
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdFollowUpJobMigration2;
|
||||
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration;
|
||||
import org.thoughtcrime.securesms.migrations.AvatarMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.DatabaseMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.MigrationCompleteJob;
|
||||
|
@ -82,6 +83,7 @@ public final class JobManagerFactories {
|
|||
put(UpdateApkJob.KEY, new UpdateApkJob.Factory());
|
||||
|
||||
// Migrations
|
||||
put(AvatarMigrationJob.KEY, new AvatarMigrationJob.Factory());
|
||||
put(DatabaseMigrationJob.KEY, new DatabaseMigrationJob.Factory());
|
||||
put(LegacyMigrationJob.KEY, new LegacyMigrationJob.Factory());
|
||||
put(MigrationCompleteJob.KEY, new MigrationCompleteJob.Factory());
|
||||
|
|
|
@ -86,7 +86,7 @@ public class RetrieveProfileAvatarJob extends BaseJob {
|
|||
|
||||
if (TextUtils.isEmpty(profileAvatar)) {
|
||||
Log.w(TAG, "Removing profile avatar (no url) for: " + recipient.requireAddress().serialize());
|
||||
AvatarHelper.delete(context, recipient.requireAddress());
|
||||
AvatarHelper.delete(context, recipient.getId());
|
||||
database.setProfileAvatar(recipient.getId(), profileAvatar);
|
||||
return;
|
||||
}
|
||||
|
@ -104,11 +104,11 @@ public class RetrieveProfileAvatarJob extends BaseJob {
|
|||
throw new IOException("Failed to copy stream. Likely a Conscrypt issue.", e);
|
||||
}
|
||||
|
||||
decryptDestination.renameTo(AvatarHelper.getAvatarFile(context, recipient.requireAddress()));
|
||||
decryptDestination.renameTo(AvatarHelper.getAvatarFile(context, recipient.getId()));
|
||||
} catch (PushNetworkException e) {
|
||||
if (e.getCause() instanceof NonSuccessfulResponseCodeException) {
|
||||
Log.w(TAG, "Removing profile avatar (no image available) for: " + recipient.requireAddress().serialize());
|
||||
AvatarHelper.delete(context, recipient.requireAddress());
|
||||
AvatarHelper.delete(context, recipient.getId());
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ public class RotateProfileKeyJob extends BaseJob {
|
|||
|
||||
private @Nullable StreamDetails getProfileAvatar() {
|
||||
try {
|
||||
File avatarFile = AvatarHelper.getAvatarFile(context, Recipient.self().requireAddress());
|
||||
File avatarFile = AvatarHelper.getAvatarFile(context, Recipient.self().getId());
|
||||
|
||||
if (avatarFile.exists()) {
|
||||
return new StreamDetails(new FileInputStream(avatarFile), "image/jpeg", avatarFile.length());
|
||||
|
|
|
@ -38,13 +38,14 @@ public class ApplicationMigrations {
|
|||
|
||||
private static final int LEGACY_CANONICAL_VERSION = 455;
|
||||
|
||||
public static final int CURRENT_VERSION = 4;
|
||||
public static final int CURRENT_VERSION = 5;
|
||||
|
||||
private static final class Version {
|
||||
static final int LEGACY = 1;
|
||||
static final int RECIPIENT_ID = 2;
|
||||
static final int RECIPIENT_SEARCH = 3;
|
||||
static final int RECIPIENT_CLEANUP = 4;
|
||||
static final int AVATAR_MIGRATION = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,6 +174,10 @@ public class ApplicationMigrations {
|
|||
jobs.put(Version.RECIPIENT_CLEANUP, new DatabaseMigrationJob());
|
||||
}
|
||||
|
||||
if (lastSeenVersion < Version.AVATAR_MIGRATION) {
|
||||
jobs.put(Version.AVATAR_MIGRATION, new AvatarMigrationJob());
|
||||
}
|
||||
|
||||
return jobs;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
package org.thoughtcrime.securesms.migrations;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Previously, we used a recipient's address as the filename for their avatar. We want to use
|
||||
* recipientId's instead in preparation for UUIDs.
|
||||
*/
|
||||
public class AvatarMigrationJob extends MigrationJob {
|
||||
|
||||
public static final String KEY = "AvatarMigrationJob";
|
||||
|
||||
private static final String TAG = Log.tag(AvatarMigrationJob.class);
|
||||
|
||||
AvatarMigrationJob() {
|
||||
this(new Parameters.Builder().build());
|
||||
}
|
||||
|
||||
private AvatarMigrationJob(@NonNull Parameters parameters) {
|
||||
super(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUiBlocking() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performMigration() {
|
||||
File oldDirectory = new File(context.getFilesDir(), "avatars");
|
||||
File[] files = oldDirectory.listFiles();
|
||||
|
||||
Log.i(TAG, "Preparing to move " + files.length + " avatars.");
|
||||
|
||||
for (File file : files) {
|
||||
try {
|
||||
Recipient recipient = Recipient.external(context, file.getName());
|
||||
byte[] data = Util.readFully(new FileInputStream(file));
|
||||
|
||||
AvatarHelper.setAvatar(context, recipient.getId(), data);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed to copy avatar file. Skipping it.", e);
|
||||
} finally {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
oldDirectory.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean shouldRetry(@NonNull Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class Factory implements Job.Factory<AvatarMigrationJob> {
|
||||
@Override
|
||||
public @NonNull AvatarMigrationJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
return new AvatarMigrationJob(parameters);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@ abstract class MigrationJob extends Job {
|
|||
@Override
|
||||
public @NonNull Result run() {
|
||||
try {
|
||||
Log.i(TAG, "About to run " + getClass().getSimpleName());
|
||||
performMigration();
|
||||
return Result.success();
|
||||
} catch (RuntimeException e) {
|
||||
|
@ -54,7 +55,7 @@ abstract class MigrationJob extends Job {
|
|||
|
||||
@Override
|
||||
public void onCanceled() {
|
||||
throw new AssertionError("This job should never fail.");
|
||||
throw new AssertionError("This job should never fail. " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
|
|||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
public class ProfilePreference extends Preference {
|
||||
|
@ -64,11 +65,11 @@ public class ProfilePreference extends Preference {
|
|||
public void refresh() {
|
||||
if (profileNumberView == null) return;
|
||||
|
||||
final Address localAddress = Address.fromSerialized(TextSecurePreferences.getLocalNumber(getContext()));
|
||||
final Recipient self = Recipient.self();
|
||||
final String profileName = TextSecurePreferences.getProfileName(getContext());
|
||||
|
||||
GlideApp.with(getContext().getApplicationContext())
|
||||
.load(new ProfileContactPhoto(localAddress, String.valueOf(TextSecurePreferences.getProfileAvatarId(getContext()))))
|
||||
.load(new ProfileContactPhoto(self.getId(), String.valueOf(TextSecurePreferences.getProfileAvatarId(getContext()))))
|
||||
.error(new ResourceContactPhoto(R.drawable.ic_camera_alt_white_24dp).asDrawable(getContext(), getContext().getResources().getColor(R.color.grey_400)))
|
||||
.circleCrop()
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
|
@ -78,6 +79,6 @@ public class ProfilePreference extends Preference {
|
|||
profileNameView.setText(profileName);
|
||||
}
|
||||
|
||||
profileNumberView.setText(localAddress.toPhoneString());
|
||||
profileNumberView.setText(self.requireAddress().toPhoneString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import androidx.annotation.Nullable;
|
|||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
@ -21,7 +22,7 @@ public class AvatarHelper {
|
|||
|
||||
private static final String AVATAR_DIRECTORY = "avatars";
|
||||
|
||||
public static InputStream getInputStreamFor(@NonNull Context context, @NonNull Address address)
|
||||
public static InputStream getInputStreamFor(@NonNull Context context, @NonNull RecipientId address)
|
||||
throws IOException
|
||||
{
|
||||
return new FileInputStream(getAvatarFile(context, address));
|
||||
|
@ -35,24 +36,24 @@ public class AvatarHelper {
|
|||
else return Stream.of(results).toList();
|
||||
}
|
||||
|
||||
public static void delete(@NonNull Context context, @NonNull Address address) {
|
||||
getAvatarFile(context, address).delete();
|
||||
public static void delete(@NonNull Context context, @NonNull RecipientId recipientId) {
|
||||
getAvatarFile(context, recipientId).delete();
|
||||
}
|
||||
|
||||
public static @NonNull File getAvatarFile(@NonNull Context context, @NonNull Address address) {
|
||||
public static @NonNull File getAvatarFile(@NonNull Context context, @NonNull RecipientId recipientId) {
|
||||
File avatarDirectory = new File(context.getFilesDir(), AVATAR_DIRECTORY);
|
||||
avatarDirectory.mkdirs();
|
||||
|
||||
return new File(avatarDirectory, new File(address.serialize()).getName());
|
||||
return new File(avatarDirectory, new File(recipientId.serialize()).getName());
|
||||
}
|
||||
|
||||
public static void setAvatar(@NonNull Context context, @NonNull Address address, @Nullable byte[] data)
|
||||
public static void setAvatar(@NonNull Context context, @NonNull RecipientId recipientId, @Nullable byte[] data)
|
||||
throws IOException
|
||||
{
|
||||
if (data == null) {
|
||||
delete(context, address);
|
||||
delete(context, recipientId);
|
||||
} else {
|
||||
FileOutputStream out = new FileOutputStream(getAvatarFile(context, address));
|
||||
FileOutputStream out = new FileOutputStream(getAvatarFile(context, recipientId));
|
||||
out.write(data);
|
||||
out.close();
|
||||
}
|
||||
|
|
|
@ -302,7 +302,7 @@ public class Recipient {
|
|||
if (localNumber) return null;
|
||||
else if (isGroupInternal() && groupAvatarId.isPresent()) return new GroupRecordContactPhoto(address, groupAvatarId.get());
|
||||
else if (systemContactPhoto != null) return new SystemContactPhoto(address, systemContactPhoto, 0);
|
||||
else if (profileAvatar != null) return new ProfileContactPhoto(address, profileAvatar);
|
||||
else if (profileAvatar != null) return new ProfileContactPhoto(id, profileAvatar);
|
||||
else return null;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue