Migrate avatars to use recipientId filenames.

master
Greyson Parrelli 2019-09-23 11:37:01 -04:00
parent b7ce220600
commit b89163bb14
17 changed files with 315 additions and 62 deletions

View File

@ -40,8 +40,9 @@ message Sticker {
}
message Avatar {
optional string name = 1;
optional uint32 length = 2;
optional string name = 1;
optional string recipientId = 3;
optional uint32 length = 2;
}
message DatabaseVersion {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 String profileName = TextSecurePreferences.getProfileName(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());
}
}

View File

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

View File

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