mirror of
https://github.com/oxen-io/session-android.git
synced 2025-01-11 23:23:39 +00:00
Support for retrieving and storing profile information
Initial support for sharing profile keys // FREEBIE
This commit is contained in:
parent
1893047a78
commit
77a216b705
@ -63,7 +63,7 @@ dependencies {
|
|||||||
|
|
||||||
compile 'org.whispersystems:jobmanager:1.0.2'
|
compile 'org.whispersystems:jobmanager:1.0.2'
|
||||||
compile 'org.whispersystems:libpastelog:1.0.7'
|
compile 'org.whispersystems:libpastelog:1.0.7'
|
||||||
compile 'org.whispersystems:signal-service-android:2.6.0'
|
compile 'org.whispersystems:signal-service-android:2.6.1'
|
||||||
compile 'org.whispersystems:webrtc-android:M59-S1'
|
compile 'org.whispersystems:webrtc-android:M59-S1'
|
||||||
|
|
||||||
compile "me.leolin:ShortcutBadger:1.1.16"
|
compile "me.leolin:ShortcutBadger:1.1.16"
|
||||||
@ -138,7 +138,7 @@ dependencyVerification {
|
|||||||
'com.google.android.exoplayer:exoplayer:955085aa611a8f7cf6c61b88ae03d1a392f4ad94c9bfbc153f3dedb9ffb14718',
|
'com.google.android.exoplayer:exoplayer:955085aa611a8f7cf6c61b88ae03d1a392f4ad94c9bfbc153f3dedb9ffb14718',
|
||||||
'org.whispersystems:jobmanager:506f679fc2fcf7bb6d10f00f41d6f6ea0abf75c70dc95b913398661ad538a181',
|
'org.whispersystems:jobmanager:506f679fc2fcf7bb6d10f00f41d6f6ea0abf75c70dc95b913398661ad538a181',
|
||||||
'org.whispersystems:libpastelog:bb331d9a98240fc139101128ba836c1edec3c40e000597cdbb29ebf4cbf34d88',
|
'org.whispersystems:libpastelog:bb331d9a98240fc139101128ba836c1edec3c40e000597cdbb29ebf4cbf34d88',
|
||||||
'org.whispersystems:signal-service-android:a25bfa4fd86f2d5f6cedb54d2c917af7b521dc4dda14ee35a3ac7267f1d29c7e',
|
'org.whispersystems:signal-service-android:9458c3f05698863f3abacbbcb3dfcbb6e2de26e4b1869e25baccebc1e140c97f',
|
||||||
'org.whispersystems:webrtc-android:de647643afbbea45a26a4f24db75aa10bc8de45426e8eb0d9d563cc10af4f582',
|
'org.whispersystems:webrtc-android:de647643afbbea45a26a4f24db75aa10bc8de45426e8eb0d9d563cc10af4f582',
|
||||||
'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774',
|
'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774',
|
||||||
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
|
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
|
||||||
@ -173,7 +173,7 @@ dependencyVerification {
|
|||||||
'com.google.android.gms:play-services-basement:95dd882c5ffba15b9a99de3fefb05d3a01946623af67454ca00055d222f85a8d',
|
'com.google.android.gms:play-services-basement:95dd882c5ffba15b9a99de3fefb05d3a01946623af67454ca00055d222f85a8d',
|
||||||
'com.google.android.gms:play-services-iid:54e919f9957b8b7820da7ee9b83471d00d0cac1cf08ddea8b5b41aea80bb1a70',
|
'com.google.android.gms:play-services-iid:54e919f9957b8b7820da7ee9b83471d00d0cac1cf08ddea8b5b41aea80bb1a70',
|
||||||
'org.whispersystems:signal-protocol-android:5b8acded7f2a40178eb90ab8e8cbfec89d170d91b3ff5e78487d1098df6185a1',
|
'org.whispersystems:signal-protocol-android:5b8acded7f2a40178eb90ab8e8cbfec89d170d91b3ff5e78487d1098df6185a1',
|
||||||
'org.whispersystems:signal-service-java:6524523dc774943a31784fa088c8185ad32f240e7c5f63245ab1e61081f76a83',
|
'org.whispersystems:signal-service-java:8c72b0055ed01fd98430105c872e93c9af7ce18197cd33566ca9ab561e3102be',
|
||||||
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',
|
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',
|
||||||
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'com.klinkerapps:logger:177e325259a8b111ad6745ec10db5861723c99f402222b80629f576f49408541',
|
'com.klinkerapps:logger:177e325259a8b111ad6745ec10db5861723c99f402222b80629f576f49408541',
|
||||||
|
@ -293,7 +293,7 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedLi
|
|||||||
Uri contentUri = ContactsContract.Contacts.lookupContact(context.getContentResolver(),
|
Uri contentUri = ContactsContract.Contacts.lookupContact(context.getContentResolver(),
|
||||||
recipient.getContactUri());
|
recipient.getContactUri());
|
||||||
windowManager.getDefaultDisplay().getMetrics(metrics);
|
windowManager.getDefaultDisplay().getMetrics(metrics);
|
||||||
return ContactPhotoFactory.getContactPhoto(context, contentUri, null, metrics.widthPixels);
|
return ContactPhotoFactory.getContactPhoto(context, contentUri, recipient.getAddress(), null, metrics.widthPixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -13,7 +13,10 @@ import com.bumptech.glide.Glide;
|
|||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.database.Address;
|
||||||
import org.thoughtcrime.securesms.mms.ContactPhotoUriLoader.ContactPhotoUri;
|
import org.thoughtcrime.securesms.mms.ContactPhotoUriLoader.ContactPhotoUri;
|
||||||
|
import org.thoughtcrime.securesms.profiles.AvatarPhotoUriLoader;
|
||||||
|
import org.thoughtcrime.securesms.profiles.AvatarPhotoUriLoader.AvatarPhotoUri;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
@ -38,17 +41,18 @@ public class ContactPhotoFactory {
|
|||||||
return new ResourceContactPhoto(R.drawable.ic_group_white_24dp);
|
return new ResourceContactPhoto(R.drawable.ic_group_white_24dp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ContactPhoto getContactPhoto(Context context, Uri uri, String name) {
|
public static ContactPhoto getContactPhoto(@NonNull Context context, @Nullable Uri uri, @NonNull Address address, @Nullable String name) {
|
||||||
int targetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size);
|
int targetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size);
|
||||||
return getContactPhoto(context, uri, name, targetSize);
|
return getContactPhoto(context, uri, address, name, targetSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ContactPhoto getContactPhoto(@NonNull Context context,
|
public static ContactPhoto getContactPhoto(@NonNull Context context,
|
||||||
@Nullable Uri uri,
|
@Nullable Uri uri,
|
||||||
|
@NonNull Address address,
|
||||||
@Nullable String name,
|
@Nullable String name,
|
||||||
int targetSize)
|
int targetSize)
|
||||||
{
|
{
|
||||||
if (uri == null) return getDefaultContactPhoto(name);
|
if (uri == null) return getSignalAvatarContactPhoto(context, address, name, targetSize);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Bitmap bitmap = Glide.with(context)
|
Bitmap bitmap = Glide.with(context)
|
||||||
@ -57,7 +61,7 @@ public class ContactPhotoFactory {
|
|||||||
.centerCrop().into(targetSize, targetSize).get();
|
.centerCrop().into(targetSize, targetSize).get();
|
||||||
return new BitmapContactPhoto(bitmap);
|
return new BitmapContactPhoto(bitmap);
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
return getDefaultContactPhoto(name);
|
return getSignalAvatarContactPhoto(context, address, name, targetSize);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
@ -68,4 +72,26 @@ public class ContactPhotoFactory {
|
|||||||
|
|
||||||
return new BitmapContactPhoto(BitmapFactory.decodeByteArray(avatar, 0, avatar.length));
|
return new BitmapContactPhoto(BitmapFactory.decodeByteArray(avatar, 0, avatar.length));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ContactPhoto getSignalAvatarContactPhoto(@NonNull Context context,
|
||||||
|
@NonNull Address address,
|
||||||
|
@Nullable String name,
|
||||||
|
int targetSize)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Bitmap bitmap = Glide.with(context)
|
||||||
|
.load(new AvatarPhotoUri(address))
|
||||||
|
.asBitmap()
|
||||||
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
|
.centerCrop()
|
||||||
|
.into(targetSize, targetSize)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
return new BitmapContactPhoto(bitmap);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
return getDefaultContactPhoto(name);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,8 @@ public class DatabaseFactory {
|
|||||||
private static final int NO_MORE_RECIPIENTS_PLURAL = 38;
|
private static final int NO_MORE_RECIPIENTS_PLURAL = 38;
|
||||||
private static final int INTERNAL_DIRECTORY = 39;
|
private static final int INTERNAL_DIRECTORY = 39;
|
||||||
private static final int INTERNAL_SYSTEM_DISPLAY_NAME = 40;
|
private static final int INTERNAL_SYSTEM_DISPLAY_NAME = 40;
|
||||||
private static final int DATABASE_VERSION = 40;
|
private static final int PROFILES = 41;
|
||||||
|
private static final int DATABASE_VERSION = 41;
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "messages.db";
|
private static final String DATABASE_NAME = "messages.db";
|
||||||
private static final Object lock = new Object();
|
private static final Object lock = new Object();
|
||||||
@ -1297,6 +1298,12 @@ public class DatabaseFactory {
|
|||||||
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_display_name TEXT DEFAULT NULL");
|
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN system_display_name TEXT DEFAULT NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < PROFILES) {
|
||||||
|
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN profile_key TEXT DEFAULT NULL");
|
||||||
|
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN signal_profile_name TEXT DEFAULT NULL");
|
||||||
|
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN signal_profile_avatar TEXT DEFAULT NULL");
|
||||||
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,10 @@ import org.greenrobot.eventbus.EventBus;
|
|||||||
import org.thoughtcrime.securesms.color.MaterialColor;
|
import org.thoughtcrime.securesms.color.MaterialColor;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||||
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -40,10 +42,14 @@ public class RecipientPreferenceDatabase extends Database {
|
|||||||
private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id";
|
private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id";
|
||||||
private static final String EXPIRE_MESSAGES = "expire_messages";
|
private static final String EXPIRE_MESSAGES = "expire_messages";
|
||||||
private static final String REGISTERED = "registered";
|
private static final String REGISTERED = "registered";
|
||||||
|
private static final String PROFILE_KEY = "profile_key";
|
||||||
private static final String SYSTEM_DISPLAY_NAME = "system_display_name";
|
private static final String SYSTEM_DISPLAY_NAME = "system_display_name";
|
||||||
|
private static final String SIGNAL_PROFILE_NAME = "signal_profile_name";
|
||||||
|
private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar";
|
||||||
|
|
||||||
private static final String[] RECIPIENT_PROJECTION = new String[] {
|
private static final String[] RECIPIENT_PROJECTION = new String[] {
|
||||||
BLOCK, NOTIFICATION, VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED, SYSTEM_DISPLAY_NAME
|
BLOCK, NOTIFICATION, VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED,
|
||||||
|
PROFILE_KEY, SYSTEM_DISPLAY_NAME, SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR
|
||||||
};
|
};
|
||||||
|
|
||||||
static final List<String> TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
|
static final List<String> TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
|
||||||
@ -81,7 +87,10 @@ public class RecipientPreferenceDatabase extends Database {
|
|||||||
DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
|
DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
|
||||||
EXPIRE_MESSAGES + " INTEGER DEFAULT 0, " +
|
EXPIRE_MESSAGES + " INTEGER DEFAULT 0, " +
|
||||||
REGISTERED + " INTEGER DEFAULT 0, " +
|
REGISTERED + " INTEGER DEFAULT 0, " +
|
||||||
SYSTEM_DISPLAY_NAME + " TEXT DEFAULT NULL);";
|
SYSTEM_DISPLAY_NAME + " TEXT DEFAULT NULL, " +
|
||||||
|
PROFILE_KEY + " TEXT DEFAULT NULL, " +
|
||||||
|
SIGNAL_PROFILE_NAME + " TEXT DEFAULT NULL, " +
|
||||||
|
SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL);";
|
||||||
|
|
||||||
public RecipientPreferenceDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
public RecipientPreferenceDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
||||||
super(context, databaseHelper);
|
super(context, databaseHelper);
|
||||||
@ -130,9 +139,13 @@ public class RecipientPreferenceDatabase extends Database {
|
|||||||
int defaultSubscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(DEFAULT_SUBSCRIPTION_ID));
|
int defaultSubscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(DEFAULT_SUBSCRIPTION_ID));
|
||||||
int expireMessages = cursor.getInt(cursor.getColumnIndexOrThrow(EXPIRE_MESSAGES));
|
int expireMessages = cursor.getInt(cursor.getColumnIndexOrThrow(EXPIRE_MESSAGES));
|
||||||
boolean registered = cursor.getInt(cursor.getColumnIndexOrThrow(REGISTERED)) == 1;
|
boolean registered = cursor.getInt(cursor.getColumnIndexOrThrow(REGISTERED)) == 1;
|
||||||
String systemDisplayname = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME));
|
String profileKeyString = cursor.getString(cursor.getColumnIndexOrThrow(PROFILE_KEY));
|
||||||
|
String systemDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME));
|
||||||
|
String signalProfileName = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_NAME));
|
||||||
|
String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_AVATAR));
|
||||||
|
|
||||||
MaterialColor color;
|
MaterialColor color;
|
||||||
|
byte[] profileKey = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
color = serializedColor == null ? null : MaterialColor.fromSerialized(serializedColor);
|
color = serializedColor == null ? null : MaterialColor.fromSerialized(serializedColor);
|
||||||
@ -141,11 +154,21 @@ public class RecipientPreferenceDatabase extends Database {
|
|||||||
color = null;
|
color = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (profileKeyString != null) {
|
||||||
|
try {
|
||||||
|
profileKey = Base64.decode(profileKeyString);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
profileKey = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Optional.of(new RecipientsPreferences(blocked, muteUntil,
|
return Optional.of(new RecipientsPreferences(blocked, muteUntil,
|
||||||
VibrateState.fromId(vibrateState),
|
VibrateState.fromId(vibrateState),
|
||||||
notificationUri, color, seenInviteReminder,
|
notificationUri, color, seenInviteReminder,
|
||||||
defaultSubscriptionId, expireMessages, registered,
|
defaultSubscriptionId, expireMessages, registered,
|
||||||
systemDisplayname));
|
profileKey, systemDisplayName, signalProfileName,
|
||||||
|
signalProfileAvatar));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setColor(Recipient recipient, MaterialColor color) {
|
public void setColor(Recipient recipient, MaterialColor color) {
|
||||||
@ -206,6 +229,24 @@ public class RecipientPreferenceDatabase extends Database {
|
|||||||
updateOrInsert(address, values);
|
updateOrInsert(address, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setProfileKey(@NonNull Address address, @Nullable byte[] profileKey) {
|
||||||
|
ContentValues values = new ContentValues(1);
|
||||||
|
values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey));
|
||||||
|
updateOrInsert(address, values);;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProfileName(@NonNull Address address, @Nullable String profileName) {
|
||||||
|
ContentValues contentValues = new ContentValues(1);
|
||||||
|
contentValues.put(SIGNAL_PROFILE_NAME, profileName);
|
||||||
|
updateOrInsert(address, contentValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProfileAvatar(@NonNull Address address, @Nullable String profileAvatar) {
|
||||||
|
ContentValues contentValues = new ContentValues(1);
|
||||||
|
contentValues.put(SIGNAL_PROFILE_AVATAR, profileAvatar);
|
||||||
|
updateOrInsert(address, contentValues);
|
||||||
|
}
|
||||||
|
|
||||||
public Set<Address> getAllRecipients() {
|
public Set<Address> getAllRecipients() {
|
||||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
Set<Address> results = new HashSet<>();
|
Set<Address> results = new HashSet<>();
|
||||||
@ -285,7 +326,10 @@ public class RecipientPreferenceDatabase extends Database {
|
|||||||
private final int defaultSubscriptionId;
|
private final int defaultSubscriptionId;
|
||||||
private final int expireMessages;
|
private final int expireMessages;
|
||||||
private final boolean registered;
|
private final boolean registered;
|
||||||
|
private final byte[] profileKey;
|
||||||
private final String systemDisplayName;
|
private final String systemDisplayName;
|
||||||
|
private final String signalProfileName;
|
||||||
|
private final String signalProfileAvatar;
|
||||||
|
|
||||||
RecipientsPreferences(boolean blocked, long muteUntil,
|
RecipientsPreferences(boolean blocked, long muteUntil,
|
||||||
@NonNull VibrateState vibrateState,
|
@NonNull VibrateState vibrateState,
|
||||||
@ -295,7 +339,10 @@ public class RecipientPreferenceDatabase extends Database {
|
|||||||
int defaultSubscriptionId,
|
int defaultSubscriptionId,
|
||||||
int expireMessages,
|
int expireMessages,
|
||||||
boolean registered,
|
boolean registered,
|
||||||
String systemDisplayName)
|
@Nullable byte[] profileKey,
|
||||||
|
@Nullable String systemDisplayName,
|
||||||
|
@Nullable String signalProfileName,
|
||||||
|
@Nullable String signalProfileAvatar)
|
||||||
{
|
{
|
||||||
this.blocked = blocked;
|
this.blocked = blocked;
|
||||||
this.muteUntil = muteUntil;
|
this.muteUntil = muteUntil;
|
||||||
@ -306,7 +353,10 @@ public class RecipientPreferenceDatabase extends Database {
|
|||||||
this.defaultSubscriptionId = defaultSubscriptionId;
|
this.defaultSubscriptionId = defaultSubscriptionId;
|
||||||
this.expireMessages = expireMessages;
|
this.expireMessages = expireMessages;
|
||||||
this.registered = registered;
|
this.registered = registered;
|
||||||
|
this.profileKey = profileKey;
|
||||||
this.systemDisplayName = systemDisplayName;
|
this.systemDisplayName = systemDisplayName;
|
||||||
|
this.signalProfileName = signalProfileName;
|
||||||
|
this.signalProfileAvatar = signalProfileAvatar;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable MaterialColor getColor() {
|
public @Nullable MaterialColor getColor() {
|
||||||
@ -345,9 +395,21 @@ public class RecipientPreferenceDatabase extends Database {
|
|||||||
return registered;
|
return registered;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSystemDisplayName() {
|
public byte[] getProfileKey() {
|
||||||
|
return profileKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getSystemDisplayName() {
|
||||||
return systemDisplayName;
|
return systemDisplayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @Nullable String getProfileName() {
|
||||||
|
return signalProfileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getProfileAvatar() {
|
||||||
|
return signalProfileAvatar;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BlockedReader {
|
public static class BlockedReader {
|
||||||
|
@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
|||||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
||||||
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
|
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
|
||||||
import org.thoughtcrime.securesms.push.SecurityEventListener;
|
import org.thoughtcrime.securesms.push.SecurityEventListener;
|
||||||
@ -65,7 +66,8 @@ import dagger.Provides;
|
|||||||
WebRtcCallService.class,
|
WebRtcCallService.class,
|
||||||
RetrieveProfileJob.class,
|
RetrieveProfileJob.class,
|
||||||
MultiDeviceVerifiedUpdateJob.class,
|
MultiDeviceVerifiedUpdateJob.class,
|
||||||
CreateProfileActivity.class})
|
CreateProfileActivity.class,
|
||||||
|
RetrieveProfileAvatarJob.class})
|
||||||
public class SignalCommunicationModule {
|
public class SignalCommunicationModule {
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
@ -25,6 +25,8 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
|||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||||
import org.thoughtcrime.securesms.database.PushDatabase;
|
import org.thoughtcrime.securesms.database.PushDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
|
import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
|
||||||
@ -81,6 +83,8 @@ import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSy
|
|||||||
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@ -164,6 +168,10 @@ public class PushDecryptJob extends ContextJob {
|
|||||||
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))) {
|
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))) {
|
||||||
handleUnknownGroupMessage(envelope, message.getGroupInfo().get());
|
handleUnknownGroupMessage(envelope, message.getGroupInfo().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message.getProfileKey().isPresent()) {
|
||||||
|
handleProfileKey(envelope, message);
|
||||||
|
}
|
||||||
} else if (content.getSyncMessage().isPresent()) {
|
} else if (content.getSyncMessage().isPresent()) {
|
||||||
SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
|
SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
|
||||||
|
|
||||||
@ -794,6 +802,24 @@ public class PushDecryptJob extends ContextJob {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleProfileKey(@NonNull SignalServiceEnvelope envelope,
|
||||||
|
@NonNull SignalServiceDataMessage message)
|
||||||
|
{
|
||||||
|
RecipientPreferenceDatabase database = DatabaseFactory.getRecipientPreferenceDatabase(context);
|
||||||
|
Address sourceAddress = Address.fromExternal(context, envelope.getSource());
|
||||||
|
Optional<RecipientsPreferences> preferences = database.getRecipientsPreferences(sourceAddress);
|
||||||
|
|
||||||
|
if (!preferences.isPresent() || preferences.get().getProfileKey() == null ||
|
||||||
|
!MessageDigest.isEqual(message.getProfileKey().get(), preferences.get().getProfileKey()))
|
||||||
|
{
|
||||||
|
database.setProfileKey(sourceAddress, message.getProfileKey().get());
|
||||||
|
|
||||||
|
Recipient recipient = RecipientFactory.getRecipientFor(context, sourceAddress, true);
|
||||||
|
ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileJob(context, recipient));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private Optional<InsertResult> insertPlaceholder(@NonNull SignalServiceEnvelope envelope) {
|
private Optional<InsertResult> insertPlaceholder(@NonNull SignalServiceEnvelope envelope) {
|
||||||
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
|
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
|
||||||
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
|
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
|
||||||
|
@ -162,7 +162,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
|||||||
SignalServiceDataMessage groupMessage = new SignalServiceDataMessage(message.getSentTimeMillis(), group,
|
SignalServiceDataMessage groupMessage = new SignalServiceDataMessage(message.getSentTimeMillis(), group,
|
||||||
attachmentStreams, message.getBody(), false,
|
attachmentStreams, message.getBody(), false,
|
||||||
(int)(message.getExpiresIn() / 1000),
|
(int)(message.getExpiresIn() / 1000),
|
||||||
message.isExpirationUpdate());
|
message.isExpirationUpdate(), null);
|
||||||
|
|
||||||
messageSender.sendMessage(addresses, groupMessage);
|
messageSender.sendMessage(addresses, groupMessage);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
|||||||
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
||||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||||
@ -113,11 +114,13 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
|||||||
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
|
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
|
||||||
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
|
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
|
||||||
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments);
|
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments);
|
||||||
|
Optional<byte[]> profileKey = getProfileKey(message.getRecipient().getAddress());
|
||||||
SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder()
|
SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder()
|
||||||
.withBody(message.getBody())
|
.withBody(message.getBody())
|
||||||
.withAttachments(attachmentStreams)
|
.withAttachments(attachmentStreams)
|
||||||
.withTimestamp(message.getSentTimeMillis())
|
.withTimestamp(message.getSentTimeMillis())
|
||||||
.withExpiration((int)(message.getExpiresIn() / 1000))
|
.withExpiration((int)(message.getExpiresIn() / 1000))
|
||||||
|
.withProfileKey(profileKey.orNull())
|
||||||
.asExpirationUpdate(message.isExpirationUpdate())
|
.asExpirationUpdate(message.isExpirationUpdate())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
package org.thoughtcrime.securesms.jobs;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
@ -10,12 +11,16 @@ import org.thoughtcrime.securesms.attachments.Attachment;
|
|||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
import org.thoughtcrime.securesms.database.Address;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
|
||||||
import org.thoughtcrime.securesms.events.PartProgressEvent;
|
import org.thoughtcrime.securesms.events.PartProgressEvent;
|
||||||
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
|
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.jobqueue.JobParameters;
|
import org.whispersystems.jobqueue.JobParameters;
|
||||||
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
@ -60,6 +65,28 @@ public abstract class PushSendJob extends SendJob {
|
|||||||
onPushSend(masterSecret);
|
onPushSend(masterSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Optional<byte[]> getProfileKey(Address address) {
|
||||||
|
try {
|
||||||
|
Optional<RecipientsPreferences> recipientsPreferences = DatabaseFactory.getRecipientPreferenceDatabase(context)
|
||||||
|
.getRecipientsPreferences(address);
|
||||||
|
|
||||||
|
if (recipientsPreferences.isPresent() && !TextUtils.isEmpty(recipientsPreferences.get().getSystemDisplayName())) {
|
||||||
|
String profileKey = TextSecurePreferences.getProfileKey(context);
|
||||||
|
|
||||||
|
if (profileKey == null) {
|
||||||
|
profileKey = Util.getSecret(32);
|
||||||
|
TextSecurePreferences.setProfileKey(context, profileKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(Base64.decode(profileKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.absent();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected SignalServiceAddress getPushAddress(Address address) {
|
protected SignalServiceAddress getPushAddress(Address address) {
|
||||||
// String relay = TextSecureDirectory.getInstance(context).getRelay(address.toPhoneString());
|
// String relay = TextSecureDirectory.getInstance(context).getRelay(address.toPhoneString());
|
||||||
String relay = null;
|
String relay = null;
|
||||||
|
@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
|||||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||||
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
||||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
@ -101,10 +102,12 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
|||||||
try {
|
try {
|
||||||
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getAddress());
|
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getAddress());
|
||||||
SignalServiceMessageSender messageSender = messageSenderFactory.create();
|
SignalServiceMessageSender messageSender = messageSenderFactory.create();
|
||||||
|
Optional<byte[]> profileKey = getProfileKey(message.getIndividualRecipient().getAddress());
|
||||||
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
|
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
|
||||||
.withTimestamp(message.getDateSent())
|
.withTimestamp(message.getDateSent())
|
||||||
.withBody(message.getBody().getBody())
|
.withBody(message.getBody().getBody())
|
||||||
.withExpiration((int)(message.getExpiresIn() / 1000))
|
.withExpiration((int)(message.getExpiresIn() / 1000))
|
||||||
|
.withProfileKey(profileKey.orNull())
|
||||||
.asEndSessionMessage(message.isEndSession())
|
.asEndSessionMessage(message.isEndSession())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -0,0 +1,109 @@
|
|||||||
|
package org.thoughtcrime.securesms.jobs;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
|
||||||
|
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
import org.whispersystems.jobqueue.JobParameters;
|
||||||
|
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||||
|
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class RetrieveProfileAvatarJob extends ContextJob implements InjectableType {
|
||||||
|
|
||||||
|
private static final String TAG = RetrieveProfileAvatarJob.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final int MAX_PROFILE_SIZE_BYTES = 20 * 1024 * 1024;
|
||||||
|
|
||||||
|
@Inject SignalServiceMessageReceiver receiver;
|
||||||
|
|
||||||
|
private final String profileAvatar;
|
||||||
|
private final Recipient recipient;
|
||||||
|
|
||||||
|
public RetrieveProfileAvatarJob(Context context, Recipient recipient, String profileAvatar) {
|
||||||
|
super(context, JobParameters.newBuilder()
|
||||||
|
.withGroupId(RetrieveProfileAvatarJob.class.getSimpleName() + recipient.getAddress().serialize())
|
||||||
|
.withRequirement(new NetworkRequirement(context))
|
||||||
|
.create());
|
||||||
|
|
||||||
|
this.recipient = recipient;
|
||||||
|
this.profileAvatar = profileAvatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAdded() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRun() throws IOException {
|
||||||
|
RecipientPreferenceDatabase database = DatabaseFactory.getRecipientPreferenceDatabase(context);
|
||||||
|
Optional<RecipientsPreferences> recipientsPreferences = database.getRecipientsPreferences(recipient.getAddress());
|
||||||
|
File avatarDirectory = new File(context.getFilesDir(), "avatars");
|
||||||
|
File avatarFile = new File(avatarDirectory, new File(recipient.getAddress().serialize()).getName());
|
||||||
|
|
||||||
|
avatarDirectory.mkdirs();
|
||||||
|
|
||||||
|
if (!recipientsPreferences.isPresent()) {
|
||||||
|
Log.w(TAG, "Recipient preference row is gone!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recipientsPreferences.get().getProfileKey() == null) {
|
||||||
|
Log.w(TAG, "Recipient profile key is gone!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Util.equals(profileAvatar, recipientsPreferences.get().getProfileAvatar())) {
|
||||||
|
Log.w(TAG, "Already retrieved profile avatar: " + profileAvatar);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(profileAvatar)) {
|
||||||
|
Log.w(TAG, "Removing profile avatar for: " + recipient.getAddress().serialize());
|
||||||
|
avatarFile.delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File downloadDestination = File.createTempFile("avatar", "jpg", context.getCacheDir());
|
||||||
|
|
||||||
|
try {
|
||||||
|
InputStream avatarStream = receiver.retrieveProfileAvatar(profileAvatar, downloadDestination, recipientsPreferences.get().getProfileKey(), MAX_PROFILE_SIZE_BYTES);
|
||||||
|
File decryptDestination = File.createTempFile("avatar", "jpg", context.getCacheDir());
|
||||||
|
|
||||||
|
Util.copy(avatarStream, new FileOutputStream(decryptDestination));
|
||||||
|
decryptDestination.renameTo(avatarFile);
|
||||||
|
} finally {
|
||||||
|
if (downloadDestination != null) downloadDestination.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
database.setProfileAvatar(recipient.getAddress(), profileAvatar);
|
||||||
|
RecipientFactory.clearCache(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onShouldRetry(Exception e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
if (e instanceof PushNetworkException) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCanceled() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -6,17 +6,23 @@ import android.support.annotation.NonNull;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||||
import org.thoughtcrime.securesms.service.MessageRetrievalService;
|
import org.thoughtcrime.securesms.service.MessageRetrievalService;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.jobqueue.JobParameters;
|
import org.whispersystems.jobqueue.JobParameters;
|
||||||
import org.whispersystems.libsignal.IdentityKey;
|
import org.whispersystems.libsignal.IdentityKey;
|
||||||
import org.whispersystems.libsignal.InvalidKeyException;
|
import org.whispersystems.libsignal.InvalidKeyException;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
|
import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||||
|
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
|
||||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||||
@ -66,25 +72,13 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
|
|||||||
private void handleIndividualRecipient(Recipient recipient)
|
private void handleIndividualRecipient(Recipient recipient)
|
||||||
throws IOException, InvalidKeyException, InvalidNumberException
|
throws IOException, InvalidKeyException, InvalidNumberException
|
||||||
{
|
{
|
||||||
String number = recipient.getAddress().toPhoneString();
|
String number = recipient.getAddress().toPhoneString();
|
||||||
SignalServiceProfile profile = retrieveProfile(number);
|
SignalServiceProfile profile = retrieveProfile(number);
|
||||||
|
Optional<RecipientsPreferences> recipientPreferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipient.getAddress());
|
||||||
|
|
||||||
if (TextUtils.isEmpty(profile.getIdentityKey())) {
|
setIdentityKey(recipient, profile.getIdentityKey());
|
||||||
Log.w(TAG, "Identity key is missing on profile!");
|
setProfileName(recipient, recipientPreferences, profile.getName());
|
||||||
return;
|
setProfileAvatar(recipient, recipientPreferences, profile.getAvatar());
|
||||||
}
|
|
||||||
|
|
||||||
IdentityKey identityKey = new IdentityKey(Base64.decode(profile.getIdentityKey()), 0);
|
|
||||||
|
|
||||||
if (!DatabaseFactory.getIdentityDatabase(context)
|
|
||||||
.getIdentity(recipient.getAddress())
|
|
||||||
.isPresent())
|
|
||||||
{
|
|
||||||
Log.w(TAG, "Still first use...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityUtil.saveIdentity(context, number, identityKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleGroupRecipient(Recipient group)
|
private void handleGroupRecipient(Recipient group)
|
||||||
@ -110,4 +104,59 @@ public class RetrieveProfileJob extends ContextJob implements InjectableType {
|
|||||||
|
|
||||||
return receiver.retrieveProfile(new SignalServiceAddress(number));
|
return receiver.retrieveProfile(new SignalServiceAddress(number));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setIdentityKey(Recipient recipient, String identityKeyValue) {
|
||||||
|
try {
|
||||||
|
if (TextUtils.isEmpty(identityKeyValue)) {
|
||||||
|
Log.w(TAG, "Identity key is missing on profile!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityKey identityKey = new IdentityKey(Base64.decode(identityKeyValue), 0);
|
||||||
|
|
||||||
|
if (!DatabaseFactory.getIdentityDatabase(context)
|
||||||
|
.getIdentity(recipient.getAddress())
|
||||||
|
.isPresent())
|
||||||
|
{
|
||||||
|
Log.w(TAG, "Still first use...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityUtil.saveIdentity(context, recipient.getAddress().toPhoneString(), identityKey);
|
||||||
|
} catch (InvalidKeyException | IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setProfileName(Recipient recipient, Optional<RecipientsPreferences> recipientPreferences, String profileName) {
|
||||||
|
try {
|
||||||
|
if (!recipientPreferences.isPresent()) return;
|
||||||
|
if (recipientPreferences.get().getProfileKey() == null) return;
|
||||||
|
|
||||||
|
String plaintextProfileName = null;
|
||||||
|
|
||||||
|
if (profileName != null) {
|
||||||
|
ProfileCipher profileCipher = new ProfileCipher(recipientPreferences.get().getProfileKey());
|
||||||
|
plaintextProfileName = new String(profileCipher.decryptName(Base64.decode(profileName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Util.equals(plaintextProfileName, recipientPreferences.get().getProfileName())) {
|
||||||
|
DatabaseFactory.getRecipientPreferenceDatabase(context).setProfileName(recipient.getAddress(), plaintextProfileName);
|
||||||
|
RecipientFactory.clearCache(context);
|
||||||
|
}
|
||||||
|
} catch (ProfileCipher.InvalidCiphertextException | IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setProfileAvatar(Recipient recipient, Optional<RecipientsPreferences> recipientPreferences, String profileAvatar) {
|
||||||
|
if (!recipientPreferences.isPresent()) return;
|
||||||
|
if (recipientPreferences.get().getProfileKey() == null) return;
|
||||||
|
|
||||||
|
if (!Util.equals(profileAvatar, recipientPreferences.get().getProfileAvatar())) {
|
||||||
|
ApplicationContext.getInstance(context)
|
||||||
|
.getJobManager()
|
||||||
|
.add(new RetrieveProfileAvatarJob(context, recipient, profileAvatar));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ import org.thoughtcrime.securesms.glide.OkHttpUrlLoader;
|
|||||||
import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel;
|
import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel;
|
||||||
import org.thoughtcrime.securesms.mms.ContactPhotoUriLoader.ContactPhotoUri;
|
import org.thoughtcrime.securesms.mms.ContactPhotoUriLoader.ContactPhotoUri;
|
||||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||||
|
import org.thoughtcrime.securesms.profiles.AvatarPhotoUriLoader;
|
||||||
|
import org.thoughtcrime.securesms.profiles.AvatarPhotoUriLoader.AvatarPhotoUri;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@ -28,6 +30,7 @@ public class TextSecureGlideModule implements GlideModule {
|
|||||||
glide.register(ContactPhotoUri.class, InputStream.class, new ContactPhotoUriLoader.Factory());
|
glide.register(ContactPhotoUri.class, InputStream.class, new ContactPhotoUriLoader.Factory());
|
||||||
glide.register(AttachmentModel.class, InputStream.class, new AttachmentStreamUriLoader.Factory());
|
glide.register(AttachmentModel.class, InputStream.class, new AttachmentStreamUriLoader.Factory());
|
||||||
glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
|
glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
|
||||||
|
glide.register(AvatarPhotoUri.class, InputStream.class, new AvatarPhotoUriLoader.Factory());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class NoopDiskCacheFactory implements DiskCache.Factory {
|
public static class NoopDiskCacheFactory implements DiskCache.Factory {
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package org.thoughtcrime.securesms.profiles;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Priority;
|
||||||
|
import com.bumptech.glide.load.data.DataFetcher;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.Address;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class AvatarPhotoUriFetcher implements DataFetcher<InputStream> {
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final Address address;
|
||||||
|
|
||||||
|
private InputStream inputStream;
|
||||||
|
|
||||||
|
public AvatarPhotoUriFetcher(@NonNull Context context, @NonNull Address address) {
|
||||||
|
this.context = context.getApplicationContext();
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream loadData(Priority priority) throws FileNotFoundException {
|
||||||
|
File avatarsDir = new File(context.getFilesDir(), "avatars");
|
||||||
|
inputStream = new FileInputStream(new File(avatarsDir, address.serialize()));
|
||||||
|
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanup() {
|
||||||
|
try {
|
||||||
|
if (inputStream != null) inputStream.close();
|
||||||
|
} catch (IOException e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return address.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package org.thoughtcrime.securesms.profiles;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.data.DataFetcher;
|
||||||
|
import com.bumptech.glide.load.model.GenericLoaderFactory;
|
||||||
|
import com.bumptech.glide.load.model.ModelLoaderFactory;
|
||||||
|
import com.bumptech.glide.load.model.stream.StreamModelLoader;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.Address;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class AvatarPhotoUriLoader implements StreamModelLoader<AvatarPhotoUriLoader.AvatarPhotoUri> {
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
public static class Factory implements ModelLoaderFactory<AvatarPhotoUri, InputStream> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamModelLoader<AvatarPhotoUri> build(Context context, GenericLoaderFactory factories) {
|
||||||
|
return new AvatarPhotoUriLoader(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void teardown() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AvatarPhotoUriLoader(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataFetcher<InputStream> getResourceFetcher(AvatarPhotoUri model, int width, int height) {
|
||||||
|
return new AvatarPhotoUriFetcher(context, model.address);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AvatarPhotoUri {
|
||||||
|
public @NonNull Address address;
|
||||||
|
|
||||||
|
public AvatarPhotoUri(@NonNull Address address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -139,6 +139,7 @@ class RecipientProvider {
|
|||||||
String name = resultNumber.equals(cursor.getString(0)) ? null : cursor.getString(0);
|
String name = resultNumber.equals(cursor.getString(0)) ? null : cursor.getString(0);
|
||||||
ContactPhoto contactPhoto = ContactPhotoFactory.getContactPhoto(context,
|
ContactPhoto contactPhoto = ContactPhotoFactory.getContactPhoto(context,
|
||||||
Uri.withAppendedPath(Contacts.CONTENT_URI, cursor.getLong(2) + ""),
|
Uri.withAppendedPath(Contacts.CONTENT_URI, cursor.getLong(2) + ""),
|
||||||
|
address,
|
||||||
name);
|
name);
|
||||||
|
|
||||||
return new RecipientDetails(cursor.getString(0), cursor.getString(4), contactUri, contactPhoto, preferences.orNull(), null);
|
return new RecipientDetails(cursor.getString(0), cursor.getString(4), contactUri, contactPhoto, preferences.orNull(), null);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user