mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-11 11:08:34 +00:00
Groups V2 avatar download job.
This commit is contained in:
parent
e2d3a43593
commit
2b1ffac564
@ -380,7 +380,7 @@ public final class GroupDatabase extends Database {
|
|||||||
/**
|
/**
|
||||||
* Used to bust the Glide cache when an avatar changes.
|
* Used to bust the Glide cache when an avatar changes.
|
||||||
*/
|
*/
|
||||||
public void onAvatarUpdated(@NonNull GroupId.V1 groupId, boolean hasAvatar) {
|
public void onAvatarUpdated(@NonNull GroupId.Push groupId, boolean hasAvatar) {
|
||||||
ContentValues contentValues = new ContentValues(1);
|
ContentValues contentValues = new ContentValues(1);
|
||||||
contentValues.put(AVATAR_ID, hasAvatar ? Math.abs(new SecureRandom().nextLong()) : 0);
|
contentValues.put(AVATAR_ID, hasAvatar ? Math.abs(new SecureRandom().nextLong()) : 0);
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ public class ApplicationDependencies {
|
|||||||
private static FrameRateTracker frameRateTracker;
|
private static FrameRateTracker frameRateTracker;
|
||||||
private static KeyValueStore keyValueStore;
|
private static KeyValueStore keyValueStore;
|
||||||
private static MegaphoneRepository megaphoneRepository;
|
private static MegaphoneRepository megaphoneRepository;
|
||||||
|
private static GroupsV2Operations groupsV2Operations;
|
||||||
|
|
||||||
public static synchronized void init(@NonNull Application application, @NonNull Provider provider) {
|
public static synchronized void init(@NonNull Application application, @NonNull Provider provider) {
|
||||||
if (ApplicationDependencies.application != null || ApplicationDependencies.provider != null) {
|
if (ApplicationDependencies.application != null || ApplicationDependencies.provider != null) {
|
||||||
@ -71,6 +72,16 @@ public class ApplicationDependencies {
|
|||||||
return accountManager;
|
return accountManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static synchronized @NonNull GroupsV2Operations getGroupsV2Operations() {
|
||||||
|
assertInitialization();
|
||||||
|
|
||||||
|
if (groupsV2Operations == null) {
|
||||||
|
groupsV2Operations = provider.provideGroupsV2Operations();
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupsV2Operations;
|
||||||
|
}
|
||||||
|
|
||||||
public static synchronized @NonNull KeyBackupService getKeyBackupService() {
|
public static synchronized @NonNull KeyBackupService getKeyBackupService() {
|
||||||
return getSignalServiceAccountManager().getKeyBackupService(IasKeyStore.getIasKeyStore(application),
|
return getSignalServiceAccountManager().getKeyBackupService(IasKeyStore.getIasKeyStore(application),
|
||||||
BuildConfig.KBS_ENCLAVE_NAME,
|
BuildConfig.KBS_ENCLAVE_NAME,
|
||||||
|
@ -15,7 +15,7 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
|
|||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
|
import org.thoughtcrime.securesms.jobs.AvatarGroupsV1DownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
|
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
@ -228,7 +228,7 @@ public final class GroupV1MessageProcessor {
|
|||||||
{
|
{
|
||||||
if (group.getAvatar().isPresent()) {
|
if (group.getAvatar().isPresent()) {
|
||||||
ApplicationDependencies.getJobManager()
|
ApplicationDependencies.getJobManager()
|
||||||
.add(new AvatarDownloadJob(GroupId.v1(group.getGroupId())));
|
.add(new AvatarGroupsV1DownloadJob(GroupId.v1(group.getGroupId())));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -5,7 +5,7 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.AttachmentUploadJob;
|
import org.thoughtcrime.securesms.jobs.AttachmentUploadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
|
import org.thoughtcrime.securesms.jobs.AvatarGroupsV1DownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob;
|
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob;
|
||||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
||||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||||
@ -54,7 +54,7 @@ public class WorkManagerFactoryMappings {
|
|||||||
private static final Map<String, String> FACTORY_MAP = new HashMap<String, String>() {{
|
private static final Map<String, String> FACTORY_MAP = new HashMap<String, String>() {{
|
||||||
put(AttachmentDownloadJob.class.getName(), AttachmentDownloadJob.KEY);
|
put(AttachmentDownloadJob.class.getName(), AttachmentDownloadJob.KEY);
|
||||||
put(AttachmentUploadJob.class.getName(), AttachmentUploadJob.KEY);
|
put(AttachmentUploadJob.class.getName(), AttachmentUploadJob.KEY);
|
||||||
put(AvatarDownloadJob.class.getName(), AvatarDownloadJob.KEY);
|
put("AvatarDownloadJob", AvatarGroupsV1DownloadJob.KEY);
|
||||||
put(CleanPreKeysJob.class.getName(), CleanPreKeysJob.KEY);
|
put(CleanPreKeysJob.class.getName(), CleanPreKeysJob.KEY);
|
||||||
put(CreateSignedPreKeyJob.class.getName(), CreateSignedPreKeyJob.KEY);
|
put(CreateSignedPreKeyJob.class.getName(), CreateSignedPreKeyJob.KEY);
|
||||||
put(DirectoryRefreshJob.class.getName(), DirectoryRefreshJob.KEY);
|
put(DirectoryRefreshJob.class.getName(), DirectoryRefreshJob.KEY);
|
||||||
|
@ -24,19 +24,17 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
public class AvatarDownloadJob extends BaseJob {
|
public final class AvatarGroupsV1DownloadJob extends BaseJob {
|
||||||
|
|
||||||
public static final String KEY = "AvatarDownloadJob";
|
public static final String KEY = "AvatarDownloadJob";
|
||||||
|
|
||||||
private static final String TAG = AvatarDownloadJob.class.getSimpleName();
|
private static final String TAG = Log.tag(AvatarGroupsV1DownloadJob.class);
|
||||||
|
|
||||||
private static final int MAX_AVATAR_SIZE = 20 * 1024 * 1024;
|
|
||||||
|
|
||||||
private static final String KEY_GROUP_ID = "group_id";
|
private static final String KEY_GROUP_ID = "group_id";
|
||||||
|
|
||||||
private @NonNull GroupId.V1 groupId;
|
@NonNull private final GroupId.V1 groupId;
|
||||||
|
|
||||||
public AvatarDownloadJob(@NonNull GroupId.V1 groupId) {
|
public AvatarGroupsV1DownloadJob(@NonNull GroupId.V1 groupId) {
|
||||||
this(new Job.Parameters.Builder()
|
this(new Job.Parameters.Builder()
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
.addConstraint(NetworkConstraint.KEY)
|
||||||
.setMaxAttempts(10)
|
.setMaxAttempts(10)
|
||||||
@ -44,7 +42,7 @@ public class AvatarDownloadJob extends BaseJob {
|
|||||||
groupId);
|
groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AvatarDownloadJob(@NonNull Job.Parameters parameters, @NonNull GroupId.V1 groupId) {
|
private AvatarGroupsV1DownloadJob(@NonNull Job.Parameters parameters, @NonNull GroupId.V1 groupId) {
|
||||||
super(parameters);
|
super(parameters);
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
}
|
}
|
||||||
@ -111,10 +109,10 @@ public class AvatarDownloadJob extends BaseJob {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<AvatarDownloadJob> {
|
public static final class Factory implements Job.Factory<AvatarGroupsV1DownloadJob> {
|
||||||
@Override
|
@Override
|
||||||
public @NonNull AvatarDownloadJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
public @NonNull AvatarGroupsV1DownloadJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||||
return new AvatarDownloadJob(parameters, GroupId.parse(data.getString(KEY_GROUP_ID)).requireV1());
|
return new AvatarGroupsV1DownloadJob(parameters, GroupId.parse(data.getString(KEY_GROUP_ID)).requireV1());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,131 @@
|
|||||||
|
package org.thoughtcrime.securesms.jobs;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.signal.zkgroup.VerificationFailedException;
|
||||||
|
import org.signal.zkgroup.groups.GroupSecretParams;
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||||
|
import org.thoughtcrime.securesms.util.ByteUnit;
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||||
|
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
|
||||||
|
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public final class AvatarGroupsV2DownloadJob extends BaseJob {
|
||||||
|
|
||||||
|
public static final String KEY = "AvatarGroupsV2DownloadJob";
|
||||||
|
|
||||||
|
private static final String TAG = Log.tag(AvatarGroupsV2DownloadJob.class);
|
||||||
|
|
||||||
|
private static final long AVATAR_DOWNLOAD_FAIL_SAFE_MAX_SIZE = ByteUnit.MEGABYTES.toBytes(5);
|
||||||
|
|
||||||
|
private static final String KEY_GROUP_ID = "group_id";
|
||||||
|
private static final String CDN_KEY = "cdn_key";
|
||||||
|
|
||||||
|
private final GroupId.V2 groupId;
|
||||||
|
private final String cdnKey;
|
||||||
|
|
||||||
|
public AvatarGroupsV2DownloadJob(@NonNull GroupId.V2 groupId, @NonNull String cdnKey) {
|
||||||
|
this(new Parameters.Builder()
|
||||||
|
.addConstraint(NetworkConstraint.KEY)
|
||||||
|
.setQueue("AvatarGroupsV2DownloadJob::" + groupId)
|
||||||
|
.setMaxAttempts(10)
|
||||||
|
.build(),
|
||||||
|
groupId,
|
||||||
|
cdnKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AvatarGroupsV2DownloadJob(@NonNull Parameters parameters, @NonNull GroupId.V2 groupId, @NonNull String cdnKey) {
|
||||||
|
super(parameters);
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.cdnKey = cdnKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Data serialize() {
|
||||||
|
return new Data.Builder()
|
||||||
|
.putString(KEY_GROUP_ID, groupId.toString())
|
||||||
|
.putString(CDN_KEY, cdnKey)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull String getFactoryKey() {
|
||||||
|
return KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRun() throws IOException {
|
||||||
|
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||||
|
Optional<GroupRecord> record = database.getGroup(groupId);
|
||||||
|
File attachment = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!record.isPresent()) {
|
||||||
|
Log.w(TAG, "Cannot download avatar for unknown group");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
attachment = File.createTempFile("avatar", "gv2", context.getCacheDir());
|
||||||
|
attachment.deleteOnExit();
|
||||||
|
|
||||||
|
SignalServiceMessageReceiver receiver = ApplicationDependencies.getSignalServiceMessageReceiver();
|
||||||
|
byte[] encryptedData;
|
||||||
|
|
||||||
|
try (FileInputStream inputStream = receiver.retrieveGroupsV2ProfileAvatar(cdnKey, attachment, AVATAR_DOWNLOAD_FAIL_SAFE_MAX_SIZE)) {
|
||||||
|
|
||||||
|
encryptedData = new byte[(int) attachment.length()];
|
||||||
|
|
||||||
|
Util.readFully(inputStream, encryptedData);
|
||||||
|
|
||||||
|
GroupsV2Operations operations = ApplicationDependencies.getGroupsV2Operations();
|
||||||
|
GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(record.get().requireV2GroupProperties().getGroupMasterKey());
|
||||||
|
GroupsV2Operations.GroupOperations groupOperations = operations.forGroup(groupSecretParams);
|
||||||
|
byte[] decryptedAvatar = groupOperations.decryptAvatar(encryptedData);
|
||||||
|
|
||||||
|
AvatarHelper.setAvatar(context, record.get().getRecipientId(), decryptedAvatar != null ? new ByteArrayInputStream(decryptedAvatar) : null);
|
||||||
|
database.onAvatarUpdated(groupId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (NonSuccessfulResponseCodeException | VerificationFailedException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
} finally {
|
||||||
|
if (attachment != null && attachment.exists())
|
||||||
|
if (!attachment.delete()) {
|
||||||
|
Log.w(TAG, "Unable to delete temp avatar file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onShouldRetry(@NonNull Exception exception) {
|
||||||
|
return exception instanceof IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Factory implements Job.Factory<AvatarGroupsV2DownloadJob> {
|
||||||
|
@Override
|
||||||
|
public @NonNull AvatarGroupsV2DownloadJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||||
|
return new AvatarGroupsV2DownloadJob(parameters,
|
||||||
|
GroupId.parse(data.getString(KEY_GROUP_ID)).requireV2(),
|
||||||
|
data.getString(CDN_KEY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -49,7 +49,8 @@ public final class JobManagerFactories {
|
|||||||
put(AttachmentUploadJob.KEY, new AttachmentUploadJob.Factory());
|
put(AttachmentUploadJob.KEY, new AttachmentUploadJob.Factory());
|
||||||
put(AttachmentMarkUploadedJob.KEY, new AttachmentMarkUploadedJob.Factory());
|
put(AttachmentMarkUploadedJob.KEY, new AttachmentMarkUploadedJob.Factory());
|
||||||
put(AttachmentCompressionJob.KEY, new AttachmentCompressionJob.Factory());
|
put(AttachmentCompressionJob.KEY, new AttachmentCompressionJob.Factory());
|
||||||
put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory());
|
put(AvatarGroupsV1DownloadJob.KEY, new AvatarGroupsV1DownloadJob.Factory());
|
||||||
|
put(AvatarGroupsV2DownloadJob.KEY, new AvatarGroupsV2DownloadJob.Factory());
|
||||||
put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory());
|
put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory());
|
||||||
put(CreateSignedPreKeyJob.KEY, new CreateSignedPreKeyJob.Factory());
|
put(CreateSignedPreKeyJob.KEY, new CreateSignedPreKeyJob.Factory());
|
||||||
put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory());
|
put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user