kopia lustrzana https://github.com/ryukoposting/Signal-Android
Blur avatar photos from unknown senders when in message request state.
rodzic
bf124b87fa
commit
ad81b310e3
|
@ -17,6 +17,7 @@ import net.sqlcipher.database.SQLiteStatement;
|
|||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.util.Hex;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
|
@ -237,7 +238,12 @@ public class FlipperSqlCipherAdapter extends DatabaseDriver<FlipperSqlCipherAdap
|
|||
case Cursor.FIELD_TYPE_FLOAT:
|
||||
return cursor.getDouble(column);
|
||||
case Cursor.FIELD_TYPE_BLOB:
|
||||
return cursor.getBlob(column);
|
||||
byte[] blob = cursor.getBlob(column);
|
||||
String bytes = blob != null ? "(blob) " + Hex.toStringCondensed(Arrays.copyOf(blob, Math.min(blob.length, 32))) : null;
|
||||
if (bytes != null && bytes.length() == 32 && blob.length > 32) {
|
||||
bytes += "...";
|
||||
}
|
||||
return bytes;
|
||||
case Cursor.FIELD_TYPE_STRING:
|
||||
default:
|
||||
return cursor.getString(column);
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.components;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
|
@ -14,7 +15,11 @@ import androidx.annotation.Nullable;
|
|||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.bumptech.glide.load.MultiTransformation;
|
||||
import com.bumptech.glide.load.Transformation;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.resource.bitmap.CircleCrop;
|
||||
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
@ -23,6 +28,7 @@ import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
|
|||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.ui.managegroup.ManageGroupActivity;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
|
@ -30,9 +36,12 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
|||
import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.recipients.ui.managerecipient.ManageRecipientActivity;
|
||||
import org.thoughtcrime.securesms.util.AvatarUtil;
|
||||
import org.thoughtcrime.securesms.util.BlurTransformation;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class AvatarImageView extends AppCompatImageView {
|
||||
|
@ -63,6 +72,7 @@ public final class AvatarImageView extends AppCompatImageView {
|
|||
private Paint outlinePaint;
|
||||
private OnClickListener listener;
|
||||
private Recipient.FallbackPhotoProvider fallbackPhotoProvider;
|
||||
private boolean blurred;
|
||||
|
||||
private @Nullable RecipientContactPhoto recipientContactPhoto;
|
||||
private @NonNull Drawable unknownRecipientDrawable;
|
||||
|
@ -90,15 +100,16 @@ public final class AvatarImageView extends AppCompatImageView {
|
|||
outlinePaint = ThemeUtil.isDarkTheme(getContext()) ? DARK_THEME_OUTLINE_PAINT : LIGHT_THEME_OUTLINE_PAINT;
|
||||
|
||||
unknownRecipientDrawable = new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20).asDrawable(getContext(), ContactColors.UNKNOWN_COLOR.toConversationColor(getContext()), inverted);
|
||||
blurred = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
float width = getWidth() - getPaddingRight() - getPaddingLeft();
|
||||
float width = getWidth() - getPaddingRight() - getPaddingLeft();
|
||||
float height = getHeight() - getPaddingBottom() - getPaddingTop();
|
||||
float cx = width / 2f;
|
||||
float cx = width / 2f;
|
||||
float cy = height / 2f;
|
||||
float radius = Math.min(cx, cy) - (outlinePaint.getStrokeWidth() / 2f);
|
||||
|
||||
|
@ -160,20 +171,30 @@ public final class AvatarImageView extends AppCompatImageView {
|
|||
Recipient.self().getProfileAvatar()))
|
||||
: new RecipientContactPhoto(recipient);
|
||||
|
||||
if (!photo.equals(recipientContactPhoto)) {
|
||||
boolean shouldBlur = recipient.shouldBlurAvatar();
|
||||
|
||||
if (!photo.equals(recipientContactPhoto) || shouldBlur != blurred) {
|
||||
requestManager.clear(this);
|
||||
recipientContactPhoto = photo;
|
||||
|
||||
Drawable fallbackContactPhotoDrawable = size == SIZE_SMALL
|
||||
? photo.recipient.getSmallFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider)
|
||||
: photo.recipient.getFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider);
|
||||
Drawable fallbackContactPhotoDrawable = size == SIZE_SMALL ? photo.recipient.getSmallFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider)
|
||||
: photo.recipient.getFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider);
|
||||
|
||||
if (photo.contactPhoto != null) {
|
||||
|
||||
List<Transformation<Bitmap>> transforms = new ArrayList<>();
|
||||
if (shouldBlur) {
|
||||
transforms.add(new BlurTransformation(ApplicationDependencies.getApplication(), 0.25f, BlurTransformation.MAX_RADIUS));
|
||||
}
|
||||
transforms.add(new CircleCrop());
|
||||
blurred = shouldBlur;
|
||||
|
||||
requestManager.load(photo.contactPhoto)
|
||||
.fallback(fallbackContactPhotoDrawable)
|
||||
.error(fallbackContactPhotoDrawable)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.circleCrop()
|
||||
.downsample(DownsampleStrategy.CENTER_INSIDE)
|
||||
.transform(new MultiTransformation<>(transforms))
|
||||
.into(this);
|
||||
} else {
|
||||
setImageDrawable(fallbackContactPhotoDrawable);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package org.thoughtcrime.securesms.conversation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
@ -9,11 +11,15 @@ import android.widget.TextView;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.widget.ImageViewCompat;
|
||||
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
|
@ -24,6 +30,7 @@ public class ConversationBannerView extends ConstraintLayout {
|
|||
private TextView contactAbout;
|
||||
private TextView contactSubtitle;
|
||||
private TextView contactDescription;
|
||||
private View tapToView;
|
||||
|
||||
public ConversationBannerView(Context context) {
|
||||
this(context, null);
|
||||
|
@ -43,12 +50,28 @@ public class ConversationBannerView extends ConstraintLayout {
|
|||
contactAbout = findViewById(R.id.message_request_about);
|
||||
contactSubtitle = findViewById(R.id.message_request_subtitle);
|
||||
contactDescription = findViewById(R.id.message_request_description);
|
||||
tapToView = findViewById(R.id.message_request_avatar_tap_to_view);
|
||||
|
||||
contactAvatar.setFallbackPhotoProvider(new FallbackPhotoProvider());
|
||||
}
|
||||
|
||||
public void setAvatar(@NonNull GlideRequests requests, @Nullable Recipient recipient) {
|
||||
contactAvatar.setAvatar(requests, recipient, false);
|
||||
|
||||
if (recipient.shouldBlurAvatar() && recipient.getContactPhoto() != null) {
|
||||
tapToView.setVisibility(VISIBLE);
|
||||
tapToView.setOnClickListener(v -> {
|
||||
SignalExecutors.BOUNDED.execute(() -> DatabaseFactory.getRecipientDatabase(getContext().getApplicationContext())
|
||||
.manuallyShowAvatar(recipient.getId()));
|
||||
});
|
||||
ImageViewCompat.setImageTintList(contactAvatar, ColorStateList.valueOf(ContextCompat.getColor(getContext(), R.color.transparent_black_40)));
|
||||
ImageViewCompat.setImageTintMode(contactAvatar, PorterDuff.Mode.SRC_ATOP);
|
||||
} else {
|
||||
tapToView.setVisibility(GONE);
|
||||
tapToView.setOnClickListener(null);
|
||||
ImageViewCompat.setImageTintList(contactAvatar, null);
|
||||
ImageViewCompat.setImageTintMode(contactAvatar, PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTitle(@Nullable CharSequence title) {
|
||||
|
|
|
@ -15,11 +15,9 @@ import com.google.protobuf.InvalidProtocolBufferException;
|
|||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.storageservice.protos.groups.AccessControl;
|
||||
import org.signal.storageservice.protos.groups.GroupChange;
|
||||
import org.signal.storageservice.protos.groups.Member;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
||||
import org.signal.zkgroup.InvalidInputException;
|
||||
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
|
@ -447,15 +445,17 @@ public final class GroupDatabase extends Database {
|
|||
|
||||
contentValues.put(MMS, groupId.isMms());
|
||||
|
||||
List<RecipientId> groupMembers = members;
|
||||
if (groupMasterKey != null) {
|
||||
if (groupState == null) {
|
||||
throw new AssertionError("V2 master key but no group state");
|
||||
}
|
||||
groupId.requireV2();
|
||||
groupMembers = getV2GroupMembers(groupState);
|
||||
contentValues.put(V2_MASTER_KEY, groupMasterKey.serialize());
|
||||
contentValues.put(V2_REVISION, groupState.getRevision());
|
||||
contentValues.put(V2_DECRYPTED_GROUP, groupState.toByteArray());
|
||||
contentValues.put(MEMBERS, serializeV2GroupMembers(groupState));
|
||||
contentValues.put(MEMBERS, RecipientId.toSerializedList(groupMembers));
|
||||
} else {
|
||||
if (groupId.isV2()) {
|
||||
throw new AssertionError("V2 group id but no master key");
|
||||
|
@ -468,6 +468,10 @@ public final class GroupDatabase extends Database {
|
|||
recipientDatabase.setExpireMessages(groupRecipientId, groupState.getDisappearingMessagesTimer().getDuration());
|
||||
}
|
||||
|
||||
if (groupMembers != null && (groupId.isMms() || Recipient.resolved(groupRecipientId).isProfileSharing())) {
|
||||
recipientDatabase.setHasGroupsInCommon(groupMembers);
|
||||
}
|
||||
|
||||
Recipient.live(groupRecipientId).refresh();
|
||||
|
||||
notifyConversationListListeners();
|
||||
|
@ -585,10 +589,11 @@ public final class GroupDatabase extends Database {
|
|||
contentValues.put(UNMIGRATED_V1_MEMBERS, unmigratedV1Members.isEmpty() ? null : RecipientId.toSerializedList(unmigratedV1Members));
|
||||
}
|
||||
|
||||
List<RecipientId> groupMembers = getV2GroupMembers(decryptedGroup);
|
||||
contentValues.put(TITLE, title);
|
||||
contentValues.put(V2_REVISION, decryptedGroup.getRevision());
|
||||
contentValues.put(V2_DECRYPTED_GROUP, decryptedGroup.toByteArray());
|
||||
contentValues.put(MEMBERS, serializeV2GroupMembers(decryptedGroup));
|
||||
contentValues.put(MEMBERS, RecipientId.toSerializedList(groupMembers));
|
||||
contentValues.put(ACTIVE, gv2GroupActive(decryptedGroup) ? 1 : 0);
|
||||
|
||||
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,
|
||||
|
@ -599,6 +604,10 @@ public final class GroupDatabase extends Database {
|
|||
recipientDatabase.setExpireMessages(groupRecipientId, decryptedGroup.getDisappearingMessagesTimer().getDuration());
|
||||
}
|
||||
|
||||
if (groupMembers != null && (groupId.isMms() || Recipient.resolved(groupRecipientId).isProfileSharing())) {
|
||||
recipientDatabase.setHasGroupsInCommon(groupMembers);
|
||||
}
|
||||
|
||||
Recipient.live(groupRecipientId).refresh();
|
||||
|
||||
notifyConversationListListeners();
|
||||
|
@ -742,11 +751,11 @@ public final class GroupDatabase extends Database {
|
|||
return groupMembers;
|
||||
}
|
||||
|
||||
private static String serializeV2GroupMembers(@NonNull DecryptedGroup decryptedGroup) {
|
||||
private static List<RecipientId> getV2GroupMembers(@NonNull DecryptedGroup decryptedGroup) {
|
||||
List<UUID> uuids = DecryptedGroupUtil.membersToUuidList(decryptedGroup.getMembersList());
|
||||
List<RecipientId> recipientIds = uuidsToRecipientIds(uuids);
|
||||
|
||||
return RecipientId.toSerializedList(recipientIds);
|
||||
return recipientIds;
|
||||
}
|
||||
|
||||
public @NonNull List<GroupId.V2> getAllGroupV2Ids() {
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.text.TextUtils;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.arch.core.util.Function;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.google.protobuf.ByteString;
|
||||
|
@ -31,6 +32,7 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
|||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.DeviceLastResetTime;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileKeyCredentialColumnData;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
|
@ -43,8 +45,8 @@ import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
|||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
|
||||
import org.thoughtcrime.securesms.storage.StorageRecordUpdate;
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncModels;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.Bitmask;
|
||||
|
@ -68,8 +70,6 @@ import org.whispersystems.signalservice.api.storage.SignalAccountRecord;
|
|||
import org.whispersystems.signalservice.api.storage.SignalContactRecord;
|
||||
import org.whispersystems.signalservice.api.storage.SignalGroupV1Record;
|
||||
import org.whispersystems.signalservice.api.storage.SignalGroupV2Record;
|
||||
import org.whispersystems.signalservice.api.storage.SignalRecord;
|
||||
import org.whispersystems.signalservice.api.storage.SignalStorageRecord;
|
||||
import org.whispersystems.signalservice.api.storage.StorageId;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
|
||||
|
@ -142,8 +142,10 @@ public class RecipientDatabase extends Database {
|
|||
private static final String LAST_SESSION_RESET = "last_session_reset";
|
||||
private static final String WALLPAPER = "wallpaper";
|
||||
private static final String WALLPAPER_URI = "wallpaper_file";
|
||||
public static final String ABOUT = "about";
|
||||
public static final String ABOUT_EMOJI = "about_emoji";
|
||||
public static final String ABOUT = "about";
|
||||
public static final String ABOUT_EMOJI = "about_emoji";
|
||||
private static final String EXTRAS = "extras";
|
||||
private static final String GROUPS_IN_COMMON = "groups_in_common";
|
||||
|
||||
public static final String SEARCH_PROFILE_NAME = "search_signal_profile";
|
||||
private static final String SORT_NAME = "sort_name";
|
||||
|
@ -170,12 +172,13 @@ public class RecipientDatabase extends Database {
|
|||
STORAGE_SERVICE_ID, DIRTY,
|
||||
MENTION_SETTING, WALLPAPER, WALLPAPER_URI,
|
||||
MENTION_SETTING,
|
||||
ABOUT, ABOUT_EMOJI
|
||||
ABOUT, ABOUT_EMOJI,
|
||||
EXTRAS, GROUPS_IN_COMMON
|
||||
};
|
||||
|
||||
private static final String[] ID_PROJECTION = new String[]{ID};
|
||||
private static final String[] SEARCH_PROJECTION = new String[]{ID, SYSTEM_JOINED_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, "COALESCE(" + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ") AS " + SEARCH_PROFILE_NAME, "COALESCE(" + nullIfEmpty(SYSTEM_JOINED_NAME) + ", " + nullIfEmpty(SYSTEM_GIVEN_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ") AS " + SORT_NAME};
|
||||
public static final String[] SEARCH_PROJECTION_NAMES = new String[]{ID, SYSTEM_JOINED_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, SEARCH_PROFILE_NAME, SORT_NAME};
|
||||
private static final String[] SEARCH_PROJECTION = new String[]{ID, SYSTEM_JOINED_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, EXTRAS, GROUPS_IN_COMMON, "COALESCE(" + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ") AS " + SEARCH_PROFILE_NAME, "COALESCE(" + nullIfEmpty(SYSTEM_JOINED_NAME) + ", " + nullIfEmpty(SYSTEM_GIVEN_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ") AS " + SORT_NAME};
|
||||
public static final String[] SEARCH_PROJECTION_NAMES = new String[]{ID, SYSTEM_JOINED_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, EXTRAS, GROUPS_IN_COMMON, SEARCH_PROFILE_NAME, SORT_NAME};
|
||||
private static final String[] TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
|
||||
.map(columnName -> TABLE_NAME + "." + columnName)
|
||||
.toList().toArray(new String[0]);
|
||||
|
@ -371,7 +374,9 @@ public class RecipientDatabase extends Database {
|
|||
WALLPAPER + " BLOB DEFAULT NULL, " +
|
||||
WALLPAPER_URI + " TEXT DEFAULT NULL, " +
|
||||
ABOUT + " TEXT DEFAULT NULL, " +
|
||||
ABOUT_EMOJI + " TEXT DEFAULT NULL);";
|
||||
ABOUT_EMOJI + " TEXT DEFAULT NULL, " +
|
||||
EXTRAS + " BLOB DEFAULT NULL, " +
|
||||
GROUPS_IN_COMMON + " INTEGER DEFAULT 0);";
|
||||
|
||||
private static final String INSIGHTS_INVITEE_LIST = "SELECT " + TABLE_NAME + "." + ID +
|
||||
" FROM " + TABLE_NAME +
|
||||
|
@ -1461,6 +1466,7 @@ public class RecipientDatabase extends Database {
|
|||
byte[] wallpaper = CursorUtil.requireBlob(cursor, WALLPAPER);
|
||||
String about = CursorUtil.requireString(cursor, ABOUT);
|
||||
String aboutEmoji = CursorUtil.requireString(cursor, ABOUT_EMOJI);
|
||||
boolean hasGroupsInCommon = CursorUtil.requireBoolean(cursor, GROUPS_IN_COMMON);
|
||||
|
||||
MaterialColor color;
|
||||
byte[] profileKey = null;
|
||||
|
@ -1549,7 +1555,9 @@ public class RecipientDatabase extends Database {
|
|||
chatWallpaper,
|
||||
about,
|
||||
aboutEmoji,
|
||||
getSyncExtras(cursor));
|
||||
getSyncExtras(cursor),
|
||||
getExtras(cursor),
|
||||
hasGroupsInCommon);
|
||||
}
|
||||
|
||||
private static @NonNull RecipientSettings.SyncExtras getSyncExtras(@NonNull Cursor cursor) {
|
||||
|
@ -1565,6 +1573,23 @@ public class RecipientDatabase extends Database {
|
|||
return new RecipientSettings.SyncExtras(storageProto, groupMasterKey, identityKey, identityStatus, archived, forcedUnread);
|
||||
}
|
||||
|
||||
private static @Nullable Recipient.Extras getExtras(@NonNull Cursor cursor) {
|
||||
return Recipient.Extras.from(getRecipientExtras(cursor));
|
||||
}
|
||||
|
||||
private static @Nullable RecipientExtras getRecipientExtras(@NonNull Cursor cursor) {
|
||||
final Optional<byte[]> blob = CursorUtil.getBlob(cursor, EXTRAS);
|
||||
|
||||
return blob.transform(b -> {
|
||||
try {
|
||||
return RecipientExtras.parseFrom(b);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
Log.w(TAG, e);
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}).orNull();
|
||||
}
|
||||
|
||||
public BulkOperationsHandle beginBulkSystemContactUpdate() {
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
database.beginTransaction();
|
||||
|
@ -1986,6 +2011,14 @@ public class RecipientDatabase extends Database {
|
|||
boolean profiledUpdated = update(id, contentValues);
|
||||
boolean colorUpdated = enabled && setColorIfNotSetInternal(id, ContactColors.generateFor(Recipient.resolved(id).getDisplayName(context)));
|
||||
|
||||
if (profiledUpdated && enabled) {
|
||||
Optional<GroupDatabase.GroupRecord> group = DatabaseFactory.getGroupDatabase(context).getGroup(id);
|
||||
|
||||
if (group.isPresent()) {
|
||||
setHasGroupsInCommon(group.get().getMembers());
|
||||
}
|
||||
}
|
||||
|
||||
if (profiledUpdated || colorUpdated) {
|
||||
markDirty(id, DirtyState.UPDATE);
|
||||
Recipient.live(id).refresh();
|
||||
|
@ -2841,6 +2874,93 @@ public class RecipientDatabase extends Database {
|
|||
}
|
||||
}
|
||||
|
||||
public void markPreMessageRequestRecipientsAsProfileSharingEnabled(long messageRequestEnableTime) {
|
||||
String[] whereArgs = SqlUtil.buildArgs(messageRequestEnableTime, messageRequestEnableTime);
|
||||
|
||||
String select = "SELECT r." + ID + " FROM " + TABLE_NAME + " AS r "
|
||||
+ "INNER JOIN " + ThreadDatabase.TABLE_NAME + " AS t ON t." + ThreadDatabase.RECIPIENT_ID + " = r." + ID + " WHERE "
|
||||
+ "r." + PROFILE_SHARING + " = 0 AND "
|
||||
+ "("
|
||||
+ "EXISTS(SELECT 1 FROM " + SmsDatabase.TABLE_NAME + " WHERE " + SmsDatabase.THREAD_ID + " = t." + ThreadDatabase.ID + " AND " + SmsDatabase.DATE_RECEIVED + " < ?) "
|
||||
+ "OR "
|
||||
+ "EXISTS(SELECT 1 FROM " + MmsDatabase.TABLE_NAME + " WHERE " + MmsDatabase.THREAD_ID + " = t." + ThreadDatabase.ID + " AND " + MmsDatabase.DATE_RECEIVED + " < ?) "
|
||||
+ ")";
|
||||
|
||||
List<Long> idsToUpdate = new ArrayList<>();
|
||||
try (Cursor cursor = databaseHelper.getReadableDatabase().rawQuery(select, whereArgs)) {
|
||||
while (cursor.moveToNext()) {
|
||||
idsToUpdate.add(CursorUtil.requireLong(cursor, ID));
|
||||
}
|
||||
}
|
||||
|
||||
if (Util.hasItems(idsToUpdate)) {
|
||||
SqlUtil.Query query = SqlUtil.buildCollectionQuery(ID, idsToUpdate);
|
||||
ContentValues values = new ContentValues(1);
|
||||
values.put(PROFILE_SHARING, 1);
|
||||
databaseHelper.getWritableDatabase().update(TABLE_NAME, values, query.getWhere(), query.getWhereArgs());
|
||||
|
||||
for (long id : idsToUpdate) {
|
||||
Recipient.live(RecipientId.from(id)).refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setHasGroupsInCommon(@NonNull List<RecipientId> recipientIds) {
|
||||
SqlUtil.Query query = SqlUtil.buildCollectionQuery(ID, recipientIds);
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
try (Cursor cursor = db.query(TABLE_NAME,
|
||||
new String[]{ID},
|
||||
query.getWhere() + " AND " + GROUPS_IN_COMMON + " = 0",
|
||||
query.getWhereArgs(),
|
||||
null,
|
||||
null,
|
||||
null))
|
||||
{
|
||||
List<Long> idsToUpdate = new ArrayList<>(cursor.getCount());
|
||||
while (cursor.moveToNext()) {
|
||||
idsToUpdate.add(CursorUtil.requireLong(cursor, ID));
|
||||
}
|
||||
|
||||
if (Util.hasItems(idsToUpdate)) {
|
||||
query = SqlUtil.buildCollectionQuery(ID, idsToUpdate);
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(GROUPS_IN_COMMON, 1);
|
||||
int count = db.update(TABLE_NAME, values, query.getWhere(), query.getWhereArgs());
|
||||
if (count > 0) {
|
||||
for (long id : idsToUpdate) {
|
||||
Recipient.live(RecipientId.from(id)).refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void manuallyShowAvatar(@NonNull RecipientId recipientId) {
|
||||
updateExtras(recipientId, b -> b.setManuallyShownAvatar(true));
|
||||
}
|
||||
|
||||
private void updateExtras(@NonNull RecipientId recipientId, @NonNull Function<RecipientExtras.Builder, RecipientExtras.Builder> updater) {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
db.beginTransaction();
|
||||
try {
|
||||
try (Cursor cursor = db.query(TABLE_NAME, new String[]{ID, EXTRAS}, ID_WHERE, SqlUtil.buildArgs(recipientId), null, null, null)) {
|
||||
if (cursor.moveToNext()) {
|
||||
RecipientExtras state = getRecipientExtras(cursor);
|
||||
RecipientExtras.Builder builder = state != null ? state.toBuilder() : RecipientExtras.newBuilder();
|
||||
byte[] updatedState = updater.apply(builder).build().toByteArray();
|
||||
ContentValues values = new ContentValues(1);
|
||||
values.put(EXTRAS, updatedState);
|
||||
db.update(TABLE_NAME, values, ID_WHERE, SqlUtil.buildArgs(CursorUtil.requireLong(cursor, ID)));
|
||||
}
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
Recipient.live(recipientId).refresh();
|
||||
}
|
||||
|
||||
void markDirty(@NonNull RecipientId recipientId, @NonNull DirtyState dirtyState) {
|
||||
Log.d(TAG, "Attempting to mark " + recipientId + " with dirty state " + dirtyState);
|
||||
|
||||
|
@ -3232,7 +3352,9 @@ public class RecipientDatabase extends Database {
|
|||
private final ChatWallpaper wallpaper;
|
||||
private final String about;
|
||||
private final String aboutEmoji;
|
||||
private final SyncExtras syncExtras;
|
||||
private final SyncExtras syncExtras;
|
||||
private final Recipient.Extras extras;
|
||||
private final boolean hasGroupsInCommon;
|
||||
|
||||
RecipientSettings(@NonNull RecipientId id,
|
||||
@Nullable UUID uuid,
|
||||
|
@ -3273,7 +3395,9 @@ public class RecipientDatabase extends Database {
|
|||
@Nullable ChatWallpaper wallpaper,
|
||||
@Nullable String about,
|
||||
@Nullable String aboutEmoji,
|
||||
@NonNull SyncExtras syncExtras)
|
||||
@NonNull SyncExtras syncExtras,
|
||||
@Nullable Recipient.Extras extras,
|
||||
boolean hasGroupsInCommon)
|
||||
{
|
||||
this.id = id;
|
||||
this.uuid = uuid;
|
||||
|
@ -3316,7 +3440,9 @@ public class RecipientDatabase extends Database {
|
|||
this.wallpaper = wallpaper;
|
||||
this.about = about;
|
||||
this.aboutEmoji = aboutEmoji;
|
||||
this.syncExtras = syncExtras;
|
||||
this.syncExtras = syncExtras;
|
||||
this.extras = extras;
|
||||
this.hasGroupsInCommon = hasGroupsInCommon;
|
||||
}
|
||||
|
||||
public RecipientId getId() {
|
||||
|
@ -3483,6 +3609,14 @@ public class RecipientDatabase extends Database {
|
|||
return syncExtras;
|
||||
}
|
||||
|
||||
public @Nullable Recipient.Extras getExtras() {
|
||||
return extras;
|
||||
}
|
||||
|
||||
public boolean hasGroupsInCommon() {
|
||||
return hasGroupsInCommon;
|
||||
}
|
||||
|
||||
long getCapabilities() {
|
||||
return capabilities;
|
||||
}
|
||||
|
|
|
@ -43,8 +43,8 @@ import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
|
|||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.SqlCipherDatabaseHook;
|
||||
import org.thoughtcrime.securesms.database.StickerDatabase;
|
||||
import org.thoughtcrime.securesms.database.UnknownStorageIdDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.UnknownStorageIdDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
||||
|
@ -172,8 +172,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
|||
private static final int PAYMENTS = 91;
|
||||
private static final int CLEAN_STORAGE_IDS = 92;
|
||||
private static final int MP4_GIF_SUPPORT = 93;
|
||||
private static final int BLUR_AVATARS = 94;
|
||||
|
||||
private static final int DATABASE_VERSION = 93;
|
||||
private static final int DATABASE_VERSION = 94;
|
||||
private static final String DATABASE_NAME = "signal.db";
|
||||
|
||||
private final Context context;
|
||||
|
@ -1304,6 +1305,23 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
|||
db.execSQL("ALTER TABLE part ADD COLUMN video_gif INTEGER DEFAULT 0");
|
||||
}
|
||||
|
||||
if (oldVersion < BLUR_AVATARS) {
|
||||
db.execSQL("ALTER TABLE recipient ADD COLUMN extras BLOB DEFAULT NULL");
|
||||
db.execSQL("ALTER TABLE recipient ADD COLUMN groups_in_common INTEGER DEFAULT 0");
|
||||
|
||||
String secureOutgoingSms = "EXISTS(SELECT 1 FROM sms WHERE thread_id = t._id AND (type & 31) = 23 AND (type & 10485760) AND (type & 131072 = 0))";
|
||||
String secureOutgoingMms = "EXISTS(SELECT 1 FROM mms WHERE thread_id = t._id AND (msg_box & 31) = 23 AND (msg_box & 10485760) AND (msg_box & 131072 = 0))";
|
||||
|
||||
String selectIdsToUpdateProfileSharing = "SELECT r._id FROM recipient AS r INNER JOIN thread AS t ON r._id = t.recipient_ids WHERE profile_sharing = 0 AND (" + secureOutgoingSms + " OR " + secureOutgoingMms + ")";
|
||||
|
||||
db.rawExecSQL("UPDATE recipient SET profile_sharing = 1 WHERE _id IN (" + selectIdsToUpdateProfileSharing + ")");
|
||||
|
||||
String selectIdsWithGroupsInCommon = "SELECT r._id FROM recipient AS r WHERE EXISTS("
|
||||
+ "SELECT 1 FROM groups AS g INNER JOIN recipient AS gr ON (g.recipient_id = gr._id AND gr.profile_sharing = 1) WHERE g.active = 1 AND (g.members LIKE r._id || ',%' OR g.members LIKE '%,' || r._id || ',%' OR g.members LIKE '%,' || r._id)"
|
||||
+ ")";
|
||||
db.rawExecSQL("UPDATE recipient SET groups_in_common = 1 WHERE _id IN (" + selectIdsWithGroupsInCommon + ")");
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.thoughtcrime.securesms.migrations.PassingMigrationJob;
|
|||
import org.thoughtcrime.securesms.migrations.PinOptOutMigration;
|
||||
import org.thoughtcrime.securesms.migrations.PinReminderMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.ProfileMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.ProfileSharingUpdateMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.RecipientSearchMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.RegistrationPinV2MigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.StickerAdditionMigrationJob;
|
||||
|
@ -167,6 +168,7 @@ public final class JobManagerFactories {
|
|||
put(PinOptOutMigration.KEY, new PinOptOutMigration.Factory());
|
||||
put(PinReminderMigrationJob.KEY, new PinReminderMigrationJob.Factory());
|
||||
put(ProfileMigrationJob.KEY, new ProfileMigrationJob.Factory());
|
||||
put(ProfileSharingUpdateMigrationJob.KEY, new ProfileSharingUpdateMigrationJob.Factory());
|
||||
put(RecipientSearchMigrationJob.KEY, new RecipientSearchMigrationJob.Factory());
|
||||
put(RegistrationPinV2MigrationJob.KEY, new RegistrationPinV2MigrationJob.Factory());
|
||||
put(StickerLaunchMigrationJob.KEY, new StickerLaunchMigrationJob.Factory());
|
||||
|
|
|
@ -40,39 +40,40 @@ public class ApplicationMigrations {
|
|||
|
||||
private static final int LEGACY_CANONICAL_VERSION = 455;
|
||||
|
||||
public static final int CURRENT_VERSION = 31;
|
||||
public static final int CURRENT_VERSION = 32;
|
||||
|
||||
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;
|
||||
static final int UUIDS = 6;
|
||||
static final int CACHED_ATTACHMENTS = 7;
|
||||
static final int STICKERS_LAUNCH = 8;
|
||||
//static final int TEST_ARGON2 = 9;
|
||||
static final int SWOON_STICKERS = 10;
|
||||
static final int STORAGE_SERVICE = 11;
|
||||
//static final int STORAGE_KEY_ROTATE = 12;
|
||||
static final int REMOVE_AVATAR_ID = 13;
|
||||
static final int STORAGE_CAPABILITY = 14;
|
||||
static final int PIN_REMINDER = 15;
|
||||
static final int VERSIONED_PROFILE = 16;
|
||||
static final int PIN_OPT_OUT = 17;
|
||||
static final int TRIM_SETTINGS = 18;
|
||||
static final int THUMBNAIL_CLEANUP = 19;
|
||||
static final int GV2 = 20;
|
||||
static final int GV2_2 = 21;
|
||||
static final int CDS = 22;
|
||||
static final int BACKUP_NOTIFICATION = 23;
|
||||
static final int GV1_MIGRATION = 24;
|
||||
static final int USER_NOTIFICATION = 25;
|
||||
static final int DAY_BY_DAY_STICKERS = 26;
|
||||
static final int BLOB_LOCATION = 27;
|
||||
static final int SYSTEM_NAME_SPLIT = 28;
|
||||
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;
|
||||
static final int UUIDS = 6;
|
||||
static final int CACHED_ATTACHMENTS = 7;
|
||||
static final int STICKERS_LAUNCH = 8;
|
||||
//static final int TEST_ARGON2 = 9;
|
||||
static final int SWOON_STICKERS = 10;
|
||||
static final int STORAGE_SERVICE = 11;
|
||||
//static final int STORAGE_KEY_ROTATE = 12;
|
||||
static final int REMOVE_AVATAR_ID = 13;
|
||||
static final int STORAGE_CAPABILITY = 14;
|
||||
static final int PIN_REMINDER = 15;
|
||||
static final int VERSIONED_PROFILE = 16;
|
||||
static final int PIN_OPT_OUT = 17;
|
||||
static final int TRIM_SETTINGS = 18;
|
||||
static final int THUMBNAIL_CLEANUP = 19;
|
||||
static final int GV2 = 20;
|
||||
static final int GV2_2 = 21;
|
||||
static final int CDS = 22;
|
||||
static final int BACKUP_NOTIFICATION = 23;
|
||||
static final int GV1_MIGRATION = 24;
|
||||
static final int USER_NOTIFICATION = 25;
|
||||
static final int DAY_BY_DAY_STICKERS = 26;
|
||||
static final int BLOB_LOCATION = 27;
|
||||
static final int SYSTEM_NAME_SPLIT = 28;
|
||||
// Versions 29, 30 accidentally skipped
|
||||
static final int MUTE_SYNC = 31;
|
||||
static final int MUTE_SYNC = 31;
|
||||
static final int PROFILE_SHARING_UPDATE = 32;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -307,6 +308,10 @@ public class ApplicationMigrations {
|
|||
jobs.put(Version.MUTE_SYNC, new StorageServiceMigrationJob());
|
||||
}
|
||||
|
||||
if (lastSeenVersion < Version.PROFILE_SHARING_UPDATE) {
|
||||
jobs.put(Version.PROFILE_SHARING_UPDATE, new ProfileSharingUpdateMigrationJob());
|
||||
}
|
||||
|
||||
return jobs;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
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.keyvalue.SignalStore;
|
||||
|
||||
/**
|
||||
* Updates profile sharing flag to true if conversation is pre-message request enable time.
|
||||
*/
|
||||
public class ProfileSharingUpdateMigrationJob extends MigrationJob {
|
||||
|
||||
public static final String KEY = "ProfileSharingUpdateMigrationJob";
|
||||
|
||||
ProfileSharingUpdateMigrationJob() {
|
||||
this(new Parameters.Builder().build());
|
||||
}
|
||||
|
||||
private ProfileSharingUpdateMigrationJob(@NonNull Parameters parameters) {
|
||||
super(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUiBlocking() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performMigration() {
|
||||
long messageRequestEnableTime = SignalStore.misc().getMessageRequestEnableTime();
|
||||
DatabaseFactory.getRecipientDatabase(context).markPreMessageRequestRecipientsAsProfileSharingEnabled(messageRequestEnableTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean shouldRetry(@NonNull Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class Factory implements Job.Factory<ProfileSharingUpdateMigrationJob> {
|
||||
@Override
|
||||
public @NonNull ProfileSharingUpdateMigrationJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
return new ProfileSharingUpdateMigrationJob(parameters);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,16 +5,21 @@ import android.content.Intent
|
|||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import com.bumptech.glide.load.MultiTransformation
|
||||
import com.bumptech.glide.load.Transformation
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.resource.bitmap.CircleCrop
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri
|
||||
import org.thoughtcrime.securesms.mms.GlideApp
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||
import org.thoughtcrime.securesms.util.BlurTransformation
|
||||
import java.util.concurrent.ExecutionException
|
||||
|
||||
fun Drawable?.toLargeBitmap(context: Context): Bitmap? {
|
||||
|
@ -32,10 +37,16 @@ fun Recipient.getContactDrawable(context: Context): Drawable? {
|
|||
val fallbackContactPhoto: FallbackContactPhoto = if (isSelf) getFallback(context) else fallbackContactPhoto
|
||||
return if (contactPhoto != null) {
|
||||
try {
|
||||
val transforms: MutableList<Transformation<Bitmap>> = mutableListOf()
|
||||
if (shouldBlurAvatar()) {
|
||||
transforms += BlurTransformation(ApplicationDependencies.getApplication(), 0.25f, BlurTransformation.MAX_RADIUS)
|
||||
}
|
||||
transforms += CircleCrop()
|
||||
|
||||
GlideApp.with(context.applicationContext)
|
||||
.load(contactPhoto)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.circleCrop()
|
||||
.transform(MultiTransformation(transforms))
|
||||
.submit(
|
||||
context.resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_width),
|
||||
context.resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_height)
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.MentionSetting;
|
|||
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
|
@ -111,7 +112,8 @@ public class Recipient {
|
|||
private final String aboutEmoji;
|
||||
private final ProfileName systemProfileName;
|
||||
private final String systemContactName;
|
||||
|
||||
private final Optional<Extras> extras;
|
||||
private final boolean hasGroupsInCommon;
|
||||
|
||||
/**
|
||||
* Returns a {@link LiveRecipient}, which contains a {@link Recipient} that may or may not be
|
||||
|
@ -349,6 +351,8 @@ public class Recipient {
|
|||
this.aboutEmoji = null;
|
||||
this.systemProfileName = ProfileName.EMPTY;
|
||||
this.systemContactName = null;
|
||||
this.extras = Optional.absent();
|
||||
this.hasGroupsInCommon = false;
|
||||
}
|
||||
|
||||
public Recipient(@NonNull RecipientId id, @NonNull RecipientDetails details, boolean resolved) {
|
||||
|
@ -396,6 +400,8 @@ public class Recipient {
|
|||
this.aboutEmoji = details.aboutEmoji;
|
||||
this.systemProfileName = details.systemProfileName;
|
||||
this.systemContactName = details.systemContactName;
|
||||
this.extras = details.extras;
|
||||
this.hasGroupsInCommon = details.hasGroupsInCommon;
|
||||
}
|
||||
|
||||
public @NonNull RecipientId getId() {
|
||||
|
@ -929,6 +935,18 @@ public class Recipient {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean shouldBlurAvatar() {
|
||||
boolean showOverride = false;
|
||||
if (extras.isPresent()) {
|
||||
showOverride = extras.get().manuallyShownAvatar();
|
||||
}
|
||||
return !showOverride && !isSelf() && !isProfileSharing() && !isSystemContact() && !hasGroupsInCommon && isRegistered();
|
||||
}
|
||||
|
||||
public boolean hasGroupsInCommon() {
|
||||
return hasGroupsInCommon;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this recipient is missing crucial data, this will return a populated copy. Otherwise it
|
||||
* returns itself.
|
||||
|
@ -1003,6 +1021,39 @@ public class Recipient {
|
|||
}
|
||||
}
|
||||
|
||||
public static final class Extras {
|
||||
private final RecipientExtras recipientExtras;
|
||||
|
||||
public static @Nullable Extras from(@Nullable RecipientExtras recipientExtras) {
|
||||
if (recipientExtras != null) {
|
||||
return new Extras(recipientExtras);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Extras(@NonNull RecipientExtras extras) {
|
||||
this.recipientExtras = extras;
|
||||
}
|
||||
|
||||
public boolean manuallyShownAvatar() {
|
||||
return recipientExtras.getManuallyShownAvatar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final Extras that = (Extras) o;
|
||||
return manuallyShownAvatar() == that.manuallyShownAvatar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(manuallyShownAvatar());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasSameContent(@NonNull Recipient other) {
|
||||
return Objects.equals(id, other.id) &&
|
||||
resolving == other.resolving &&
|
||||
|
@ -1047,7 +1098,8 @@ public class Recipient {
|
|||
mentionSetting == other.mentionSetting &&
|
||||
Objects.equals(wallpaper, other.wallpaper) &&
|
||||
Objects.equals(about, other.about) &&
|
||||
Objects.equals(aboutEmoji, other.aboutEmoji);
|
||||
Objects.equals(aboutEmoji, other.aboutEmoji) &&
|
||||
Objects.equals(extras, other.extras);
|
||||
}
|
||||
|
||||
private static boolean allContentsAreTheSame(@NonNull List<Recipient> a, @NonNull List<Recipient> b) {
|
||||
|
|
|
@ -27,49 +27,51 @@ import java.util.UUID;
|
|||
|
||||
public class RecipientDetails {
|
||||
|
||||
final UUID uuid;
|
||||
final String username;
|
||||
final String e164;
|
||||
final String email;
|
||||
final GroupId groupId;
|
||||
final String groupName;
|
||||
final String systemContactName;
|
||||
final String customLabel;
|
||||
final Uri systemContactPhoto;
|
||||
final Uri contactUri;
|
||||
final Optional<Long> groupAvatarId;
|
||||
final MaterialColor color;
|
||||
final Uri messageRingtone;
|
||||
final Uri callRingtone;
|
||||
final long mutedUntil;
|
||||
final VibrateState messageVibrateState;
|
||||
final VibrateState callVibrateState;
|
||||
final boolean blocked;
|
||||
final int expireMessages;
|
||||
final List<Recipient> participants;
|
||||
final ProfileName profileName;
|
||||
final Optional<Integer> defaultSubscriptionId;
|
||||
final RegisteredState registered;
|
||||
final byte[] profileKey;
|
||||
final ProfileKeyCredential profileKeyCredential;
|
||||
final String profileAvatar;
|
||||
final boolean hasProfileImage;
|
||||
final boolean profileSharing;
|
||||
final long lastProfileFetch;
|
||||
final boolean systemContact;
|
||||
final boolean isSelf;
|
||||
final String notificationChannel;
|
||||
final UnidentifiedAccessMode unidentifiedAccessMode;
|
||||
final boolean forceSmsSelection;
|
||||
final Recipient.Capability groupsV2Capability;
|
||||
final Recipient.Capability groupsV1MigrationCapability;
|
||||
final InsightsBannerTier insightsBannerTier;
|
||||
final byte[] storageId;
|
||||
final MentionSetting mentionSetting;
|
||||
final ChatWallpaper wallpaper;
|
||||
final String about;
|
||||
final String aboutEmoji;
|
||||
final ProfileName systemProfileName;
|
||||
final UUID uuid;
|
||||
final String username;
|
||||
final String e164;
|
||||
final String email;
|
||||
final GroupId groupId;
|
||||
final String groupName;
|
||||
final String systemContactName;
|
||||
final String customLabel;
|
||||
final Uri systemContactPhoto;
|
||||
final Uri contactUri;
|
||||
final Optional<Long> groupAvatarId;
|
||||
final MaterialColor color;
|
||||
final Uri messageRingtone;
|
||||
final Uri callRingtone;
|
||||
final long mutedUntil;
|
||||
final VibrateState messageVibrateState;
|
||||
final VibrateState callVibrateState;
|
||||
final boolean blocked;
|
||||
final int expireMessages;
|
||||
final List<Recipient> participants;
|
||||
final ProfileName profileName;
|
||||
final Optional<Integer> defaultSubscriptionId;
|
||||
final RegisteredState registered;
|
||||
final byte[] profileKey;
|
||||
final ProfileKeyCredential profileKeyCredential;
|
||||
final String profileAvatar;
|
||||
final boolean hasProfileImage;
|
||||
final boolean profileSharing;
|
||||
final long lastProfileFetch;
|
||||
final boolean systemContact;
|
||||
final boolean isSelf;
|
||||
final String notificationChannel;
|
||||
final UnidentifiedAccessMode unidentifiedAccessMode;
|
||||
final boolean forceSmsSelection;
|
||||
final Recipient.Capability groupsV2Capability;
|
||||
final Recipient.Capability groupsV1MigrationCapability;
|
||||
final InsightsBannerTier insightsBannerTier;
|
||||
final byte[] storageId;
|
||||
final MentionSetting mentionSetting;
|
||||
final ChatWallpaper wallpaper;
|
||||
final String about;
|
||||
final String aboutEmoji;
|
||||
final ProfileName systemProfileName;
|
||||
final Optional<Recipient.Extras> extras;
|
||||
final boolean hasGroupsInCommon;
|
||||
|
||||
public RecipientDetails(@Nullable String groupName,
|
||||
@Nullable String systemContactName,
|
||||
|
@ -122,6 +124,8 @@ public class RecipientDetails {
|
|||
this.systemProfileName = settings.getSystemProfileName();
|
||||
this.groupName = groupName;
|
||||
this.systemContactName = systemContactName;
|
||||
this.extras = Optional.fromNullable(settings.getExtras());
|
||||
this.hasGroupsInCommon = settings.hasGroupsInCommon();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -171,6 +175,8 @@ public class RecipientDetails {
|
|||
this.aboutEmoji = null;
|
||||
this.systemProfileName = ProfileName.EMPTY;
|
||||
this.systemContactName = null;
|
||||
this.extras = Optional.absent();
|
||||
this.hasGroupsInCommon = false;
|
||||
}
|
||||
|
||||
public static @NonNull RecipientDetails forIndividual(@NonNull Context context, @NonNull RecipientSettings settings) {
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.util;
|
|||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.text.TextUtils;
|
||||
|
@ -108,7 +107,11 @@ public final class AvatarUtil {
|
|||
@WorkerThread
|
||||
public static @NonNull Icon getIconForShortcut(@NonNull Context context, @NonNull Recipient recipient) {
|
||||
try {
|
||||
return Icon.createWithAdaptiveBitmap(GlideApp.with(context).asBitmap().load(new ConversationShortcutPhoto(recipient)).submit().get());
|
||||
GlideRequest<Bitmap> glideRequest = GlideApp.with(context).asBitmap().load(new ConversationShortcutPhoto(recipient));
|
||||
if (recipient.shouldBlurAvatar()) {
|
||||
glideRequest = glideRequest.transform(new BlurTransformation(context, 0.25f, BlurTransformation.MAX_RADIUS));
|
||||
}
|
||||
return Icon.createWithAdaptiveBitmap(glideRequest.submit().get());
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
throw new AssertionError("This call should not fail.");
|
||||
}
|
||||
|
@ -117,7 +120,11 @@ public final class AvatarUtil {
|
|||
@WorkerThread
|
||||
public static @NonNull IconCompat getIconCompatForShortcut(@NonNull Context context, @NonNull Recipient recipient) {
|
||||
try {
|
||||
return IconCompat.createWithAdaptiveBitmap(GlideApp.with(context).asBitmap().load(new ConversationShortcutPhoto(recipient)).submit().get());
|
||||
GlideRequest<Bitmap> glideRequest = GlideApp.with(context).asBitmap().load(new ConversationShortcutPhoto(recipient));
|
||||
if (recipient.shouldBlurAvatar()) {
|
||||
glideRequest = glideRequest.transform(new BlurTransformation(context, 0.25f, BlurTransformation.MAX_RADIUS));
|
||||
}
|
||||
return IconCompat.createWithAdaptiveBitmap(glideRequest.submit().get());
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
throw new AssertionError("This call should not fail.");
|
||||
}
|
||||
|
@ -152,9 +159,15 @@ public final class AvatarUtil {
|
|||
photo = recipient.getContactPhoto();
|
||||
}
|
||||
|
||||
return glideRequest.load(photo)
|
||||
.error(getFallback(context, recipient))
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL);
|
||||
final GlideRequest<T> request = glideRequest.load(photo)
|
||||
.error(getFallback(context, recipient))
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL);
|
||||
|
||||
if (recipient.shouldBlurAvatar()) {
|
||||
return request.transform(new BlurTransformation(context, 0.25f, BlurTransformation.MAX_RADIUS));
|
||||
} else {
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
private static Drawable getFallback(@NonNull Context context, @NonNull Recipient recipient) {
|
||||
|
|
|
@ -41,7 +41,9 @@ public final class BlurTransformation extends BitmapTransformation {
|
|||
Matrix scaleMatrix = new Matrix();
|
||||
scaleMatrix.setScale(bitmapScaleFactor, bitmapScaleFactor);
|
||||
|
||||
Bitmap blurredBitmap = Bitmap.createBitmap(toTransform, 0, 0, outWidth, outHeight, scaleMatrix, true);
|
||||
int targetWidth = Math.min(outWidth, toTransform.getWidth());
|
||||
int targetHeight = Math.min(outHeight, toTransform.getHeight());
|
||||
Bitmap blurredBitmap = Bitmap.createBitmap(toTransform, 0, 0, targetWidth, targetHeight, scaleMatrix, true);
|
||||
Allocation input = Allocation.createFromBitmap(rs, blurredBitmap, Allocation.MipmapControl.MIPMAP_FULL, Allocation.USAGE_SHARED);
|
||||
Allocation output = Allocation.createTyped(rs, input.getType());
|
||||
ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
|
||||
|
|
|
@ -124,4 +124,8 @@ message Wallpaper {
|
|||
}
|
||||
|
||||
float dimLevelInDarkTheme = 4;
|
||||
}
|
||||
|
||||
message RecipientExtras {
|
||||
bool manuallyShownAvatar = 1;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M14,13.08L12.4,8.83C12.3377,8.658 12.2416,8.5003 12.1173,8.366C11.993,8.2318 11.8431,8.1238 11.6765,8.0485C11.5098,7.9731 11.3297,7.9319 11.1469,7.9272C10.964,7.9225 10.7821,7.9545 10.6118,8.0213C10.4415,8.0881 10.2863,8.1883 10.1554,8.316C10.0244,8.4438 9.9204,8.5964 9.8494,8.765C9.7784,8.9335 9.7419,9.1146 9.742,9.2976C9.7421,9.4805 9.7788,9.6615 9.85,9.83"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M16.76,12.52L15.44,9.12C15.3802,8.9497 15.2864,8.7934 15.1643,8.6605C15.0422,8.5276 14.8943,8.4209 14.7297,8.347C14.565,8.2731 14.387,8.2335 14.2066,8.2306C14.0261,8.2276 13.847,8.2615 13.68,8.33C13.5093,8.3909 13.3528,8.4857 13.2198,8.6087C13.0869,8.7318 12.9802,8.8806 12.9064,9.046C12.8325,9.2115 12.793,9.3902 12.7902,9.5713C12.7874,9.7525 12.8213,9.9323 12.89,10.1"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M2.39,7.81C2.1523,7.2139 2.0361,6.5762 2.0482,5.9346C2.0602,5.2929 2.2003,4.6601 2.4601,4.0733C2.72,3.4865 3.0945,2.9575 3.5615,2.5173C4.0286,2.0771 4.5788,1.7347 5.18,1.51C5.7757,1.2753 6.4122,1.1614 7.0523,1.1749C7.6925,1.1884 8.3236,1.329 8.9089,1.5886C9.4942,1.8483 10.022,2.2217 10.4617,2.6871C10.9014,3.1526 11.2441,3.7009 11.47,4.3"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M11.49,14.06L8.21,5.56C8.071,5.231 7.809,4.9693 7.4798,4.8307C7.1507,4.692 6.7804,4.6874 6.4479,4.8178C6.1154,4.9482 5.8469,5.2033 5.6997,5.5287C5.5526,5.8542 5.5383,6.2242 5.66,6.56L8.94,15.07L9.43,16.34L9.71,17.07C9.71,17.07 7.34,15.49 5.26,13.9C3.72,12.69 2.55,14.6 3.65,15.55C7.98,19.55 11.53,24.67 17.07,22.19C22.61,19.71 20.79,15.38 20.13,13.68L18.65,9.83L18.49,9.41C18.4277,9.238 18.3316,9.0803 18.2073,8.946C18.083,8.8118 17.9331,8.7038 17.7665,8.6285C17.5998,8.5531 17.4197,8.5119 17.2369,8.5072C17.054,8.5025 16.8721,8.5345 16.7018,8.6013C16.5315,8.6681 16.3763,8.7683 16.2454,8.896C16.1144,9.0238 16.0104,9.1764 15.9394,9.345C15.8684,9.5135 15.8319,9.6946 15.832,9.8776C15.8321,10.0605 15.8688,10.2415 15.94,10.41"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
|
@ -13,7 +13,37 @@
|
|||
android:layout_height="112dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:srcCompat="@tools:sample/avatars" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/message_request_avatar_tap_to_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/message_request_avatar"
|
||||
app:layout_constraintEnd_toEndOf="@+id/message_request_avatar"
|
||||
app:layout_constraintStart_toStartOf="@+id/message_request_avatar"
|
||||
app:layout_constraintTop_toTopOf="@+id/message_request_avatar">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
app:tint="@color/core_white"
|
||||
app:srcCompat="@drawable/ic_tap_outline_24" />
|
||||
|
||||
<TextView
|
||||
android:textAppearance="@style/TextAppearance.Signal.Subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/core_white"
|
||||
android:text="@string/MessageRequestProfileView_view" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/message_request_title"
|
||||
|
|
|
@ -1234,6 +1234,7 @@
|
|||
<string name="MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept">Join this group? They won’t know you’ve seen their messages until you accept.</string>
|
||||
<string name="MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members">Unblock this group and share your name and photo with its members? You won\'t receive any messages until you unblock them.</string>
|
||||
<string name="MessageRequestBottomView_legacy_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007459591</string>
|
||||
<string name="MessageRequestProfileView_view">View</string>
|
||||
<string name="MessageRequestProfileView_member_of_one_group">Member of %1$s</string>
|
||||
<string name="MessageRequestProfileView_member_of_two_groups">Member of %1$s and %2$s</string>
|
||||
<string name="MessageRequestProfileView_member_of_many_groups">Member of %1$s, %2$s, and %3$s</string>
|
||||
|
|
|
@ -7,6 +7,8 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -111,6 +113,14 @@ public final class SqlUtilTest {
|
|||
assertArrayEquals(new String[] { "1", "2", "3" }, updateQuery.getWhereArgs());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildCollectionQuery_multipleRecipientIds() {
|
||||
SqlUtil.Query updateQuery = SqlUtil.buildCollectionQuery("a", Arrays.asList(RecipientId.from(1), RecipientId.from(2), RecipientId.from(3)));
|
||||
|
||||
assertEquals("a IN (?, ?, ?)", updateQuery.getWhere());
|
||||
assertArrayEquals(new String[] { "1", "2", "3" }, updateQuery.getWhereArgs());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void buildCollectionQuery_none() {
|
||||
SqlUtil.buildCollectionQuery("a", Collections.emptyList());
|
||||
|
|
Ładowanie…
Reference in New Issue