mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-12 13:27:45 +00:00
Use V2 file server for profile pictures
Also don't randomly rotate profile key
This commit is contained in:
@@ -42,6 +42,7 @@ import org.session.libsession.messaging.sending_receiving.pollers.Poller;
|
||||
import org.session.libsession.messaging.threads.Address;
|
||||
import org.session.libsession.snode.SnodeModule;
|
||||
import org.session.libsession.utilities.IdentityKeyUtil;
|
||||
import org.session.libsession.utilities.ProfilePictureUtilities;
|
||||
import org.session.libsession.utilities.SSKEnvironment;
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsession.utilities.Util;
|
||||
@@ -50,6 +51,7 @@ import org.session.libsession.utilities.dynamiclanguage.LocaleParser;
|
||||
import org.session.libsession.utilities.preferences.ProfileKeyUtil;
|
||||
import org.session.libsignal.service.api.util.StreamDetails;
|
||||
import org.session.libsignal.service.loki.LokiAPIDatabaseProtocol;
|
||||
import org.session.libsignal.utilities.ThreadUtils;
|
||||
import org.session.libsignal.utilities.logging.Log;
|
||||
import org.signal.aesgcmprovider.AesGcmProvider;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||
@@ -91,8 +93,10 @@ import org.webrtc.PeerConnectionFactory.InitializationOptions;
|
||||
import org.webrtc.voiceengine.WebRtcAudioManager;
|
||||
import org.webrtc.voiceengine.WebRtcAudioUtils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.util.Date;
|
||||
@@ -101,6 +105,7 @@ import java.util.Set;
|
||||
|
||||
import dagger.ObjectGraph;
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import kotlinx.coroutines.Job;
|
||||
import network.loki.messenger.BuildConfig;
|
||||
|
||||
@@ -481,21 +486,31 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
}
|
||||
|
||||
private void resubmitProfilePictureIfNeeded() {
|
||||
// Files expire on the file server after a while, so we simply re-upload the user's profile picture
|
||||
// at a certain interval to ensure it's always available.
|
||||
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||
if (userPublicKey == null) return;
|
||||
long now = new Date().getTime();
|
||||
long lastProfilePictureUpload = TextSecurePreferences.getLastProfilePictureUpload(this);
|
||||
if (now - lastProfilePictureUpload <= 14 * 24 * 60 * 60 * 1000) return;
|
||||
AsyncTask.execute(() -> {
|
||||
String encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this);
|
||||
byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey);
|
||||
ThreadUtils.queue(() -> {
|
||||
// Don't generate a new profile key here; we do that when the user changes their profile picture
|
||||
String encodedProfileKey = TextSecurePreferences.getProfileKey(ApplicationContext.this);
|
||||
try {
|
||||
File profilePicture = AvatarHelper.getAvatarFile(this, Address.fromSerialized(userPublicKey));
|
||||
StreamDetails stream = new StreamDetails(new FileInputStream(profilePicture), "image/jpeg", profilePicture.length());
|
||||
FileServerAPI.shared.uploadProfilePicture(FileServerAPI.shared.getServer(), profileKey, stream, () -> {
|
||||
TextSecurePreferences.setLastProfilePictureUpload(this, new Date().getTime());
|
||||
TextSecurePreferences.setProfileAvatarId(this, new SecureRandom().nextInt());
|
||||
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey);
|
||||
// Read the file into a byte array
|
||||
InputStream inputStream = AvatarHelper.getInputStreamFor(ApplicationContext.this, Address.fromSerialized(userPublicKey));
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int count;
|
||||
byte[] buffer = new byte[1024];
|
||||
while ((count = inputStream.read(buffer, 0, buffer.length)) != -1) {
|
||||
baos.write(buffer, 0, count);
|
||||
}
|
||||
baos.flush();
|
||||
byte[] profilePicture = baos.toByteArray();
|
||||
// Re-upload it
|
||||
ProfilePictureUtilities.INSTANCE.upload(profilePicture, encodedProfileKey, ApplicationContext.this).success(unit -> {
|
||||
// Update the last profile picture upload date
|
||||
TextSecurePreferences.setLastProfilePictureUpload(ApplicationContext.this, new Date().getTime());
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
} catch (Exception exception) {
|
||||
|
@@ -23,18 +23,14 @@ import network.loki.messenger.BuildConfig
|
||||
import network.loki.messenger.R
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.all
|
||||
import nl.komponents.kovenant.deferred
|
||||
import nl.komponents.kovenant.functional.bind
|
||||
import nl.komponents.kovenant.task
|
||||
import nl.komponents.kovenant.ui.alwaysUi
|
||||
import org.session.libsession.messaging.avatars.AvatarHelper
|
||||
import org.session.libsession.messaging.file_server.FileServerAPI
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupAPI
|
||||
import org.session.libsession.messaging.threads.Address
|
||||
import org.session.libsession.utilities.ProfilePictureUtilities
|
||||
import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.preferences.ProfileKeyUtil
|
||||
import org.session.libsignal.service.api.util.StreamDetails
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.avatar.AvatarSelection
|
||||
@@ -51,7 +47,6 @@ import org.thoughtcrime.securesms.permissions.Permissions
|
||||
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.File
|
||||
import java.security.SecureRandom
|
||||
import java.util.*
|
||||
@@ -127,7 +122,9 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
when (requestCode) {
|
||||
AvatarSelection.REQUEST_CODE_AVATAR -> {
|
||||
if (resultCode != Activity.RESULT_OK) { return }
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
return
|
||||
}
|
||||
val outputFile = Uri.fromFile(File(cacheDir, "cropped"))
|
||||
var inputFile: Uri? = data?.data
|
||||
if (inputFile == null && tempFile != null) {
|
||||
@@ -136,7 +133,9 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
AvatarSelection.circularCropImage(this, inputFile, outputFile, R.string.CropImageActivity_profile_avatar)
|
||||
}
|
||||
AvatarSelection.REQUEST_CODE_CROP_IMAGE -> {
|
||||
if (resultCode != Activity.RESULT_OK) { return }
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
return
|
||||
}
|
||||
AsyncTask.execute {
|
||||
try {
|
||||
profilePictureToBeUploaded = BitmapUtil.createScaledBytes(this@SettingsActivity, AvatarSelection.getResultUri(data), ProfileMediaConstraints()).bitmap
|
||||
@@ -186,37 +185,23 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
}
|
||||
val profilePicture = profilePictureToBeUploaded
|
||||
val encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this)
|
||||
val profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey)
|
||||
if (isUpdatingProfilePicture && profilePicture != null) {
|
||||
val storageAPI = FileServerAPI.shared
|
||||
val deferred = deferred<Unit, Exception>()
|
||||
AsyncTask.execute {
|
||||
val stream = StreamDetails(ByteArrayInputStream(profilePicture), "image/jpeg", profilePicture.size.toLong())
|
||||
val (_, url) = storageAPI.uploadProfilePicture(storageAPI.server, profileKey, stream) {
|
||||
TextSecurePreferences.setLastProfilePictureUpload(this@SettingsActivity, Date().time)
|
||||
}
|
||||
TextSecurePreferences.setProfilePictureURL(this, url)
|
||||
deferred.resolve(Unit)
|
||||
}
|
||||
promises.add(deferred.promise)
|
||||
promises.add(ProfilePictureUtilities.upload(profilePicture, encodedProfileKey, this))
|
||||
}
|
||||
|
||||
all(promises).bind {
|
||||
// updating the profile name or picture
|
||||
if (profilePicture != null || displayName != null) {
|
||||
task {
|
||||
if (isUpdatingProfilePicture && profilePicture != null) {
|
||||
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture)
|
||||
TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt())
|
||||
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey)
|
||||
ApplicationContext.getInstance(this).updateOpenGroupProfilePicturesIfNeeded()
|
||||
}
|
||||
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(this@SettingsActivity)
|
||||
}
|
||||
} else {
|
||||
Promise.of(Unit)
|
||||
val compoundPromise = all(promises)
|
||||
compoundPromise.success {
|
||||
if (isUpdatingProfilePicture && profilePicture != null) {
|
||||
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture)
|
||||
TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt())
|
||||
TextSecurePreferences.setLastProfilePictureUpload(this, Date().time)
|
||||
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey)
|
||||
ApplicationContext.getInstance(this).updateOpenGroupProfilePicturesIfNeeded()
|
||||
}
|
||||
}.alwaysUi {
|
||||
if (profilePicture != null || displayName != null) {
|
||||
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(this@SettingsActivity)
|
||||
}
|
||||
}
|
||||
compoundPromise.alwaysUi {
|
||||
if (displayName != null) {
|
||||
btnGroupNameDisplay.text = displayName
|
||||
}
|
||||
|
Reference in New Issue
Block a user