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.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||||
|
import org.thoughtcrime.securesms.util.Hex;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -237,7 +238,12 @@ public class FlipperSqlCipherAdapter extends DatabaseDriver<FlipperSqlCipherAdap
|
||||||
case Cursor.FIELD_TYPE_FLOAT:
|
case Cursor.FIELD_TYPE_FLOAT:
|
||||||
return cursor.getDouble(column);
|
return cursor.getDouble(column);
|
||||||
case Cursor.FIELD_TYPE_BLOB:
|
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:
|
case Cursor.FIELD_TYPE_STRING:
|
||||||
default:
|
default:
|
||||||
return cursor.getString(column);
|
return cursor.getString(column);
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.components;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
@ -14,7 +15,11 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.widget.AppCompatImageView;
|
import androidx.appcompat.widget.AppCompatImageView;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
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.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.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.R;
|
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.ContactPhoto;
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
|
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
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.groups.ui.managegroup.ManageGroupActivity;
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
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.bottomsheet.RecipientBottomSheetDialogFragment;
|
||||||
import org.thoughtcrime.securesms.recipients.ui.managerecipient.ManageRecipientActivity;
|
import org.thoughtcrime.securesms.recipients.ui.managerecipient.ManageRecipientActivity;
|
||||||
import org.thoughtcrime.securesms.util.AvatarUtil;
|
import org.thoughtcrime.securesms.util.AvatarUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.BlurTransformation;
|
||||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public final class AvatarImageView extends AppCompatImageView {
|
public final class AvatarImageView extends AppCompatImageView {
|
||||||
|
@ -63,6 +72,7 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||||
private Paint outlinePaint;
|
private Paint outlinePaint;
|
||||||
private OnClickListener listener;
|
private OnClickListener listener;
|
||||||
private Recipient.FallbackPhotoProvider fallbackPhotoProvider;
|
private Recipient.FallbackPhotoProvider fallbackPhotoProvider;
|
||||||
|
private boolean blurred;
|
||||||
|
|
||||||
private @Nullable RecipientContactPhoto recipientContactPhoto;
|
private @Nullable RecipientContactPhoto recipientContactPhoto;
|
||||||
private @NonNull Drawable unknownRecipientDrawable;
|
private @NonNull Drawable unknownRecipientDrawable;
|
||||||
|
@ -90,6 +100,7 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||||
outlinePaint = ThemeUtil.isDarkTheme(getContext()) ? DARK_THEME_OUTLINE_PAINT : LIGHT_THEME_OUTLINE_PAINT;
|
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);
|
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
|
@Override
|
||||||
|
@ -160,20 +171,30 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||||
Recipient.self().getProfileAvatar()))
|
Recipient.self().getProfileAvatar()))
|
||||||
: new RecipientContactPhoto(recipient);
|
: new RecipientContactPhoto(recipient);
|
||||||
|
|
||||||
if (!photo.equals(recipientContactPhoto)) {
|
boolean shouldBlur = recipient.shouldBlurAvatar();
|
||||||
|
|
||||||
|
if (!photo.equals(recipientContactPhoto) || shouldBlur != blurred) {
|
||||||
requestManager.clear(this);
|
requestManager.clear(this);
|
||||||
recipientContactPhoto = photo;
|
recipientContactPhoto = photo;
|
||||||
|
|
||||||
Drawable fallbackContactPhotoDrawable = size == SIZE_SMALL
|
Drawable fallbackContactPhotoDrawable = size == SIZE_SMALL ? photo.recipient.getSmallFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider)
|
||||||
? photo.recipient.getSmallFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider)
|
|
||||||
: photo.recipient.getFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider);
|
: photo.recipient.getFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider);
|
||||||
|
|
||||||
if (photo.contactPhoto != null) {
|
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)
|
requestManager.load(photo.contactPhoto)
|
||||||
.fallback(fallbackContactPhotoDrawable)
|
.fallback(fallbackContactPhotoDrawable)
|
||||||
.error(fallbackContactPhotoDrawable)
|
.error(fallbackContactPhotoDrawable)
|
||||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||||
.circleCrop()
|
.downsample(DownsampleStrategy.CENTER_INSIDE)
|
||||||
|
.transform(new MultiTransformation<>(transforms))
|
||||||
.into(this);
|
.into(this);
|
||||||
} else {
|
} else {
|
||||||
setImageDrawable(fallbackContactPhotoDrawable);
|
setImageDrawable(fallbackContactPhotoDrawable);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package org.thoughtcrime.securesms.conversation;
|
package org.thoughtcrime.securesms.conversation;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -9,11 +11,15 @@ import android.widget.TextView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
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.R;
|
||||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
|
||||||
|
@ -24,6 +30,7 @@ public class ConversationBannerView extends ConstraintLayout {
|
||||||
private TextView contactAbout;
|
private TextView contactAbout;
|
||||||
private TextView contactSubtitle;
|
private TextView contactSubtitle;
|
||||||
private TextView contactDescription;
|
private TextView contactDescription;
|
||||||
|
private View tapToView;
|
||||||
|
|
||||||
public ConversationBannerView(Context context) {
|
public ConversationBannerView(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
|
@ -43,12 +50,28 @@ public class ConversationBannerView extends ConstraintLayout {
|
||||||
contactAbout = findViewById(R.id.message_request_about);
|
contactAbout = findViewById(R.id.message_request_about);
|
||||||
contactSubtitle = findViewById(R.id.message_request_subtitle);
|
contactSubtitle = findViewById(R.id.message_request_subtitle);
|
||||||
contactDescription = findViewById(R.id.message_request_description);
|
contactDescription = findViewById(R.id.message_request_description);
|
||||||
|
tapToView = findViewById(R.id.message_request_avatar_tap_to_view);
|
||||||
|
|
||||||
contactAvatar.setFallbackPhotoProvider(new FallbackPhotoProvider());
|
contactAvatar.setFallbackPhotoProvider(new FallbackPhotoProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAvatar(@NonNull GlideRequests requests, @Nullable Recipient recipient) {
|
public void setAvatar(@NonNull GlideRequests requests, @Nullable Recipient recipient) {
|
||||||
contactAvatar.setAvatar(requests, recipient, false);
|
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) {
|
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.core.util.logging.Log;
|
||||||
import org.signal.storageservice.protos.groups.AccessControl;
|
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.Member;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
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.InvalidInputException;
|
||||||
import org.signal.zkgroup.groups.GroupMasterKey;
|
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||||
|
@ -447,15 +445,17 @@ public final class GroupDatabase extends Database {
|
||||||
|
|
||||||
contentValues.put(MMS, groupId.isMms());
|
contentValues.put(MMS, groupId.isMms());
|
||||||
|
|
||||||
|
List<RecipientId> groupMembers = members;
|
||||||
if (groupMasterKey != null) {
|
if (groupMasterKey != null) {
|
||||||
if (groupState == null) {
|
if (groupState == null) {
|
||||||
throw new AssertionError("V2 master key but no group state");
|
throw new AssertionError("V2 master key but no group state");
|
||||||
}
|
}
|
||||||
groupId.requireV2();
|
groupId.requireV2();
|
||||||
|
groupMembers = getV2GroupMembers(groupState);
|
||||||
contentValues.put(V2_MASTER_KEY, groupMasterKey.serialize());
|
contentValues.put(V2_MASTER_KEY, groupMasterKey.serialize());
|
||||||
contentValues.put(V2_REVISION, groupState.getRevision());
|
contentValues.put(V2_REVISION, groupState.getRevision());
|
||||||
contentValues.put(V2_DECRYPTED_GROUP, groupState.toByteArray());
|
contentValues.put(V2_DECRYPTED_GROUP, groupState.toByteArray());
|
||||||
contentValues.put(MEMBERS, serializeV2GroupMembers(groupState));
|
contentValues.put(MEMBERS, RecipientId.toSerializedList(groupMembers));
|
||||||
} else {
|
} else {
|
||||||
if (groupId.isV2()) {
|
if (groupId.isV2()) {
|
||||||
throw new AssertionError("V2 group id but no master key");
|
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());
|
recipientDatabase.setExpireMessages(groupRecipientId, groupState.getDisappearingMessagesTimer().getDuration());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (groupMembers != null && (groupId.isMms() || Recipient.resolved(groupRecipientId).isProfileSharing())) {
|
||||||
|
recipientDatabase.setHasGroupsInCommon(groupMembers);
|
||||||
|
}
|
||||||
|
|
||||||
Recipient.live(groupRecipientId).refresh();
|
Recipient.live(groupRecipientId).refresh();
|
||||||
|
|
||||||
notifyConversationListListeners();
|
notifyConversationListListeners();
|
||||||
|
@ -585,10 +589,11 @@ public final class GroupDatabase extends Database {
|
||||||
contentValues.put(UNMIGRATED_V1_MEMBERS, unmigratedV1Members.isEmpty() ? null : RecipientId.toSerializedList(unmigratedV1Members));
|
contentValues.put(UNMIGRATED_V1_MEMBERS, unmigratedV1Members.isEmpty() ? null : RecipientId.toSerializedList(unmigratedV1Members));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<RecipientId> groupMembers = getV2GroupMembers(decryptedGroup);
|
||||||
contentValues.put(TITLE, title);
|
contentValues.put(TITLE, title);
|
||||||
contentValues.put(V2_REVISION, decryptedGroup.getRevision());
|
contentValues.put(V2_REVISION, decryptedGroup.getRevision());
|
||||||
contentValues.put(V2_DECRYPTED_GROUP, decryptedGroup.toByteArray());
|
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);
|
contentValues.put(ACTIVE, gv2GroupActive(decryptedGroup) ? 1 : 0);
|
||||||
|
|
||||||
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,
|
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,
|
||||||
|
@ -599,6 +604,10 @@ public final class GroupDatabase extends Database {
|
||||||
recipientDatabase.setExpireMessages(groupRecipientId, decryptedGroup.getDisappearingMessagesTimer().getDuration());
|
recipientDatabase.setExpireMessages(groupRecipientId, decryptedGroup.getDisappearingMessagesTimer().getDuration());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (groupMembers != null && (groupId.isMms() || Recipient.resolved(groupRecipientId).isProfileSharing())) {
|
||||||
|
recipientDatabase.setHasGroupsInCommon(groupMembers);
|
||||||
|
}
|
||||||
|
|
||||||
Recipient.live(groupRecipientId).refresh();
|
Recipient.live(groupRecipientId).refresh();
|
||||||
|
|
||||||
notifyConversationListListeners();
|
notifyConversationListListeners();
|
||||||
|
@ -742,11 +751,11 @@ public final class GroupDatabase extends Database {
|
||||||
return groupMembers;
|
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<UUID> uuids = DecryptedGroupUtil.membersToUuidList(decryptedGroup.getMembersList());
|
||||||
List<RecipientId> recipientIds = uuidsToRecipientIds(uuids);
|
List<RecipientId> recipientIds = uuidsToRecipientIds(uuids);
|
||||||
|
|
||||||
return RecipientId.toSerializedList(recipientIds);
|
return recipientIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull List<GroupId.V2> getAllGroupV2Ids() {
|
public @NonNull List<GroupId.V2> getAllGroupV2Ids() {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.arch.core.util.Function;
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
import com.google.protobuf.ByteString;
|
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.ThreadRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.DeviceLastResetTime;
|
import org.thoughtcrime.securesms.database.model.databaseprotos.DeviceLastResetTime;
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileKeyCredentialColumnData;
|
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.database.model.databaseprotos.Wallpaper;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.groups.GroupId;
|
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.profiles.ProfileName;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
|
|
||||||
import org.thoughtcrime.securesms.storage.StorageRecordUpdate;
|
import org.thoughtcrime.securesms.storage.StorageRecordUpdate;
|
||||||
|
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
|
||||||
import org.thoughtcrime.securesms.storage.StorageSyncModels;
|
import org.thoughtcrime.securesms.storage.StorageSyncModels;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
import org.thoughtcrime.securesms.util.Bitmask;
|
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.SignalContactRecord;
|
||||||
import org.whispersystems.signalservice.api.storage.SignalGroupV1Record;
|
import org.whispersystems.signalservice.api.storage.SignalGroupV1Record;
|
||||||
import org.whispersystems.signalservice.api.storage.SignalGroupV2Record;
|
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.storage.StorageId;
|
||||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
|
|
||||||
|
@ -144,6 +144,8 @@ public class RecipientDatabase extends Database {
|
||||||
private static final String WALLPAPER_URI = "wallpaper_file";
|
private static final String WALLPAPER_URI = "wallpaper_file";
|
||||||
public static final String ABOUT = "about";
|
public static final String ABOUT = "about";
|
||||||
public static final String ABOUT_EMOJI = "about_emoji";
|
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";
|
public static final String SEARCH_PROFILE_NAME = "search_signal_profile";
|
||||||
private static final String SORT_NAME = "sort_name";
|
private static final String SORT_NAME = "sort_name";
|
||||||
|
@ -170,12 +172,13 @@ public class RecipientDatabase extends Database {
|
||||||
STORAGE_SERVICE_ID, DIRTY,
|
STORAGE_SERVICE_ID, DIRTY,
|
||||||
MENTION_SETTING, WALLPAPER, WALLPAPER_URI,
|
MENTION_SETTING, WALLPAPER, WALLPAPER_URI,
|
||||||
MENTION_SETTING,
|
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[] 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};
|
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, SEARCH_PROFILE_NAME, 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)
|
private static final String[] TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
|
||||||
.map(columnName -> TABLE_NAME + "." + columnName)
|
.map(columnName -> TABLE_NAME + "." + columnName)
|
||||||
.toList().toArray(new String[0]);
|
.toList().toArray(new String[0]);
|
||||||
|
@ -371,7 +374,9 @@ public class RecipientDatabase extends Database {
|
||||||
WALLPAPER + " BLOB DEFAULT NULL, " +
|
WALLPAPER + " BLOB DEFAULT NULL, " +
|
||||||
WALLPAPER_URI + " TEXT DEFAULT NULL, " +
|
WALLPAPER_URI + " TEXT DEFAULT NULL, " +
|
||||||
ABOUT + " 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 +
|
private static final String INSIGHTS_INVITEE_LIST = "SELECT " + TABLE_NAME + "." + ID +
|
||||||
" FROM " + TABLE_NAME +
|
" FROM " + TABLE_NAME +
|
||||||
|
@ -1461,6 +1466,7 @@ public class RecipientDatabase extends Database {
|
||||||
byte[] wallpaper = CursorUtil.requireBlob(cursor, WALLPAPER);
|
byte[] wallpaper = CursorUtil.requireBlob(cursor, WALLPAPER);
|
||||||
String about = CursorUtil.requireString(cursor, ABOUT);
|
String about = CursorUtil.requireString(cursor, ABOUT);
|
||||||
String aboutEmoji = CursorUtil.requireString(cursor, ABOUT_EMOJI);
|
String aboutEmoji = CursorUtil.requireString(cursor, ABOUT_EMOJI);
|
||||||
|
boolean hasGroupsInCommon = CursorUtil.requireBoolean(cursor, GROUPS_IN_COMMON);
|
||||||
|
|
||||||
MaterialColor color;
|
MaterialColor color;
|
||||||
byte[] profileKey = null;
|
byte[] profileKey = null;
|
||||||
|
@ -1549,7 +1555,9 @@ public class RecipientDatabase extends Database {
|
||||||
chatWallpaper,
|
chatWallpaper,
|
||||||
about,
|
about,
|
||||||
aboutEmoji,
|
aboutEmoji,
|
||||||
getSyncExtras(cursor));
|
getSyncExtras(cursor),
|
||||||
|
getExtras(cursor),
|
||||||
|
hasGroupsInCommon);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NonNull RecipientSettings.SyncExtras getSyncExtras(@NonNull Cursor cursor) {
|
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);
|
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() {
|
public BulkOperationsHandle beginBulkSystemContactUpdate() {
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
database.beginTransaction();
|
database.beginTransaction();
|
||||||
|
@ -1986,6 +2011,14 @@ public class RecipientDatabase extends Database {
|
||||||
boolean profiledUpdated = update(id, contentValues);
|
boolean profiledUpdated = update(id, contentValues);
|
||||||
boolean colorUpdated = enabled && setColorIfNotSetInternal(id, ContactColors.generateFor(Recipient.resolved(id).getDisplayName(context)));
|
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) {
|
if (profiledUpdated || colorUpdated) {
|
||||||
markDirty(id, DirtyState.UPDATE);
|
markDirty(id, DirtyState.UPDATE);
|
||||||
Recipient.live(id).refresh();
|
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) {
|
void markDirty(@NonNull RecipientId recipientId, @NonNull DirtyState dirtyState) {
|
||||||
Log.d(TAG, "Attempting to mark " + recipientId + " with dirty state " + dirtyState);
|
Log.d(TAG, "Attempting to mark " + recipientId + " with dirty state " + dirtyState);
|
||||||
|
|
||||||
|
@ -3233,6 +3353,8 @@ public class RecipientDatabase extends Database {
|
||||||
private final String about;
|
private final String about;
|
||||||
private final String aboutEmoji;
|
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,
|
RecipientSettings(@NonNull RecipientId id,
|
||||||
@Nullable UUID uuid,
|
@Nullable UUID uuid,
|
||||||
|
@ -3273,7 +3395,9 @@ public class RecipientDatabase extends Database {
|
||||||
@Nullable ChatWallpaper wallpaper,
|
@Nullable ChatWallpaper wallpaper,
|
||||||
@Nullable String about,
|
@Nullable String about,
|
||||||
@Nullable String aboutEmoji,
|
@Nullable String aboutEmoji,
|
||||||
@NonNull SyncExtras syncExtras)
|
@NonNull SyncExtras syncExtras,
|
||||||
|
@Nullable Recipient.Extras extras,
|
||||||
|
boolean hasGroupsInCommon)
|
||||||
{
|
{
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
|
@ -3317,6 +3441,8 @@ public class RecipientDatabase extends Database {
|
||||||
this.about = about;
|
this.about = about;
|
||||||
this.aboutEmoji = aboutEmoji;
|
this.aboutEmoji = aboutEmoji;
|
||||||
this.syncExtras = syncExtras;
|
this.syncExtras = syncExtras;
|
||||||
|
this.extras = extras;
|
||||||
|
this.hasGroupsInCommon = hasGroupsInCommon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecipientId getId() {
|
public RecipientId getId() {
|
||||||
|
@ -3483,6 +3609,14 @@ public class RecipientDatabase extends Database {
|
||||||
return syncExtras;
|
return syncExtras;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @Nullable Recipient.Extras getExtras() {
|
||||||
|
return extras;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasGroupsInCommon() {
|
||||||
|
return hasGroupsInCommon;
|
||||||
|
}
|
||||||
|
|
||||||
long getCapabilities() {
|
long getCapabilities() {
|
||||||
return capabilities;
|
return capabilities;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,8 +43,8 @@ import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.SqlCipherDatabaseHook;
|
import org.thoughtcrime.securesms.database.SqlCipherDatabaseHook;
|
||||||
import org.thoughtcrime.securesms.database.StickerDatabase;
|
import org.thoughtcrime.securesms.database.StickerDatabase;
|
||||||
import org.thoughtcrime.securesms.database.UnknownStorageIdDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.UnknownStorageIdDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.groups.GroupId;
|
import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
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 PAYMENTS = 91;
|
||||||
private static final int CLEAN_STORAGE_IDS = 92;
|
private static final int CLEAN_STORAGE_IDS = 92;
|
||||||
private static final int MP4_GIF_SUPPORT = 93;
|
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 static final String DATABASE_NAME = "signal.db";
|
||||||
|
|
||||||
private final Context context;
|
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");
|
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();
|
db.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.thoughtcrime.securesms.migrations.PassingMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.PinOptOutMigration;
|
import org.thoughtcrime.securesms.migrations.PinOptOutMigration;
|
||||||
import org.thoughtcrime.securesms.migrations.PinReminderMigrationJob;
|
import org.thoughtcrime.securesms.migrations.PinReminderMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.ProfileMigrationJob;
|
import org.thoughtcrime.securesms.migrations.ProfileMigrationJob;
|
||||||
|
import org.thoughtcrime.securesms.migrations.ProfileSharingUpdateMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.RecipientSearchMigrationJob;
|
import org.thoughtcrime.securesms.migrations.RecipientSearchMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.RegistrationPinV2MigrationJob;
|
import org.thoughtcrime.securesms.migrations.RegistrationPinV2MigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.StickerAdditionMigrationJob;
|
import org.thoughtcrime.securesms.migrations.StickerAdditionMigrationJob;
|
||||||
|
@ -167,6 +168,7 @@ public final class JobManagerFactories {
|
||||||
put(PinOptOutMigration.KEY, new PinOptOutMigration.Factory());
|
put(PinOptOutMigration.KEY, new PinOptOutMigration.Factory());
|
||||||
put(PinReminderMigrationJob.KEY, new PinReminderMigrationJob.Factory());
|
put(PinReminderMigrationJob.KEY, new PinReminderMigrationJob.Factory());
|
||||||
put(ProfileMigrationJob.KEY, new ProfileMigrationJob.Factory());
|
put(ProfileMigrationJob.KEY, new ProfileMigrationJob.Factory());
|
||||||
|
put(ProfileSharingUpdateMigrationJob.KEY, new ProfileSharingUpdateMigrationJob.Factory());
|
||||||
put(RecipientSearchMigrationJob.KEY, new RecipientSearchMigrationJob.Factory());
|
put(RecipientSearchMigrationJob.KEY, new RecipientSearchMigrationJob.Factory());
|
||||||
put(RegistrationPinV2MigrationJob.KEY, new RegistrationPinV2MigrationJob.Factory());
|
put(RegistrationPinV2MigrationJob.KEY, new RegistrationPinV2MigrationJob.Factory());
|
||||||
put(StickerLaunchMigrationJob.KEY, new StickerLaunchMigrationJob.Factory());
|
put(StickerLaunchMigrationJob.KEY, new StickerLaunchMigrationJob.Factory());
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class ApplicationMigrations {
|
||||||
|
|
||||||
private static final int LEGACY_CANONICAL_VERSION = 455;
|
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 {
|
private static final class Version {
|
||||||
static final int LEGACY = 1;
|
static final int LEGACY = 1;
|
||||||
|
@ -73,6 +73,7 @@ public class ApplicationMigrations {
|
||||||
static final int SYSTEM_NAME_SPLIT = 28;
|
static final int SYSTEM_NAME_SPLIT = 28;
|
||||||
// Versions 29, 30 accidentally skipped
|
// 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());
|
jobs.put(Version.MUTE_SYNC, new StorageServiceMigrationJob());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lastSeenVersion < Version.PROFILE_SHARING_UPDATE) {
|
||||||
|
jobs.put(Version.PROFILE_SHARING_UPDATE, new ProfileSharingUpdateMigrationJob());
|
||||||
|
}
|
||||||
|
|
||||||
return jobs;
|
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.Bitmap
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.net.Uri
|
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.engine.DiskCacheStrategy
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.CircleCrop
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto
|
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto
|
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto
|
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto
|
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.DecryptableStreamUriLoader.DecryptableUri
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||||
|
import org.thoughtcrime.securesms.util.BlurTransformation
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
|
|
||||||
fun Drawable?.toLargeBitmap(context: Context): Bitmap? {
|
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
|
val fallbackContactPhoto: FallbackContactPhoto = if (isSelf) getFallback(context) else fallbackContactPhoto
|
||||||
return if (contactPhoto != null) {
|
return if (contactPhoto != null) {
|
||||||
try {
|
try {
|
||||||
|
val transforms: MutableList<Transformation<Bitmap>> = mutableListOf()
|
||||||
|
if (shouldBlurAvatar()) {
|
||||||
|
transforms += BlurTransformation(ApplicationDependencies.getApplication(), 0.25f, BlurTransformation.MAX_RADIUS)
|
||||||
|
}
|
||||||
|
transforms += CircleCrop()
|
||||||
|
|
||||||
GlideApp.with(context.applicationContext)
|
GlideApp.with(context.applicationContext)
|
||||||
.load(contactPhoto)
|
.load(contactPhoto)
|
||||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||||
.circleCrop()
|
.transform(MultiTransformation(transforms))
|
||||||
.submit(
|
.submit(
|
||||||
context.resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_width),
|
context.resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_width),
|
||||||
context.resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_height)
|
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.RegisteredState;
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
|
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
|
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.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.groups.GroupId;
|
import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
|
@ -111,7 +112,8 @@ public class Recipient {
|
||||||
private final String aboutEmoji;
|
private final String aboutEmoji;
|
||||||
private final ProfileName systemProfileName;
|
private final ProfileName systemProfileName;
|
||||||
private final String systemContactName;
|
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
|
* 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.aboutEmoji = null;
|
||||||
this.systemProfileName = ProfileName.EMPTY;
|
this.systemProfileName = ProfileName.EMPTY;
|
||||||
this.systemContactName = null;
|
this.systemContactName = null;
|
||||||
|
this.extras = Optional.absent();
|
||||||
|
this.hasGroupsInCommon = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recipient(@NonNull RecipientId id, @NonNull RecipientDetails details, boolean resolved) {
|
public Recipient(@NonNull RecipientId id, @NonNull RecipientDetails details, boolean resolved) {
|
||||||
|
@ -396,6 +400,8 @@ public class Recipient {
|
||||||
this.aboutEmoji = details.aboutEmoji;
|
this.aboutEmoji = details.aboutEmoji;
|
||||||
this.systemProfileName = details.systemProfileName;
|
this.systemProfileName = details.systemProfileName;
|
||||||
this.systemContactName = details.systemContactName;
|
this.systemContactName = details.systemContactName;
|
||||||
|
this.extras = details.extras;
|
||||||
|
this.hasGroupsInCommon = details.hasGroupsInCommon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull RecipientId getId() {
|
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
|
* If this recipient is missing crucial data, this will return a populated copy. Otherwise it
|
||||||
* returns itself.
|
* 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) {
|
public boolean hasSameContent(@NonNull Recipient other) {
|
||||||
return Objects.equals(id, other.id) &&
|
return Objects.equals(id, other.id) &&
|
||||||
resolving == other.resolving &&
|
resolving == other.resolving &&
|
||||||
|
@ -1047,7 +1098,8 @@ public class Recipient {
|
||||||
mentionSetting == other.mentionSetting &&
|
mentionSetting == other.mentionSetting &&
|
||||||
Objects.equals(wallpaper, other.wallpaper) &&
|
Objects.equals(wallpaper, other.wallpaper) &&
|
||||||
Objects.equals(about, other.about) &&
|
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) {
|
private static boolean allContentsAreTheSame(@NonNull List<Recipient> a, @NonNull List<Recipient> b) {
|
||||||
|
|
|
@ -70,6 +70,8 @@ public class RecipientDetails {
|
||||||
final String about;
|
final String about;
|
||||||
final String aboutEmoji;
|
final String aboutEmoji;
|
||||||
final ProfileName systemProfileName;
|
final ProfileName systemProfileName;
|
||||||
|
final Optional<Recipient.Extras> extras;
|
||||||
|
final boolean hasGroupsInCommon;
|
||||||
|
|
||||||
public RecipientDetails(@Nullable String groupName,
|
public RecipientDetails(@Nullable String groupName,
|
||||||
@Nullable String systemContactName,
|
@Nullable String systemContactName,
|
||||||
|
@ -122,6 +124,8 @@ public class RecipientDetails {
|
||||||
this.systemProfileName = settings.getSystemProfileName();
|
this.systemProfileName = settings.getSystemProfileName();
|
||||||
this.groupName = groupName;
|
this.groupName = groupName;
|
||||||
this.systemContactName = systemContactName;
|
this.systemContactName = systemContactName;
|
||||||
|
this.extras = Optional.fromNullable(settings.getExtras());
|
||||||
|
this.hasGroupsInCommon = settings.hasGroupsInCommon();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -171,6 +175,8 @@ public class RecipientDetails {
|
||||||
this.aboutEmoji = null;
|
this.aboutEmoji = null;
|
||||||
this.systemProfileName = ProfileName.EMPTY;
|
this.systemProfileName = ProfileName.EMPTY;
|
||||||
this.systemContactName = null;
|
this.systemContactName = null;
|
||||||
|
this.extras = Optional.absent();
|
||||||
|
this.hasGroupsInCommon = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull RecipientDetails forIndividual(@NonNull Context context, @NonNull RecipientSettings settings) {
|
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.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.Icon;
|
import android.graphics.drawable.Icon;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -108,7 +107,11 @@ public final class AvatarUtil {
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static @NonNull Icon getIconForShortcut(@NonNull Context context, @NonNull Recipient recipient) {
|
public static @NonNull Icon getIconForShortcut(@NonNull Context context, @NonNull Recipient recipient) {
|
||||||
try {
|
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) {
|
} catch (ExecutionException | InterruptedException e) {
|
||||||
throw new AssertionError("This call should not fail.");
|
throw new AssertionError("This call should not fail.");
|
||||||
}
|
}
|
||||||
|
@ -117,7 +120,11 @@ public final class AvatarUtil {
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static @NonNull IconCompat getIconCompatForShortcut(@NonNull Context context, @NonNull Recipient recipient) {
|
public static @NonNull IconCompat getIconCompatForShortcut(@NonNull Context context, @NonNull Recipient recipient) {
|
||||||
try {
|
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) {
|
} catch (ExecutionException | InterruptedException e) {
|
||||||
throw new AssertionError("This call should not fail.");
|
throw new AssertionError("This call should not fail.");
|
||||||
}
|
}
|
||||||
|
@ -152,9 +159,15 @@ public final class AvatarUtil {
|
||||||
photo = recipient.getContactPhoto();
|
photo = recipient.getContactPhoto();
|
||||||
}
|
}
|
||||||
|
|
||||||
return glideRequest.load(photo)
|
final GlideRequest<T> request = glideRequest.load(photo)
|
||||||
.error(getFallback(context, recipient))
|
.error(getFallback(context, recipient))
|
||||||
.diskCacheStrategy(DiskCacheStrategy.ALL);
|
.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) {
|
private static Drawable getFallback(@NonNull Context context, @NonNull Recipient recipient) {
|
||||||
|
|
|
@ -41,7 +41,9 @@ public final class BlurTransformation extends BitmapTransformation {
|
||||||
Matrix scaleMatrix = new Matrix();
|
Matrix scaleMatrix = new Matrix();
|
||||||
scaleMatrix.setScale(bitmapScaleFactor, bitmapScaleFactor);
|
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 input = Allocation.createFromBitmap(rs, blurredBitmap, Allocation.MipmapControl.MIPMAP_FULL, Allocation.USAGE_SHARED);
|
||||||
Allocation output = Allocation.createTyped(rs, input.getType());
|
Allocation output = Allocation.createTyped(rs, input.getType());
|
||||||
ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
|
ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
|
||||||
|
|
|
@ -125,3 +125,7 @@ message Wallpaper {
|
||||||
|
|
||||||
float dimLevelInDarkTheme = 4;
|
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"
|
android:layout_height="112dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="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
|
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
android:id="@+id/message_request_title"
|
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_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_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="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_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_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>
|
<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.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -111,6 +113,14 @@ public final class SqlUtilTest {
|
||||||
assertArrayEquals(new String[] { "1", "2", "3" }, updateQuery.getWhereArgs());
|
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)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void buildCollectionQuery_none() {
|
public void buildCollectionQuery_none() {
|
||||||
SqlUtil.buildCollectionQuery("a", Collections.emptyList());
|
SqlUtil.buildCollectionQuery("a", Collections.emptyList());
|
||||||
|
|
Ładowanie…
Reference in New Issue