This commit is contained in:
Brice 2021-01-11 11:06:01 +11:00
commit f420ec51e2
17 changed files with 3723 additions and 4 deletions

View File

@ -1,20 +1,20 @@
package org.session.libsession.messaging
import android.net.Uri
import org.session.libsession.messaging.jobs.AttachmentUploadJob
import org.session.libsession.messaging.jobs.Job
import org.session.libsession.messaging.jobs.MessageSendJob
import org.session.libsession.messaging.messages.Message
import org.session.libsession.messaging.messages.visible.Attachment
import org.session.libsession.messaging.opengroups.OpenGroup
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
import org.session.libsession.messaging.threads.Address
import org.session.libsession.messaging.threads.GroupRecord
import org.session.libsession.messaging.threads.recipients.Recipient.RecipientSettings
import org.session.libsignal.libsignal.ecc.ECKeyPair
import org.session.libsignal.libsignal.ecc.ECPrivateKey
import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer
import java.security.PublicKey
interface StorageProtocol {
@ -114,6 +114,10 @@ interface StorageProtocol {
fun getServerDisplayName(serverID: String, publicKey: String): String?
fun getProfilePictureURL(publicKey: String): String?
//Recipient
// Recipient
fun getRecipientSettings(address: Address): RecipientSettings?
// PartAuthority
fun getAttachmentDataUri(attachmentId: AttachmentId): Uri
fun getAttachmentThumbnailUri(attachmentId: AttachmentId): Uri
}

View File

@ -0,0 +1,150 @@
package org.session.libsession.messaging.messages.signal;
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment;
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
import org.session.libsession.messaging.threads.Address;
import org.session.libsession.utilities.GroupUtil;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
import org.session.libsignal.service.api.messages.SignalServiceGroup;
import org.session.libsession.messaging.sending_receiving.contacts.Contact;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class IncomingMediaMessage {
private final Address from;
private final Address groupId;
private final String body;
private final boolean push;
private final long sentTimeMillis;
private final int subscriptionId;
private final long expiresIn;
private final boolean expirationUpdate;
private final QuoteModel quote;
private final boolean unidentified;
private final List<Attachment> attachments = new LinkedList<>();
private final List<Contact> sharedContacts = new LinkedList<>();
private final List<LinkPreview> linkPreviews = new LinkedList<>();
public IncomingMediaMessage(Address from,
Optional<Address> groupId,
String body,
long sentTimeMillis,
List<Attachment> attachments,
int subscriptionId,
long expiresIn,
boolean expirationUpdate,
boolean unidentified)
{
this.from = from;
this.groupId = groupId.orNull();
this.sentTimeMillis = sentTimeMillis;
this.body = body;
this.push = false;
this.subscriptionId = subscriptionId;
this.expiresIn = expiresIn;
this.expirationUpdate = expirationUpdate;
this.quote = null;
this.unidentified = unidentified;
this.attachments.addAll(attachments);
}
public IncomingMediaMessage(Address from,
long sentTimeMillis,
int subscriptionId,
long expiresIn,
boolean expirationUpdate,
boolean unidentified,
Optional<String> body,
Optional<SignalServiceGroup> group,
Optional<List<SignalServiceAttachment>> attachments,
Optional<QuoteModel> quote,
Optional<List<Contact>> sharedContacts,
Optional<List<LinkPreview>> linkPreviews,
Optional<Attachment> sticker)
{
this.push = true;
this.from = from;
this.sentTimeMillis = sentTimeMillis;
this.body = body.orNull();
this.subscriptionId = subscriptionId;
this.expiresIn = expiresIn;
this.expirationUpdate = expirationUpdate;
this.quote = quote.orNull();
this.unidentified = unidentified;
if (group.isPresent()) this.groupId = Address.Companion.fromSerialized(GroupUtil.INSTANCE.getEncodedGroupID(group.get().getGroupId()));
else this.groupId = null;
this.attachments.addAll(PointerAttachment.forPointers(attachments));
this.sharedContacts.addAll(sharedContacts.or(Collections.emptyList()));
this.linkPreviews.addAll(linkPreviews.or(Collections.emptyList()));
if (sticker.isPresent()) {
this.attachments.add(sticker.get());
}
}
public int getSubscriptionId() {
return subscriptionId;
}
public String getBody() {
return body;
}
public List<Attachment> getAttachments() {
return attachments;
}
public Address getFrom() {
return from;
}
public Address getGroupId() {
return groupId;
}
public boolean isPushMessage() {
return push;
}
public boolean isExpirationUpdate() {
return expirationUpdate;
}
public long getSentTimeMillis() {
return sentTimeMillis;
}
public long getExpiresIn() {
return expiresIn;
}
public boolean isGroupMessage() {
return groupId != null;
}
public QuoteModel getQuote() {
return quote;
}
public List<Contact> getSharedContacts() {
return sharedContacts;
}
public List<LinkPreview> getLinkPreviews() {
return linkPreviews;
}
public boolean isUnidentified() {
return unidentified;
}
}

View File

@ -0,0 +1,155 @@
package org.session.libsession.messaging.sending_receiving.attachments;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public abstract class Attachment {
@NonNull
private final String contentType;
private final int transferState;
private final long size;
@Nullable
private final String fileName;
@Nullable
private final String location;
@Nullable
private final String key;
@Nullable
private final String relay;
@Nullable
private final byte[] digest;
@Nullable
private final String fastPreflightId;
private final boolean voiceNote;
private final int width;
private final int height;
private final boolean quote;
@Nullable
private final String caption;
@Nullable
private final StickerLocator stickerLocator;
// Loki
private final String url;
public Attachment(@NonNull String contentType, int transferState, long size, @Nullable String fileName,
@Nullable String location, @Nullable String key, @Nullable String relay,
@Nullable byte[] digest, @Nullable String fastPreflightId, boolean voiceNote,
int width, int height, boolean quote, @Nullable String caption, @Nullable StickerLocator stickerLocator, String url)
{
this.contentType = contentType;
this.transferState = transferState;
this.size = size;
this.fileName = fileName;
this.location = location;
this.key = key;
this.relay = relay;
this.digest = digest;
this.fastPreflightId = fastPreflightId;
this.voiceNote = voiceNote;
this.width = width;
this.height = height;
this.quote = quote;
this.stickerLocator = stickerLocator;
this.caption = caption;
this.url = url;
}
@Nullable
public abstract Uri getDataUri();
@Nullable
public abstract Uri getThumbnailUri();
public int getTransferState() {
return transferState;
}
public boolean isInProgress() {
return transferState != AttachmentTransferProgress.TRANSFER_PROGRESS_DONE.getValue() &&
transferState != AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED.getValue();
}
public long getSize() {
return size;
}
@Nullable
public String getFileName() {
return fileName;
}
@NonNull
public String getContentType() {
return contentType;
}
@Nullable
public String getLocation() {
return location;
}
@Nullable
public String getKey() {
return key;
}
@Nullable
public String getRelay() {
return relay;
}
@Nullable
public byte[] getDigest() {
return digest;
}
@Nullable
public String getFastPreflightId() {
return fastPreflightId;
}
public boolean isVoiceNote() {
return voiceNote;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public boolean isQuote() {
return quote;
}
public boolean isSticker() {
return stickerLocator != null;
}
public @Nullable StickerLocator getSticker() {
return stickerLocator;
}
public @Nullable String getCaption() {
return caption;
}
public String getUrl() { return url; }
}

View File

@ -0,0 +1,89 @@
package org.session.libsession.messaging.sending_receiving.attachments;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.session.libsession.utilities.Util;
public class AttachmentId implements Parcelable {
@JsonProperty
private final long rowId;
@JsonProperty
private final long uniqueId;
public AttachmentId(@JsonProperty("rowId") long rowId, @JsonProperty("uniqueId") long uniqueId) {
this.rowId = rowId;
this.uniqueId = uniqueId;
}
public long getRowId() {
return rowId;
}
public long getUniqueId() {
return uniqueId;
}
public String[] toStrings() {
return new String[] {String.valueOf(rowId), String.valueOf(uniqueId)};
}
public @NonNull String toString() {
return "(row id: " + rowId + ", unique ID: " + uniqueId + ")";
}
public boolean isValid() {
return rowId >= 0 && uniqueId >= 0;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AttachmentId attachmentId = (AttachmentId)o;
if (rowId != attachmentId.rowId) return false;
return uniqueId == attachmentId.uniqueId;
}
@Override
public int hashCode() {
return Util.hashCode(rowId, uniqueId);
}
//region Parcelable implementation.
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(rowId);
dest.writeLong(uniqueId);
}
public static final Creator<AttachmentId> CREATOR =
new Creator<AttachmentId>() {
@Override
public AttachmentId createFromParcel(Parcel in) {
long rowId = in.readLong();
long uniqueId = in.readLong();
return new AttachmentId(rowId, uniqueId);
}
@Override
public AttachmentId[] newArray(int size) {
return new AttachmentId[size];
}
};
//endregion
}

View File

@ -0,0 +1,8 @@
package org.session.libsession.messaging.sending_receiving.attachments
enum class AttachmentTransferProgress(val value: Int) {
TRANSFER_PROGRESS_DONE(0),
TRANSFER_PROGRESS_STARTED(1),
TRANSFER_PROGRESS_PENDING(2),
TRANSFER_PROGRESS_FAILED(3)
}

View File

@ -0,0 +1,87 @@
package org.session.libsession.messaging.sending_receiving.attachments;
import android.net.Uri;
import androidx.annotation.Nullable;
import org.session.libsession.messaging.MessagingConfiguration;
public class DatabaseAttachment extends Attachment {
private final AttachmentId attachmentId;
private final long mmsId;
private final boolean hasData;
private final boolean hasThumbnail;
private boolean isUploaded = false;
public DatabaseAttachment(AttachmentId attachmentId, long mmsId,
boolean hasData, boolean hasThumbnail,
String contentType, int transferProgress, long size,
String fileName, String location, String key, String relay,
byte[] digest, String fastPreflightId, boolean voiceNote,
int width, int height, boolean quote, @Nullable String caption,
@Nullable StickerLocator stickerLocator, String url)
{
super(contentType, transferProgress, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, quote, caption, stickerLocator, url);
this.attachmentId = attachmentId;
this.hasData = hasData;
this.hasThumbnail = hasThumbnail;
this.mmsId = mmsId;
}
@Override
@Nullable
public Uri getDataUri() {
if (hasData) {
return MessagingConfiguration.shared.getStorage().getAttachmentDataUri(attachmentId);
} else {
return null;
}
}
@Override
@Nullable
public Uri getThumbnailUri() {
if (hasThumbnail) {
return MessagingConfiguration.shared.getStorage().getAttachmentThumbnailUri(attachmentId);
} else {
return null;
}
}
public AttachmentId getAttachmentId() {
return attachmentId;
}
@Override
public boolean equals(Object other) {
return other != null &&
other instanceof DatabaseAttachment &&
((DatabaseAttachment) other).attachmentId.equals(this.attachmentId);
}
@Override
public int hashCode() {
return attachmentId.hashCode();
}
public long getMmsId() {
return mmsId;
}
public boolean hasData() {
return hasData;
}
public boolean hasThumbnail() {
return hasThumbnail;
}
public boolean isUploaded() {
return isUploaded;
}
public void setUploaded(boolean uploaded) {
isUploaded = uploaded;
}
}

View File

@ -0,0 +1,125 @@
package org.session.libsession.messaging.sending_receiving.attachments;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
import org.session.libsignal.service.api.messages.SignalServiceDataMessage;
import org.session.libsession.utilities.Base64;
import java.util.LinkedList;
import java.util.List;
public class PointerAttachment extends Attachment {
private PointerAttachment(@NonNull String contentType, int transferState, long size,
@Nullable String fileName, @NonNull String location,
@Nullable String key, @Nullable String relay,
@Nullable byte[] digest, @Nullable String fastPreflightId, boolean voiceNote,
int width, int height, @Nullable String caption, @Nullable StickerLocator stickerLocator, String url)
{
super(contentType, transferState, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, false, caption, stickerLocator, url);
}
@Nullable
@Override
public Uri getDataUri() {
return null;
}
@Nullable
@Override
public Uri getThumbnailUri() {
return null;
}
public static List<Attachment> forPointers(Optional<List<SignalServiceAttachment>> pointers) {
List<Attachment> results = new LinkedList<>();
if (pointers.isPresent()) {
for (SignalServiceAttachment pointer : pointers.get()) {
Optional<Attachment> result = forPointer(Optional.of(pointer));
if (result.isPresent()) {
results.add(result.get());
}
}
}
return results;
}
public static List<Attachment> forPointers(List<SignalServiceDataMessage.Quote.QuotedAttachment> pointers) {
List<Attachment> results = new LinkedList<>();
if (pointers != null) {
for (SignalServiceDataMessage.Quote.QuotedAttachment pointer : pointers) {
Optional<Attachment> result = forPointer(pointer);
if (result.isPresent()) {
results.add(result.get());
}
}
}
return results;
}
public static Optional<Attachment> forPointer(Optional<SignalServiceAttachment> pointer) {
return forPointer(pointer, null, null);
}
public static Optional<Attachment> forPointer(Optional<SignalServiceAttachment> pointer, @Nullable StickerLocator stickerLocator) {
return forPointer(pointer, stickerLocator, null);
}
public static Optional<Attachment> forPointer(Optional<SignalServiceAttachment> pointer, @Nullable StickerLocator stickerLocator, @Nullable String fastPreflightId) {
if (!pointer.isPresent() || !pointer.get().isPointer()) return Optional.absent();
String encodedKey = null;
if (pointer.get().asPointer().getKey() != null) {
encodedKey = Base64.encodeBytes(pointer.get().asPointer().getKey());
}
return Optional.of(new PointerAttachment(pointer.get().getContentType(),
AttachmentTransferProgress.TRANSFER_PROGRESS_PENDING.getValue(),
pointer.get().asPointer().getSize().or(0),
pointer.get().asPointer().getFileName().orNull(),
String.valueOf(pointer.get().asPointer().getId()),
encodedKey, null,
pointer.get().asPointer().getDigest().orNull(),
fastPreflightId,
pointer.get().asPointer().getVoiceNote(),
pointer.get().asPointer().getWidth(),
pointer.get().asPointer().getHeight(),
pointer.get().asPointer().getCaption().orNull(),
stickerLocator,
pointer.get().asPointer().getUrl()));
}
public static Optional<Attachment> forPointer(SignalServiceDataMessage.Quote.QuotedAttachment pointer) {
SignalServiceAttachment thumbnail = pointer.getThumbnail();
return Optional.of(new PointerAttachment(pointer.getContentType(),
AttachmentTransferProgress.TRANSFER_PROGRESS_PENDING.getValue(),
thumbnail != null ? thumbnail.asPointer().getSize().or(0) : 0,
pointer.getFileName(),
String.valueOf(thumbnail != null ? thumbnail.asPointer().getId() : 0),
thumbnail != null && thumbnail.asPointer().getKey() != null ? Base64.encodeBytes(thumbnail.asPointer().getKey()) : null,
null,
thumbnail != null ? thumbnail.asPointer().getDigest().orNull() : null,
null,
false,
thumbnail != null ? thumbnail.asPointer().getWidth() : 0,
thumbnail != null ? thumbnail.asPointer().getHeight() : 0,
thumbnail != null ? thumbnail.asPointer().getCaption().orNull() : null,
null,
thumbnail != null ? thumbnail.asPointer().getUrl() : ""));
}
}

View File

@ -0,0 +1,61 @@
package org.session.libsession.messaging.sending_receiving.attachments;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
public class StickerLocator implements Parcelable {
private final String packId;
private final String packKey;
private final int stickerId;
public StickerLocator(@NonNull String packId, @NonNull String packKey, int stickerId) {
this.packId = packId;
this.packKey = packKey;
this.stickerId = stickerId;
}
private StickerLocator(Parcel in) {
packId = in.readString();
packKey = in.readString();
stickerId = in.readInt();
}
public @NonNull String getPackId() {
return packId;
}
public @NonNull String getPackKey() {
return packKey;
}
public @NonNull int getStickerId() {
return stickerId;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(packId);
dest.writeString(packKey);
dest.writeInt(stickerId);
}
public static final Creator<StickerLocator> CREATOR = new Creator<StickerLocator>() {
@Override
public StickerLocator createFromParcel(Parcel in) {
return new StickerLocator(in);
}
@Override
public StickerLocator[] newArray(int size) {
return new StickerLocator[size];
}
};
}

View File

@ -0,0 +1,51 @@
package org.session.libsession.messaging.sending_receiving.attachments;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class UriAttachment extends Attachment {
private final @NonNull Uri dataUri;
private final @Nullable Uri thumbnailUri;
public UriAttachment(@NonNull Uri uri, @NonNull String contentType, int transferState, long size,
@Nullable String fileName, boolean voiceNote, boolean quote, @Nullable String caption,
@Nullable StickerLocator stickerLocator)
{
this(uri, uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, quote, caption, stickerLocator);
}
public UriAttachment(@NonNull Uri dataUri, @Nullable Uri thumbnailUri,
@NonNull String contentType, int transferState, long size, int width, int height,
@Nullable String fileName, @Nullable String fastPreflightId,
boolean voiceNote, boolean quote, @Nullable String caption, @Nullable StickerLocator stickerLocator)
{
super(contentType, transferState, size, fileName, null, null, null, null, fastPreflightId, voiceNote, width, height, quote, caption, stickerLocator, "");
this.dataUri = dataUri;
this.thumbnailUri = thumbnailUri;
}
@Override
@NonNull
public Uri getDataUri() {
return dataUri;
}
@Override
@Nullable
public Uri getThumbnailUri() {
return thumbnailUri;
}
@Override
public boolean equals(Object other) {
return other != null && other instanceof UriAttachment && ((UriAttachment) other).dataUri.equals(this.dataUri);
}
@Override
public int hashCode() {
return dataUri.hashCode();
}
}

View File

@ -0,0 +1,667 @@
package org.session.libsession.messaging.sending_receiving.contacts;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress;
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId;
import org.session.libsession.messaging.sending_receiving.attachments.UriAttachment;
import org.session.libsession.utilities.JsonUtils;
import org.session.libsession.utilities.MediaTypes;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
public class Contact implements Parcelable {
@JsonProperty
private final Name name;
@JsonProperty
private final String organization;
@JsonProperty
private final List<Phone> phoneNumbers;
@JsonProperty
private final List<Email> emails;
@JsonProperty
private final List<PostalAddress> postalAddresses;
@JsonProperty
private final Avatar avatar;
public Contact(@JsonProperty("name") @NonNull Name name,
@JsonProperty("organization") @Nullable String organization,
@JsonProperty("phoneNumbers") @NonNull List<Phone> phoneNumbers,
@JsonProperty("emails") @NonNull List<Email> emails,
@JsonProperty("postalAddresses") @NonNull List<PostalAddress> postalAddresses,
@JsonProperty("avatar") @Nullable Avatar avatar)
{
this.name = name;
this.organization = organization;
this.phoneNumbers = Collections.unmodifiableList(phoneNumbers);
this.emails = Collections.unmodifiableList(emails);
this.postalAddresses = Collections.unmodifiableList(postalAddresses);
this.avatar = avatar;
}
public Contact(@NonNull Contact contact, @Nullable Avatar avatar) {
this(contact.getName(),
contact.getOrganization(),
contact.getPhoneNumbers(),
contact.getEmails(),
contact.getPostalAddresses(),
avatar);
}
private Contact(Parcel in) {
this(in.readParcelable(Name.class.getClassLoader()),
in.readString(),
in.createTypedArrayList(Phone.CREATOR),
in.createTypedArrayList(Email.CREATOR),
in.createTypedArrayList(PostalAddress.CREATOR),
in.readParcelable(Avatar.class.getClassLoader()));
}
public @NonNull Name getName() {
return name;
}
public @Nullable String getOrganization() {
return organization;
}
public @NonNull List<Phone> getPhoneNumbers() {
return phoneNumbers;
}
public @NonNull List<Email> getEmails() {
return emails;
}
public @NonNull List<PostalAddress> getPostalAddresses() {
return postalAddresses;
}
public @Nullable Avatar getAvatar() {
return avatar;
}
@JsonIgnore
public @Nullable Attachment getAvatarAttachment() {
return avatar != null ? avatar.getAttachment() : null;
}
public String serialize() throws IOException {
return JsonUtils.toJson(this);
}
public static Contact deserialize(@NonNull String serialized) throws IOException {
return JsonUtils.fromJson(serialized, Contact.class);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(name, flags);
dest.writeString(organization);
dest.writeTypedList(phoneNumbers);
dest.writeTypedList(emails);
dest.writeTypedList(postalAddresses);
dest.writeParcelable(avatar, flags);
}
public static final Creator<Contact> CREATOR = new Creator<Contact>() {
@Override
public Contact createFromParcel(Parcel in) {
return new Contact(in);
}
@Override
public Contact[] newArray(int size) {
return new Contact[size];
}
};
public static class Name implements Parcelable {
@JsonProperty
private final String displayName;
@JsonProperty
private final String givenName;
@JsonProperty
private final String familyName;
@JsonProperty
private final String prefix;
@JsonProperty
private final String suffix;
@JsonProperty
private final String middleName;
Name(@JsonProperty("displayName") @Nullable String displayName,
@JsonProperty("givenName") @Nullable String givenName,
@JsonProperty("familyName") @Nullable String familyName,
@JsonProperty("prefix") @Nullable String prefix,
@JsonProperty("suffix") @Nullable String suffix,
@JsonProperty("middleName") @Nullable String middleName)
{
this.displayName = displayName;
this.givenName = givenName;
this.familyName = familyName;
this.prefix = prefix;
this.suffix = suffix;
this.middleName = middleName;
}
private Name(Parcel in) {
this(in.readString(), in.readString(), in.readString(), in.readString(), in.readString(), in.readString());
}
public @Nullable String getDisplayName() {
return displayName;
}
public @Nullable String getGivenName() {
return givenName;
}
public @Nullable String getFamilyName() {
return familyName;
}
public @Nullable String getPrefix() {
return prefix;
}
public @Nullable String getSuffix() {
return suffix;
}
public @Nullable String getMiddleName() {
return middleName;
}
public boolean isEmpty() {
return TextUtils.isEmpty(displayName) &&
TextUtils.isEmpty(givenName) &&
TextUtils.isEmpty(familyName) &&
TextUtils.isEmpty(prefix) &&
TextUtils.isEmpty(suffix) &&
TextUtils.isEmpty(middleName);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(displayName);
dest.writeString(givenName);
dest.writeString(familyName);
dest.writeString(prefix);
dest.writeString(suffix);
dest.writeString(middleName);
}
public static final Creator<Name> CREATOR = new Creator<Name>() {
@Override
public Name createFromParcel(Parcel in) {
return new Name(in);
}
@Override
public Name[] newArray(int size) {
return new Name[size];
}
};
}
public static class Phone implements Selectable, Parcelable {
@JsonProperty
private final String number;
@JsonProperty
private final Type type;
@JsonProperty
private final String label;
@JsonIgnore
private boolean selected;
Phone(@JsonProperty("number") @NonNull String number,
@JsonProperty("type") @NonNull Type type,
@JsonProperty("label") @Nullable String label)
{
this.number = number;
this.type = type;
this.label = label;
this.selected = true;
}
private Phone(Parcel in) {
this(in.readString(), Type.valueOf(in.readString()), in.readString());
}
public @NonNull String getNumber() {
return number;
}
public @NonNull Type getType() {
return type;
}
public @Nullable String getLabel() {
return label;
}
@Override
public void setSelected(boolean selected) {
this.selected = selected;
}
@Override
public boolean isSelected() {
return selected;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(number);
dest.writeString(type.name());
dest.writeString(label);
}
public static final Creator<Phone> CREATOR = new Creator<Phone>() {
@Override
public Phone createFromParcel(Parcel in) {
return new Phone(in);
}
@Override
public Phone[] newArray(int size) {
return new Phone[size];
}
};
public enum Type {
HOME, MOBILE, WORK, CUSTOM
}
}
public static class Email implements Selectable, Parcelable {
@JsonProperty
private final String email;
@JsonProperty
private final Type type;
@JsonProperty
private final String label;
@JsonIgnore
private boolean selected;
Email(@JsonProperty("email") @NonNull String email,
@JsonProperty("type") @NonNull Type type,
@JsonProperty("label") @Nullable String label)
{
this.email = email;
this.type = type;
this.label = label;
this.selected = true;
}
private Email(Parcel in) {
this(in.readString(), Type.valueOf(in.readString()), in.readString());
}
public @NonNull String getEmail() {
return email;
}
public @NonNull Type getType() {
return type;
}
public @NonNull String getLabel() {
return label;
}
@Override
public void setSelected(boolean selected) {
this.selected = selected;
}
@Override
public boolean isSelected() {
return selected;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(email);
dest.writeString(type.name());
dest.writeString(label);
}
public static final Creator<Email> CREATOR = new Creator<Email>() {
@Override
public Email createFromParcel(Parcel in) {
return new Email(in);
}
@Override
public Email[] newArray(int size) {
return new Email[size];
}
};
public enum Type {
HOME, MOBILE, WORK, CUSTOM
}
}
public static class PostalAddress implements Selectable, Parcelable {
@JsonProperty
private final Type type;
@JsonProperty
private final String label;
@JsonProperty
private final String street;
@JsonProperty
private final String poBox;
@JsonProperty
private final String neighborhood;
@JsonProperty
private final String city;
@JsonProperty
private final String region;
@JsonProperty
private final String postalCode;
@JsonProperty
private final String country;
@JsonIgnore
private boolean selected;
PostalAddress(@JsonProperty("type") @NonNull Type type,
@JsonProperty("label") @Nullable String label,
@JsonProperty("street") @Nullable String street,
@JsonProperty("poBox") @Nullable String poBox,
@JsonProperty("neighborhood") @Nullable String neighborhood,
@JsonProperty("city") @Nullable String city,
@JsonProperty("region") @Nullable String region,
@JsonProperty("postalCode") @Nullable String postalCode,
@JsonProperty("country") @Nullable String country)
{
this.type = type;
this.label = label;
this.street = street;
this.poBox = poBox;
this.neighborhood = neighborhood;
this.city = city;
this.region = region;
this.postalCode = postalCode;
this.country = country;
this.selected = true;
}
private PostalAddress(Parcel in) {
this(Type.valueOf(in.readString()),
in.readString(),
in.readString(),
in.readString(),
in.readString(),
in.readString(),
in.readString(),
in.readString(),
in.readString());
}
public @NonNull Type getType() {
return type;
}
public @Nullable String getLabel() {
return label;
}
public @Nullable String getStreet() {
return street;
}
public @Nullable String getPoBox() {
return poBox;
}
public @Nullable String getNeighborhood() {
return neighborhood;
}
public @Nullable String getCity() {
return city;
}
public @Nullable String getRegion() {
return region;
}
public @Nullable String getPostalCode() {
return postalCode;
}
public @Nullable String getCountry() {
return country;
}
@Override
public void setSelected(boolean selected) {
this.selected = selected;
}
@Override
public boolean isSelected() {
return selected;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(type.name());
dest.writeString(label);
dest.writeString(street);
dest.writeString(poBox);
dest.writeString(neighborhood);
dest.writeString(city);
dest.writeString(region);
dest.writeString(postalCode);
dest.writeString(country);
}
public static final Creator<PostalAddress> CREATOR = new Creator<PostalAddress>() {
@Override
public PostalAddress createFromParcel(Parcel in) {
return new PostalAddress(in);
}
@Override
public PostalAddress[] newArray(int size) {
return new PostalAddress[size];
}
};
@Override
public @NonNull String toString() {
StringBuilder builder = new StringBuilder();
if (!TextUtils.isEmpty(street)) {
builder.append(street).append('\n');
}
if (!TextUtils.isEmpty(poBox)) {
builder.append(poBox).append('\n');
}
if (!TextUtils.isEmpty(neighborhood)) {
builder.append(neighborhood).append('\n');
}
if (!TextUtils.isEmpty(city) && !TextUtils.isEmpty(region)) {
builder.append(city).append(", ").append(region);
} else if (!TextUtils.isEmpty(city)) {
builder.append(city).append(' ');
} else if (!TextUtils.isEmpty(region)) {
builder.append(region).append(' ');
}
if (!TextUtils.isEmpty(postalCode)) {
builder.append(postalCode);
}
if (!TextUtils.isEmpty(country)) {
builder.append('\n').append(country);
}
return builder.toString().trim();
}
public enum Type {
HOME, WORK, CUSTOM
}
}
public static class Avatar implements Selectable, Parcelable {
@JsonProperty
private final AttachmentId attachmentId;
@JsonProperty
private final boolean isProfile;
@JsonIgnore
private final Attachment attachment;
@JsonIgnore
private boolean selected;
public Avatar(@Nullable AttachmentId attachmentId, @Nullable Attachment attachment, boolean isProfile) {
this.attachmentId = attachmentId;
this.attachment = attachment;
this.isProfile = isProfile;
this.selected = true;
}
Avatar(@Nullable Uri attachmentUri, boolean isProfile) {
this(null, attachmentFromUri(attachmentUri), isProfile);
}
@JsonCreator
private Avatar(@JsonProperty("attachmentId") @Nullable AttachmentId attachmentId, @JsonProperty("isProfile") boolean isProfile) {
this(attachmentId, null, isProfile);
}
private Avatar(Parcel in) {
this((Uri) in.readParcelable(Uri.class.getClassLoader()), in.readByte() != 0);
}
public @Nullable AttachmentId getAttachmentId() {
return attachmentId;
}
public @Nullable Attachment getAttachment() {
return attachment;
}
public boolean isProfile() {
return isProfile;
}
@Override
public void setSelected(boolean selected) {
this.selected = selected;
}
@Override
public boolean isSelected() {
return selected;
}
@Override
public int describeContents() {
return 0;
}
private static Attachment attachmentFromUri(@Nullable Uri uri) {
if (uri == null) return null;
return new UriAttachment(uri, MediaTypes.IMAGE_JPEG.getValue(), AttachmentTransferProgress.TRANSFER_PROGRESS_DONE.getValue(), 0, null, false, false, null, null);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(attachment != null ? attachment.getDataUri() : null, flags);
dest.writeByte((byte) (isProfile ? 1 : 0));
}
public static final Creator<Avatar> CREATOR = new Creator<Avatar>() {
@Override
public Avatar createFromParcel(Parcel in) {
return new Avatar(in);
}
@Override
public Avatar[] newArray(int size) {
return new Avatar[size];
}
};
}
}

View File

@ -0,0 +1,6 @@
package org.session.libsession.messaging.sending_receiving.contacts;
public interface Selectable {
void setSelected(boolean selected);
boolean isSelected();
}

View File

@ -0,0 +1,78 @@
package org.session.libsession.messaging.sending_receiving.linkpreview;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId;
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
import org.session.libsession.utilities.JsonUtils;
import org.session.libsignal.libsignal.util.guava.Optional;
import java.io.IOException;
public class LinkPreview {
@JsonProperty
private final String url;
@JsonProperty
private final String title;
@JsonProperty
private final AttachmentId attachmentId;
@JsonIgnore
public Optional<Attachment> thumbnail;
public LinkPreview(@NonNull String url, @NonNull String title, @NonNull DatabaseAttachment thumbnail) {
this.url = url;
this.title = title;
this.thumbnail = Optional.of(thumbnail);
this.attachmentId = thumbnail.getAttachmentId();
}
public LinkPreview(@NonNull String url, @NonNull String title, @NonNull Optional<Attachment> thumbnail) {
this.url = url;
this.title = title;
this.thumbnail = thumbnail;
this.attachmentId = null;
}
public LinkPreview(@JsonProperty("url") @NonNull String url,
@JsonProperty("title") @NonNull String title,
@JsonProperty("attachmentId") @Nullable AttachmentId attachmentId)
{
this.url = url;
this.title = title;
this.attachmentId = attachmentId;
this.thumbnail = Optional.absent();
}
public String getUrl() {
return url;
}
public String getTitle() {
return title;
}
public Optional<Attachment> getThumbnail() {
return thumbnail;
}
public @Nullable AttachmentId getAttachmentId() {
return attachmentId;
}
public String serialize() throws IOException {
return JsonUtils.toJson(this);
}
public static LinkPreview deserialize(@NonNull String serialized) throws IOException {
return JsonUtils.fromJson(serialized, LinkPreview.class);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
package org.session.libsession.utilities;
import android.content.Context;
import java.util.concurrent.TimeUnit;
import org.session.libsession.R;
public class ExpirationUtil {
public static String getExpirationDisplayValue(Context context, int expirationTime) {
if (expirationTime <= 0) {
return context.getString(R.string.expiration_off);
} else if (expirationTime < TimeUnit.MINUTES.toSeconds(1)) {
return context.getResources().getQuantityString(R.plurals.expiration_seconds, expirationTime, expirationTime);
} else if (expirationTime < TimeUnit.HOURS.toSeconds(1)) {
int minutes = expirationTime / (int)TimeUnit.MINUTES.toSeconds(1);
return context.getResources().getQuantityString(R.plurals.expiration_minutes, minutes, minutes);
} else if (expirationTime < TimeUnit.DAYS.toSeconds(1)) {
int hours = expirationTime / (int)TimeUnit.HOURS.toSeconds(1);
return context.getResources().getQuantityString(R.plurals.expiration_hours, hours, hours);
} else if (expirationTime < TimeUnit.DAYS.toSeconds(7)) {
int days = expirationTime / (int)TimeUnit.DAYS.toSeconds(1);
return context.getResources().getQuantityString(R.plurals.expiration_days, days, days);
} else {
int weeks = expirationTime / (int)TimeUnit.DAYS.toSeconds(7);
return context.getResources().getQuantityString(R.plurals.expiration_weeks, weeks, weeks);
}
}
public static String getExpirationAbbreviatedDisplayValue(Context context, int expirationTime) {
if (expirationTime < TimeUnit.MINUTES.toSeconds(1)) {
return context.getResources().getString(R.string.expiration_seconds_abbreviated, expirationTime);
} else if (expirationTime < TimeUnit.HOURS.toSeconds(1)) {
int minutes = expirationTime / (int)TimeUnit.MINUTES.toSeconds(1);
return context.getResources().getString(R.string.expiration_minutes_abbreviated, minutes);
} else if (expirationTime < TimeUnit.DAYS.toSeconds(1)) {
int hours = expirationTime / (int)TimeUnit.HOURS.toSeconds(1);
return context.getResources().getString(R.string.expiration_hours_abbreviated, hours);
} else if (expirationTime < TimeUnit.DAYS.toSeconds(7)) {
int days = expirationTime / (int)TimeUnit.DAYS.toSeconds(1);
return context.getResources().getString(R.string.expiration_days_abbreviated, days);
} else {
int weeks = expirationTime / (int)TimeUnit.DAYS.toSeconds(7);
return context.getResources().getString(R.string.expiration_weeks_abbreviated, weeks);
}
}
}

View File

@ -0,0 +1,73 @@
package org.session.libsession.utilities;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
public class JsonUtils {
private static final ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
}
public static <T> T fromJson(byte[] serialized, Class<T> clazz) throws IOException {
return fromJson(new String(serialized), clazz);
}
public static <T> T fromJson(String serialized, Class<T> clazz) throws IOException {
return objectMapper.readValue(serialized, clazz);
}
public static <T> T fromJson(InputStream serialized, Class<T> clazz) throws IOException {
return objectMapper.readValue(serialized, clazz);
}
public static <T> T fromJson(Reader serialized, Class<T> clazz) throws IOException {
return objectMapper.readValue(serialized, clazz);
}
public static String toJson(Object object) throws IOException {
return objectMapper.writeValueAsString(object);
}
public static ObjectMapper getMapper() {
return objectMapper;
}
public static class SaneJSONObject {
private final JSONObject delegate;
public SaneJSONObject(JSONObject delegate) {
this.delegate = delegate;
}
public String getString(String name) throws JSONException {
if (delegate.isNull(name)) return null;
else return delegate.getString(name);
}
public long getLong(String name) throws JSONException {
return delegate.getLong(name);
}
public boolean isNull(String name) {
return delegate.isNull(name);
}
public int getInt(String name) throws JSONException {
return delegate.getInt(name);
}
}
}

View File

@ -0,0 +1,13 @@
package org.session.libsession.utilities
enum class MediaTypes(val value: String) {
IMAGE_PNG("image/png"),
IMAGE_JPEG("image/jpeg"),
IMAGE_WEBP("image/webp"),
IMAGE_GIF("image/gif"),
AUDIO_AAC("audio/aac"),
AUDIO_UNSPECIFIED("audio/*"),
VIDEO_UNSPECIFIED("video/*"),
VCARD("text/x-vcard"),
LONG_TEXT("text/x-signal-plain")
}

View File

@ -3,6 +3,7 @@ package org.session.libsession.utilities
import android.net.Uri
import android.os.Handler
import android.os.Looper
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
@ -71,4 +72,9 @@ object Util {
return a === b || a != null && a == b
}
@JvmStatic
fun hashCode(vararg objects: Any?): Int {
return Arrays.hashCode(objects)
}
}