mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-24 00:37:47 +00:00
Merge pull request #540 from oxen-io/open-group-invitations
Open Group Invitations
This commit is contained in:
commit
62e96705a4
@ -38,11 +38,12 @@ import androidx.loader.content.Loader;
|
||||
|
||||
|
||||
import org.session.libsession.messaging.messages.visible.LinkPreview;
|
||||
import org.session.libsession.messaging.messages.visible.OpenGroupInvitation;
|
||||
import org.session.libsession.messaging.messages.visible.Quote;
|
||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||
import org.session.libsession.messaging.open_groups.OpenGroup;
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||
import org.session.libsession.messaging.utilities.UpdateMessageData;
|
||||
import org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDeliveryStatus;
|
||||
import org.session.libsession.utilities.color.MaterialColor;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationItem;
|
||||
@ -448,7 +449,18 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||
Recipient recipient = messageRecord.getRecipient();
|
||||
VisibleMessage message = new VisibleMessage();
|
||||
message.setId(messageRecord.getId());
|
||||
message.setText(messageRecord.getBody());
|
||||
if (messageRecord.isOpenGroupInvitation()) {
|
||||
OpenGroupInvitation openGroupInvitation = new OpenGroupInvitation();
|
||||
UpdateMessageData updateMessageData = UpdateMessageData.Companion.fromJSON(messageRecord.getBody());
|
||||
if (updateMessageData.getKind() instanceof UpdateMessageData.Kind.OpenGroupInvitation) {
|
||||
UpdateMessageData.Kind.OpenGroupInvitation data = (UpdateMessageData.Kind.OpenGroupInvitation)updateMessageData.getKind();
|
||||
openGroupInvitation.setName(data.getGroupName());
|
||||
openGroupInvitation.setUrl(data.getGroupUrl());
|
||||
}
|
||||
message.setOpenGroupInvitation(openGroupInvitation);
|
||||
} else {
|
||||
message.setText(messageRecord.getBody());
|
||||
}
|
||||
message.setSentTimestamp(messageRecord.getTimestamp());
|
||||
if (recipient.isGroupRecipient()) {
|
||||
message.setGroupPublicKey(recipient.getAddress().toGroupString());
|
||||
|
@ -88,6 +88,7 @@ import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingSecureMediaMessage;
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
|
||||
import org.session.libsession.messaging.messages.visible.OpenGroupInvitation;
|
||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||
import org.session.libsession.messaging.open_groups.OpenGroup;
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupV2;
|
||||
@ -157,6 +158,7 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel;
|
||||
import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity;
|
||||
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
||||
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity;
|
||||
import org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
||||
@ -249,11 +251,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
private static final int PICK_GIF = 10;
|
||||
private static final int SMS_DEFAULT = 11;
|
||||
private static final int MEDIA_SENDER = 12;
|
||||
private static final int INVITE_CONTACTS = 124;
|
||||
|
||||
private GlideRequests glideRequests;
|
||||
protected ComposeText composeText;
|
||||
private AnimatingToggle buttonToggle;
|
||||
private ImageButton sendButton;
|
||||
private ImageButton sendButton;
|
||||
private ImageButton attachButton;
|
||||
private ProfilePictureView profilePictureView;
|
||||
private TextView titleTextView;
|
||||
@ -598,6 +601,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
});
|
||||
|
||||
break;
|
||||
case INVITE_CONTACTS:
|
||||
if (data.getExtras() == null || !data.hasExtra(SelectContactsActivity.Companion.getSelectedContactsKey())) return;
|
||||
String[] selectedContacts = data.getExtras().getStringArray(SelectContactsActivity.Companion.getSelectedContactsKey());
|
||||
sendOpenGroupInvitations(selectedContacts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -660,6 +668,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
} else if (isActiveGroup()) {
|
||||
inflater.inflate(R.menu.conversation_push_group_options, menu);
|
||||
}
|
||||
} else if (isOpenGroupOrRSSFeed) {
|
||||
inflater.inflate(R.menu.conversation_invite_open_group, menu);
|
||||
}
|
||||
|
||||
inflater.inflate(R.menu.conversation, menu);
|
||||
@ -768,6 +778,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
// case R.id.menu_conversation_settings: handleConversationSettings(); return true;
|
||||
case R.id.menu_expiring_messages_off:
|
||||
case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true;
|
||||
case R.id.menu_invite_to_open_group: handleInviteToOpenGroup(); return true;
|
||||
case android.R.id.home: handleReturnToConversationList(); return true;
|
||||
}
|
||||
|
||||
@ -1036,6 +1047,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void handleInviteToOpenGroup() {
|
||||
Intent intent = new Intent(this, SelectContactsActivity.class);
|
||||
startActivityForResult(intent, INVITE_CONTACTS);
|
||||
}
|
||||
|
||||
private void handleDistributionBroadcastEnabled(MenuItem item) {
|
||||
distributionType = DistributionTypes.BROADCAST;
|
||||
item.setChecked(true);
|
||||
@ -1843,6 +1859,23 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
sendComplete(allocatedThreadId);
|
||||
}
|
||||
|
||||
private void sendOpenGroupInvitations(String[] contactIDs) {
|
||||
final Context context = getApplicationContext();
|
||||
OpenGroupV2 openGroup = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadId);
|
||||
for (String contactID : contactIDs) {
|
||||
Recipient recipient = Recipient.from(context, Address.fromSerialized(contactID), true);
|
||||
VisibleMessage message = new VisibleMessage();
|
||||
message.setSentTimestamp(System.currentTimeMillis());
|
||||
OpenGroupInvitation openGroupInvitationMessage = new OpenGroupInvitation();
|
||||
openGroupInvitationMessage.setName(openGroup.getName());
|
||||
openGroupInvitationMessage.setUrl(openGroup.getJoinURL());
|
||||
message.setOpenGroupInvitation(openGroupInvitationMessage);
|
||||
OutgoingTextMessage outgoingTextMessage = OutgoingTextMessage.fromOpenGroupInvitation(openGroupInvitationMessage, recipient, message.getSentTimestamp());
|
||||
DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(-1, outgoingTextMessage, message.getSentTimestamp());
|
||||
MessageSender.send(message, recipient.getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateToggleButtonState() {
|
||||
if (inputPanel.isRecordingInLockedMode()) {
|
||||
buttonToggle.display(sendButton);
|
||||
|
@ -87,15 +87,17 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
Collections.synchronizedMap(new LRUCache<String, SoftReference<MessageRecord>>(MAX_CACHE_SIZE));
|
||||
private final SparseArray<String> positionToCacheRef = new SparseArray<>();
|
||||
|
||||
private static final int MESSAGE_TYPE_OUTGOING = 0;
|
||||
private static final int MESSAGE_TYPE_INCOMING = 1;
|
||||
private static final int MESSAGE_TYPE_UPDATE = 2;
|
||||
private static final int MESSAGE_TYPE_AUDIO_OUTGOING = 3;
|
||||
private static final int MESSAGE_TYPE_AUDIO_INCOMING = 4;
|
||||
private static final int MESSAGE_TYPE_THUMBNAIL_OUTGOING = 5;
|
||||
private static final int MESSAGE_TYPE_THUMBNAIL_INCOMING = 6;
|
||||
private static final int MESSAGE_TYPE_DOCUMENT_OUTGOING = 7;
|
||||
private static final int MESSAGE_TYPE_DOCUMENT_INCOMING = 8;
|
||||
private static final int MESSAGE_TYPE_OUTGOING = 0;
|
||||
private static final int MESSAGE_TYPE_INCOMING = 1;
|
||||
private static final int MESSAGE_TYPE_UPDATE = 2;
|
||||
private static final int MESSAGE_TYPE_AUDIO_OUTGOING = 3;
|
||||
private static final int MESSAGE_TYPE_AUDIO_INCOMING = 4;
|
||||
private static final int MESSAGE_TYPE_THUMBNAIL_OUTGOING = 5;
|
||||
private static final int MESSAGE_TYPE_THUMBNAIL_INCOMING = 6;
|
||||
private static final int MESSAGE_TYPE_DOCUMENT_OUTGOING = 7;
|
||||
private static final int MESSAGE_TYPE_DOCUMENT_INCOMING = 8;
|
||||
private static final int MESSAGE_TYPE_INVITATION_OUTGOING = 9;
|
||||
private static final int MESSAGE_TYPE_INVITATION_INCOMING = 10;
|
||||
|
||||
private final Set<MessageRecord> batchSelected = Collections.synchronizedSet(new HashSet<MessageRecord>());
|
||||
|
||||
@ -281,10 +283,12 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
case MESSAGE_TYPE_AUDIO_OUTGOING:
|
||||
case MESSAGE_TYPE_THUMBNAIL_OUTGOING:
|
||||
case MESSAGE_TYPE_DOCUMENT_OUTGOING:
|
||||
case MESSAGE_TYPE_INVITATION_OUTGOING:
|
||||
case MESSAGE_TYPE_OUTGOING: return R.layout.conversation_item_sent;
|
||||
case MESSAGE_TYPE_AUDIO_INCOMING:
|
||||
case MESSAGE_TYPE_THUMBNAIL_INCOMING:
|
||||
case MESSAGE_TYPE_DOCUMENT_INCOMING:
|
||||
case MESSAGE_TYPE_INVITATION_INCOMING:
|
||||
case MESSAGE_TYPE_INCOMING: return R.layout.conversation_item_received;
|
||||
case MESSAGE_TYPE_UPDATE: return R.layout.conversation_item_update;
|
||||
default: throw new IllegalArgumentException("unsupported item view type given to ConversationAdapter");
|
||||
@ -295,6 +299,9 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
public int getItemViewType(@NonNull MessageRecord messageRecord) {
|
||||
if (messageRecord.isUpdate()) {
|
||||
return MESSAGE_TYPE_UPDATE;
|
||||
} else if (messageRecord.isOpenGroupInvitation()) {
|
||||
if (messageRecord.isOutgoing()) return MESSAGE_TYPE_INVITATION_OUTGOING;
|
||||
else return MESSAGE_TYPE_INVITATION_INCOMING;
|
||||
} else if (hasAudio(messageRecord)) {
|
||||
if (messageRecord.isOutgoing()) return MESSAGE_TYPE_AUDIO_OUTGOING;
|
||||
else return MESSAGE_TYPE_AUDIO_INCOMING;
|
||||
|
@ -63,6 +63,7 @@ import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAt
|
||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||
import org.session.libsession.messaging.threads.recipients.RecipientModifiedListener;
|
||||
import org.session.libsession.messaging.utilities.UpdateMessageData;
|
||||
import org.session.libsession.utilities.GroupUtil;
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsession.utilities.ThemeUtil;
|
||||
@ -92,6 +93,7 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
||||
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
|
||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities;
|
||||
import org.thoughtcrime.securesms.loki.views.MessageAudioView;
|
||||
import org.thoughtcrime.securesms.loki.views.OpenGroupInvitationView;
|
||||
import org.thoughtcrime.securesms.loki.views.ProfilePictureView;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.ImageSlide;
|
||||
@ -156,6 +158,7 @@ public class ConversationItem extends LinearLayout
|
||||
private Stub<DocumentView> documentViewStub;
|
||||
private Stub<LinkPreviewView> linkPreviewStub;
|
||||
private Stub<StickerView> stickerStub;
|
||||
private Stub<OpenGroupInvitationView> openGroupInvitationViewStub;
|
||||
private @Nullable EventListener eventListener;
|
||||
|
||||
private int defaultBubbleColor;
|
||||
@ -203,6 +206,7 @@ public class ConversationItem extends LinearLayout
|
||||
this.documentViewStub = new Stub<>(findViewById(R.id.document_view_stub));
|
||||
this.linkPreviewStub = new Stub<>(findViewById(R.id.link_preview_stub));
|
||||
this.stickerStub = new Stub<>(findViewById(R.id.sticker_view_stub));
|
||||
this.openGroupInvitationViewStub = new Stub<>(findViewById(R.id.open_group_invitation_stub));
|
||||
this.groupSenderHolder = findViewById(R.id.group_sender_holder);
|
||||
this.quoteView = findViewById(R.id.quote_view);
|
||||
this.container = findViewById(R.id.container);
|
||||
@ -467,7 +471,9 @@ public class ConversationItem extends LinearLayout
|
||||
bodyText.setOverflowText(null);
|
||||
}
|
||||
|
||||
bodyText.setText(text);
|
||||
if (!messageRecord.isOpenGroupInvitation())
|
||||
bodyText.setText(text);
|
||||
|
||||
bodyText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
@ -528,6 +534,7 @@ public class ConversationItem extends LinearLayout
|
||||
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (openGroupInvitationViewStub.resolved()) openGroupInvitationViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0);
|
||||
@ -564,6 +571,7 @@ public class ConversationItem extends LinearLayout
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (openGroupInvitationViewStub.resolved()) openGroupInvitationViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
audioViewStub.get().setAudio(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide(), showControls);
|
||||
@ -580,6 +588,7 @@ public class ConversationItem extends LinearLayout
|
||||
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
|
||||
if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (openGroupInvitationViewStub.resolved()) openGroupInvitationViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
documentViewStub.get().setDocument(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getDocumentSlide(), showControls);
|
||||
@ -597,6 +606,7 @@ public class ConversationItem extends LinearLayout
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
if (openGroupInvitationViewStub.resolved()) openGroupInvitationViewStub.get().setVisibility(View.GONE);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
List<Slide> thumbnailSlides = ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlides();
|
||||
@ -619,6 +629,29 @@ public class ConversationItem extends LinearLayout
|
||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
footer.setVisibility(VISIBLE);
|
||||
} else if (messageRecord.isOpenGroupInvitation()) {
|
||||
openGroupInvitationViewStub.get().setVisibility(View.VISIBLE);
|
||||
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
|
||||
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
|
||||
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
|
||||
if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
|
||||
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
|
||||
|
||||
UpdateMessageData updateMessageData = UpdateMessageData.Companion.fromJSON(messageRecord.getBody());
|
||||
String name = null, url = null;
|
||||
if (updateMessageData.getKind() instanceof UpdateMessageData.Kind.OpenGroupInvitation) {
|
||||
UpdateMessageData.Kind.OpenGroupInvitation data = (UpdateMessageData.Kind.OpenGroupInvitation)updateMessageData.getKind();
|
||||
name = data.getGroupName();
|
||||
url = data.getGroupUrl();
|
||||
}
|
||||
|
||||
openGroupInvitationViewStub.get().setOpenGroup(name, url, messageRecord.isOutgoing());
|
||||
openGroupInvitationViewStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
|
||||
bodyText.setVisibility(View.GONE);
|
||||
|
||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
} else {
|
||||
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
|
||||
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
|
||||
|
@ -47,13 +47,16 @@ public interface MmsSmsColumns {
|
||||
BASE_PENDING_INSECURE_SMS_FALLBACK,
|
||||
OUTGOING_CALL_TYPE};
|
||||
|
||||
|
||||
// TODO: Clean unused keys
|
||||
|
||||
// Message attributes
|
||||
protected static final long MESSAGE_FORCE_SMS_BIT = 0x40;
|
||||
|
||||
// Key Exchange Information
|
||||
protected static final long KEY_EXCHANGE_MASK = 0xFF00;
|
||||
protected static final long KEY_EXCHANGE_BIT = 0x8000;
|
||||
protected static final long KEY_EXCHANGE_IDENTITY_VERIFIED_BIT = 0x4000;
|
||||
protected static final long KEY_EXCHANGE_IDENTITY_VERIFIED_BIT = 0x40000;
|
||||
protected static final long KEY_EXCHANGE_IDENTITY_DEFAULT_BIT = 0x2000;
|
||||
protected static final long KEY_EXCHANGE_CORRUPTED_BIT = 0x1000;
|
||||
protected static final long KEY_EXCHANGE_INVALID_VERSION_BIT = 0x800;
|
||||
@ -72,10 +75,13 @@ public interface MmsSmsColumns {
|
||||
protected static final long EXPIRATION_TIMER_UPDATE_BIT = 0x40000;
|
||||
protected static final long GROUP_UPDATE_MESSAGE_BIT = 0x80000;
|
||||
|
||||
// Data Extraction Information
|
||||
// Data Extraction Notification
|
||||
protected static final long MEDIA_SAVED_EXTRACTION_BIT = 0x01000;
|
||||
protected static final long SCREENSHOT_EXTRACTION_BIT = 0x02000;
|
||||
|
||||
// Open Group Invitation
|
||||
protected static final long OPEN_GROUP_INVITATION_BIT = 0x04000;
|
||||
|
||||
// Encrypted Storage Information XXX
|
||||
public static final long ENCRYPTION_MASK = 0xFF000000;
|
||||
// public static final long ENCRYPTION_SYMMETRIC_BIT = 0x80000000; Deprecated
|
||||
@ -210,6 +216,10 @@ public interface MmsSmsColumns {
|
||||
return (type & SCREENSHOT_EXTRACTION_BIT) != 0;
|
||||
}
|
||||
|
||||
public static boolean isOpenGroupInvitation(long type) {
|
||||
return (type & OPEN_GROUP_INVITATION_BIT) != 0;
|
||||
}
|
||||
|
||||
public static boolean isIncomingCall(long type) {
|
||||
return type == INCOMING_CALL_TYPE;
|
||||
}
|
||||
|
@ -350,7 +350,9 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
if (((IncomingGroupMessage)message).isUpdateMessage()) type |= Types.GROUP_UPDATE_MESSAGE_BIT;
|
||||
}
|
||||
|
||||
if (message.isPush()) type |= Types.PUSH_MESSAGE_BIT;
|
||||
if (message.isPush()) type |= Types.PUSH_MESSAGE_BIT;
|
||||
|
||||
if (message.isOpenGroupInvitation()) type |= Types.OPEN_GROUP_INVITATION_BIT;
|
||||
|
||||
Recipient recipient = Recipient.from(context, message.getSender(), true);
|
||||
|
||||
@ -443,8 +445,9 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
{
|
||||
long type = Types.BASE_SENDING_TYPE;
|
||||
|
||||
if (message.isSecureMessage()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT);
|
||||
if (forceSms) type |= Types.MESSAGE_FORCE_SMS_BIT;
|
||||
if (message.isSecureMessage()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT);
|
||||
if (forceSms) type |= Types.MESSAGE_FORCE_SMS_BIT;
|
||||
if (message.isOpenGroupInvitation()) type |= Types.OPEN_GROUP_INVITATION_BIT;
|
||||
|
||||
Address address = message.getRecipient().getAddress();
|
||||
Map<Address, Long> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(date);
|
||||
|
@ -24,7 +24,7 @@ import org.session.libsession.messaging.threads.Address
|
||||
import org.session.libsession.messaging.threads.Address.Companion.fromSerialized
|
||||
import org.session.libsession.messaging.threads.GroupRecord
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||
import org.session.libsession.messaging.utilities.ClosedGroupUpdateMessageData
|
||||
import org.session.libsession.messaging.utilities.UpdateMessageData
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsession.utilities.IdentityKeyUtil
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
@ -165,11 +165,15 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
mmsDatabase.endTransaction()
|
||||
} else {
|
||||
val smsDatabase = DatabaseFactory.getSmsDatabase(context)
|
||||
val isOpenGroupInvitation = (message.openGroupInvitation != null)
|
||||
|
||||
val insertResult = if (message.sender == getUserPublicKey()) {
|
||||
val textMessage = OutgoingTextMessage.from(message, targetRecipient)
|
||||
val textMessage = if (isOpenGroupInvitation) OutgoingTextMessage.fromOpenGroupInvitation(message.openGroupInvitation, targetRecipient, message.sentTimestamp)
|
||||
else OutgoingTextMessage.from(message, targetRecipient)
|
||||
smsDatabase.insertMessageOutbox(message.threadID ?: -1, textMessage, message.sentTimestamp!!)
|
||||
} else {
|
||||
val textMessage = IncomingTextMessage.from(message, senderAddress, group, targetRecipient.expireMessages * 1000L)
|
||||
val textMessage = if (isOpenGroupInvitation) IncomingTextMessage.fromOpenGroupInvitation(message.openGroupInvitation, senderAddress, message.sentTimestamp)
|
||||
else IncomingTextMessage.from(message, senderAddress, group, targetRecipient.expireMessages * 1000L)
|
||||
val encrypted = IncomingEncryptedMessage(textMessage, textMessage.messageBody)
|
||||
smsDatabase.insertMessageInbox(encrypted, message.receivedTimestamp ?: 0)
|
||||
}
|
||||
@ -475,7 +479,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
override fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection<String>, admins: Collection<String>, sentTimestamp: Long) {
|
||||
val group = SignalServiceGroup(type, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList())
|
||||
val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, sentTimestamp, "", Optional.of(group), 0, true)
|
||||
val updateData = ClosedGroupUpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON()
|
||||
val updateData = UpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON()
|
||||
val infoMessage = IncomingGroupMessage(m, groupID, updateData, true)
|
||||
val smsDB = DatabaseFactory.getSmsDatabase(context)
|
||||
smsDB.insertMessageInbox(infoMessage)
|
||||
@ -485,7 +489,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
val userPublicKey = getUserPublicKey()
|
||||
val recipient = Recipient.from(context, Address.fromSerialized(groupID), false)
|
||||
|
||||
val updateData = ClosedGroupUpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON() ?: ""
|
||||
val updateData = UpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON() ?: ""
|
||||
val infoMessage = OutgoingGroupMediaMessage(recipient, updateData, groupID, null, sentTimestamp, 0, true, null, listOf(), listOf())
|
||||
val mmsDB = DatabaseFactory.getMmsDatabase(context)
|
||||
val mmsSmsDB = DatabaseFactory.getMmsSmsDatabase(context)
|
||||
|
@ -145,6 +145,10 @@ public abstract class DisplayRecord {
|
||||
return isMediaSavedExtraction() || isScreenshotExtraction();
|
||||
}
|
||||
|
||||
public boolean isOpenGroupInvitation() {
|
||||
return MmsSmsColumns.Types.isOpenGroupInvitation(type);
|
||||
}
|
||||
|
||||
public boolean isCallLog() {
|
||||
return SmsDatabase.Types.isCallLog(type);
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ import android.text.style.StyleSpan;
|
||||
import network.loki.messenger.R;
|
||||
|
||||
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage;
|
||||
import org.session.libsession.messaging.utilities.ClosedGroupUpdateMessageBuilder;
|
||||
import org.session.libsession.messaging.utilities.ClosedGroupUpdateMessageData;
|
||||
import org.session.libsession.messaging.utilities.UpdateMessageBuilder;
|
||||
import org.session.libsession.messaging.utilities.UpdateMessageData;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.session.libsession.database.documents.IdentityKeyMismatch;
|
||||
@ -93,14 +93,14 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||
@Override
|
||||
public SpannableString getDisplayBody(@NonNull Context context) {
|
||||
if(isGroupUpdateMessage()) {
|
||||
ClosedGroupUpdateMessageData updateMessageData = ClosedGroupUpdateMessageData.Companion.fromJSON(getBody());
|
||||
return new SpannableString(ClosedGroupUpdateMessageBuilder.INSTANCE.buildGroupUpdateMessage(context, updateMessageData, getIndividualRecipient().getAddress().serialize(), isOutgoing()));
|
||||
UpdateMessageData updateMessageData = UpdateMessageData.Companion.fromJSON(getBody());
|
||||
return new SpannableString(UpdateMessageBuilder.INSTANCE.buildGroupUpdateMessage(context, updateMessageData, getIndividualRecipient().getAddress().serialize(), isOutgoing()));
|
||||
} else if (isExpirationTimerUpdate()) {
|
||||
int seconds = (int) (getExpiresIn() / 1000);
|
||||
return new SpannableString(ClosedGroupUpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, getIndividualRecipient().getAddress().serialize(), isOutgoing()));
|
||||
return new SpannableString(UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, getIndividualRecipient().getAddress().serialize(), isOutgoing()));
|
||||
} else if (isDataExtraction()) {
|
||||
if (isScreenshotExtraction()) return new SpannableString((ClosedGroupUpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT, getIndividualRecipient().getAddress().serialize())));
|
||||
else if (isMediaSavedExtraction()) return new SpannableString((ClosedGroupUpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED, getIndividualRecipient().getAddress().serialize())));
|
||||
if (isScreenshotExtraction()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT, getIndividualRecipient().getAddress().serialize())));
|
||||
else if (isMediaSavedExtraction()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED, getIndividualRecipient().getAddress().serialize())));
|
||||
}
|
||||
// TODO below lines are left here for compatibility with older group update messages, it can be deleted later on
|
||||
else if (isGroupUpdate() && isOutgoing()) {
|
||||
|
@ -77,6 +77,8 @@ public class ThreadRecord extends DisplayRecord {
|
||||
return emphasisAdded(context.getString(R.string.ThreadRecord_group_updated));
|
||||
} else if (isGroupQuit()) {
|
||||
return emphasisAdded(context.getString(R.string.ThreadRecord_left_the_group));
|
||||
} else if (isOpenGroupInvitation()) {
|
||||
return emphasisAdded(context.getString(R.string.ThreadRecord_open_group_invitation));
|
||||
} else if (isKeyExchange()) {
|
||||
return emphasisAdded(context.getString(R.string.ConversationListItem_key_exchange_message));
|
||||
} else if (SmsDatabase.Types.isFailedDecryptType(type)) {
|
||||
|
@ -150,9 +150,10 @@ class PublicChatManager(private val context: Context) {
|
||||
val groupId = "$server.$room"
|
||||
val threadId = GroupManager.getOpenGroupThreadID(groupId, context)
|
||||
val groupAddress = threadDB.getRecipientForThreadId(threadId)!!.address.serialize()
|
||||
GroupManager.deleteGroup(groupAddress, context)
|
||||
|
||||
Util.runOnMain { startPollersIfNeeded() }
|
||||
ThreadUtils.queue {
|
||||
GroupManager.deleteGroup(groupAddress, context) // Must be invoked on a background thread
|
||||
Util.runOnMain { startPollersIfNeeded() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshChatsAndPollers() {
|
||||
|
@ -0,0 +1,79 @@
|
||||
package org.thoughtcrime.securesms.loki.views
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.*
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsession.utilities.OpenGroupUrlParser
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
import org.thoughtcrime.securesms.groups.GroupManager
|
||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
|
||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
||||
|
||||
class OpenGroupInvitationView : FrameLayout {
|
||||
private val joinButton: ImageView
|
||||
private val openGroupIconContainer: RelativeLayout
|
||||
private val openGroupIconImageView: ImageView
|
||||
private val nameTextView: TextView
|
||||
private val urlTextView: TextView
|
||||
private var url: String = ""
|
||||
|
||||
constructor(context: Context): this(context, null)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr) {
|
||||
View.inflate(context, R.layout.open_group_invitation_view, this)
|
||||
joinButton = findViewById(R.id.join_open_group_button)
|
||||
openGroupIconContainer = findViewById(R.id.open_group_icon_image_view_container)
|
||||
openGroupIconImageView = findViewById(R.id.open_group_icon_image_view)
|
||||
nameTextView = findViewById(R.id.name_text_view)
|
||||
urlTextView = findViewById(R.id.url_text_view)
|
||||
joinButton.setOnClickListener { joinOpenGroup(url) }
|
||||
}
|
||||
|
||||
fun setOpenGroup(name: String, url: String, isOutgoing: Boolean = false) {
|
||||
nameTextView.text = name
|
||||
urlTextView.text = OpenGroupUrlParser.trimQueryParameter(url)
|
||||
this.url = url
|
||||
joinButton.visibility = if (isOutgoing) View.GONE else View.VISIBLE
|
||||
openGroupIconContainer.visibility = if (isOutgoing) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun joinOpenGroup(url: String) {
|
||||
val openGroup = OpenGroupUrlParser.parseUrl(url)
|
||||
val builder = AlertDialog.Builder(context)
|
||||
builder.setTitle(context.getString(R.string.ConversationActivity_join_open_group, nameTextView.text.toString()))
|
||||
builder.setCancelable(true)
|
||||
val message: String =
|
||||
context.getString(R.string.ConversationActivity_join_open_group_confirmation_message, nameTextView.text.toString())
|
||||
builder.setMessage(message)
|
||||
builder.setPositiveButton(R.string.yes) { dialog, _ ->
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
dialog.dismiss()
|
||||
val group = OpenGroupUtilities.addGroup(
|
||||
context,
|
||||
openGroup.server,
|
||||
openGroup.room,
|
||||
openGroup.serverPublicKey
|
||||
)
|
||||
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(context)
|
||||
} catch (e: Exception) {
|
||||
Log.e("Loki", "Failed to join open group.", e)
|
||||
Toast.makeText(context, R.string.activity_join_public_chat_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.setNegativeButton(R.string.no, null)
|
||||
builder.show()
|
||||
}
|
||||
}
|
@ -481,6 +481,8 @@ public class DefaultMessageNotifier implements MessageNotifier {
|
||||
String message = slideDeck.getBody() + ": " + record.getBody();
|
||||
int italicLength = message.length() - body.length();
|
||||
body = SpanUtil.italic(message, italicLength);
|
||||
} else if (record.isOpenGroupInvitation()) {
|
||||
body = SpanUtil.italic(context.getString(R.string.ThreadRecord_open_group_invitation));
|
||||
}
|
||||
|
||||
if (threadRecipients == null || !threadRecipients.isMuted()) {
|
||||
|
@ -140,6 +140,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout="@layout/conversation_item_received_link_preview" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/open_group_invitation_stub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout="@layout/conversation_item_received_open_group_invitation" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/audio_view_stub"
|
||||
android:layout="@layout/conversation_item_received_audio"
|
||||
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.loki.views.OpenGroupInvitationView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/open_group_invitation_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/>
|
@ -50,13 +50,13 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/large_spacing"
|
||||
android:layout_marginBottom="@dimen/medium_spacing"
|
||||
android:layout_marginEnd="@dimen/large_spacing"
|
||||
android:layout_marginBottom="@dimen/medium_spacing"
|
||||
android:visibility="gone"
|
||||
app:message_type="outgoing"
|
||||
app:quote_colorPrimary="@color/text"
|
||||
app:quote_colorSecondary="@color/text"
|
||||
tools:visibility="visible"/>
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/image_view_stub"
|
||||
@ -76,6 +76,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout="@layout/conversation_item_sent_link_preview" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/open_group_invitation_stub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout="@layout/conversation_item_sent_open_group_invitation" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/audio_view_stub"
|
||||
android:layout="@layout/conversation_item_sent_audio"
|
||||
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.loki.views.OpenGroupInvitationView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/open_group_invitation_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/>
|
74
app/src/main/res/layout/open_group_invitation_view.xml
Normal file
74
app/src/main/res/layout/open_group_invitation_view.xml
Normal file
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context="org.thoughtcrime.securesms.loki.views.OpenGroupInvitationView">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="@dimen/medium_spacing"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/join_open_group_button"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="@drawable/circle_tintable"
|
||||
android:backgroundTint="#00E076"
|
||||
android:contentDescription="@string/open_group_invitation_view__join_accessibility_description"
|
||||
app:srcCompat="@drawable/ic_add_white_original_24dp" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/open_group_icon_image_view_container"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="@drawable/circle_tintable"
|
||||
android:backgroundTint="#00E076">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/open_group_icon_image_view"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:contentDescription="@string/open_group_invitation_view__join_accessibility_description"
|
||||
android:src="@drawable/ic_globe" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/small_spacing"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:textSize="@dimen/large_font_size"
|
||||
android:maxLines="1"
|
||||
tools:text="Open group" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
android:maxLines="1"
|
||||
android:text="@string/open_group_invitation_view__open_group_invitation" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/url_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
android:maxLines="1"
|
||||
tools:text="http://1.1.1.1" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</merge>
|
8
app/src/main/res/menu/conversation_invite_open_group.xml
Normal file
8
app/src/main/res/menu/conversation_invite_open_group.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:title="@string/ConversationActivity_invite_to_open_group"
|
||||
android:id="@+id/menu_invite_to_open_group"
|
||||
android:icon="?menu_accept_icon" />
|
||||
|
||||
</menu>
|
@ -151,6 +151,10 @@
|
||||
<string name="ConversationActivity_attachment_exceeds_size_limits">La pièce jointe dépasse la limite de taille autorisée.</string>
|
||||
<string name="ConversationActivity_quick_camera_unavailable">L’appareil photo n’est pas disponible</string>
|
||||
<string name="ConversationActivity_unable_to_record_audio">Impossible d’enregistrer l’audio !</string>
|
||||
<string name="ConversationActivity_invite_to_open_group">Ajouter des membres</string>
|
||||
<string name="ConversationActivity_join_open_group">Rejoindre %s</string>
|
||||
<string name="ConversationActivity_join_open_group_confirmation_message">Êtes-vous sûr de vouloir rejoindre le group public <b>%s</b>?</string>
|
||||
|
||||
<string name="ConversationActivity_there_is_no_app_available_to_handle_this_link_on_your_device">Il n’y a aucune appli pour gérer ce lien sur votre appareil.</string>
|
||||
<string name="ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone">Pour envoyer des messages audio, autorisez Session à accéder à votre microphone.</string>
|
||||
<string name="ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages">Session exige l’autorisation Microphone afin d’envoyer des messages audio, mais elle a été refusée définitivement. Veuillez accéder au menu des paramètres des applis, sélectionner « Autorisations » et activer « Microphone ».</string>
|
||||
@ -599,6 +603,8 @@ Vous avez reçu un message d’échange de clés pour une version de protocole i
|
||||
<string name="ThreadRecord_your_safety_number_with_s_has_changed">Votre numéro de sécurité avec %s a changé</string>
|
||||
<string name="ThreadRecord_you_marked_verified">Vous avez marqué comme vérifié</string>
|
||||
<string name="ThreadRecord_you_marked_unverified">Vous avez marqué comme non vérifié</string>
|
||||
<string name="ThreadRecord_empty_message">Cette conversation est vide</string>
|
||||
<string name="ThreadRecord_open_group_invitation">Invitation à un groupe public</string>
|
||||
<!--UpdateApkReadyListener-->
|
||||
<string name="UpdateApkReadyListener_Signal_update">Mise à jour de Session</string>
|
||||
<string name="UpdateApkReadyListener_a_new_version_of_signal_is_available_tap_to_update">Une nouvelle version de Session est proposée. Touchez pour lancer la mise à jour</string>
|
||||
@ -782,6 +788,9 @@ Vous avez reçu un message d’échange de clés pour une version de protocole i
|
||||
<string name="audio_view__play_accessibility_description">Lire</string>
|
||||
<string name="audio_view__pause_accessibility_description">Mettre en pause</string>
|
||||
<string name="audio_view__download_accessibility_description">Télécharger</string>
|
||||
<!-- open_group_invitation_view -->
|
||||
<string name="open_group_invitation_view__join_accessibility_description">Rejoindre</string>
|
||||
<string name="open_group_invitation_view__open_group_invitation">Invitation à un groupe public</string>
|
||||
<!--QuoteView-->
|
||||
<string name="QuoteView_audio">Contenu audio</string>
|
||||
<string name="QuoteView_video">Vidéo</string>
|
||||
|
@ -175,6 +175,9 @@
|
||||
<string name="ConversationActivity_quick_camera_unavailable">Camera unavailable</string>
|
||||
<string name="ConversationActivity_unable_to_record_audio">Unable to record audio!</string>
|
||||
<string name="ConversationActivity_there_is_no_app_available_to_handle_this_link_on_your_device">There is no app available to handle this link on your device.</string>
|
||||
<string name="ConversationActivity_invite_to_open_group">Add members</string>
|
||||
<string name="ConversationActivity_join_open_group">Join %s</string>
|
||||
<string name="ConversationActivity_join_open_group_confirmation_message">Are you sure you want to join the <b>%s</b> open group?</string>
|
||||
|
||||
<string name="ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone">Session needs microphone access to send audio messages.</string>
|
||||
<string name="ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages">Session needs microphone access to send audio messages, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\".</string>
|
||||
@ -722,6 +725,7 @@
|
||||
<string name="ThreadRecord_you_marked_verified">You marked verified</string>
|
||||
<string name="ThreadRecord_you_marked_unverified">You marked unverified</string>
|
||||
<string name="ThreadRecord_empty_message">This conversation is empty</string>
|
||||
<string name="ThreadRecord_open_group_invitation">Open group invitation</string>
|
||||
|
||||
<!-- UpdateApkReadyListener -->
|
||||
<string name="UpdateApkReadyListener_Signal_update">Session update</string>
|
||||
@ -948,6 +952,10 @@
|
||||
<string name="audio_view__pause_accessibility_description">Pause</string>
|
||||
<string name="audio_view__download_accessibility_description">Download</string>
|
||||
|
||||
<!-- open_group_invitation_view -->
|
||||
<string name="open_group_invitation_view__join_accessibility_description">Join</string>
|
||||
<string name="open_group_invitation_view__open_group_invitation">Open group invitation</string>
|
||||
|
||||
<!-- QuoteView -->
|
||||
<string name="QuoteView_audio">Audio</string>
|
||||
<string name="QuoteView_video">Video</string>
|
||||
|
@ -5,8 +5,10 @@ import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.session.libsession.messaging.messages.visible.OpenGroupInvitation;
|
||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||
import org.session.libsession.messaging.threads.Address;
|
||||
import org.session.libsession.messaging.utilities.UpdateMessageData;
|
||||
import org.session.libsession.utilities.GroupUtil;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceGroup;
|
||||
@ -40,6 +42,8 @@ public class IncomingTextMessage implements Parcelable {
|
||||
private final long expiresInMillis;
|
||||
private final boolean unidentified;
|
||||
|
||||
private boolean isOpenGroupInvitation = false;
|
||||
|
||||
public IncomingTextMessage(Address sender, int senderDeviceId, long sentTimestampMillis,
|
||||
String encodedBody, Optional<SignalServiceGroup> group,
|
||||
long expiresInMillis, boolean unidentified)
|
||||
@ -94,6 +98,7 @@ public class IncomingTextMessage implements Parcelable {
|
||||
this.subscriptionId = base.getSubscriptionId();
|
||||
this.expiresInMillis = base.getExpiresIn();
|
||||
this.unidentified = base.isUnidentified();
|
||||
this.isOpenGroupInvitation= base.isOpenGroupInvitation();
|
||||
}
|
||||
|
||||
public static IncomingTextMessage from(VisibleMessage message,
|
||||
@ -104,6 +109,18 @@ public class IncomingTextMessage implements Parcelable {
|
||||
return new IncomingTextMessage(sender, 1, message.getSentTimestamp(), message.getText(), group, expiresInMillis, false);
|
||||
}
|
||||
|
||||
public static IncomingTextMessage fromOpenGroupInvitation(OpenGroupInvitation openGroupInvitation, Address sender, Long sentTimestamp)
|
||||
{
|
||||
String url = openGroupInvitation.getUrl();
|
||||
String name = openGroupInvitation.getName();
|
||||
if (url == null || name == null) { return null; }
|
||||
// FIXME: Doing toJSON() to get the body here is weird
|
||||
String body = UpdateMessageData.Companion.buildOpenGroupInvitation(url, name).toJSON();
|
||||
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(sender, 1, sentTimestamp, body, Optional.absent(), 0, false);
|
||||
incomingTextMessage.isOpenGroupInvitation = true;
|
||||
return incomingTextMessage;
|
||||
}
|
||||
|
||||
public int getSubscriptionId() {
|
||||
return subscriptionId;
|
||||
}
|
||||
@ -163,6 +180,9 @@ public class IncomingTextMessage implements Parcelable {
|
||||
public boolean isUnidentified() {
|
||||
return unidentified;
|
||||
}
|
||||
|
||||
public boolean isOpenGroupInvitation() { return isOpenGroupInvitation; }
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
|
@ -1,15 +1,17 @@
|
||||
package org.session.libsession.messaging.messages.signal;
|
||||
|
||||
import org.session.libsession.messaging.messages.visible.OpenGroupInvitation;
|
||||
import org.session.libsession.messaging.messages.visible.VisibleMessage;
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
||||
import org.session.libsession.messaging.utilities.UpdateMessageData;
|
||||
|
||||
public class OutgoingTextMessage {
|
||||
|
||||
private final Recipient recipient;
|
||||
private final String message;
|
||||
private final int subscriptionId;
|
||||
private final long expiresIn;
|
||||
private final long sentTimestampMillis;
|
||||
private boolean isOpenGroupInvitation = false;
|
||||
|
||||
public OutgoingTextMessage(Recipient recipient, String message, long expiresIn, int subscriptionId, long sentTimestampMillis) {
|
||||
this.recipient = recipient;
|
||||
@ -23,6 +25,17 @@ public class OutgoingTextMessage {
|
||||
return new OutgoingTextMessage(recipient, message.getText(), recipient.getExpireMessages() * 1000, -1, message.getSentTimestamp());
|
||||
}
|
||||
|
||||
public static OutgoingTextMessage fromOpenGroupInvitation(OpenGroupInvitation openGroupInvitation, Recipient recipient, Long sentTimestamp) {
|
||||
String url = openGroupInvitation.getUrl();
|
||||
String name = openGroupInvitation.getName();
|
||||
if (url == null || name == null) { return null; }
|
||||
// FIXME: Doing toJSON() to get the body here is weird
|
||||
String body = UpdateMessageData.Companion.buildOpenGroupInvitation(url, name).toJSON();
|
||||
OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipient, body, 0, -1, sentTimestamp);
|
||||
outgoingTextMessage.isOpenGroupInvitation = true;
|
||||
return outgoingTextMessage;
|
||||
}
|
||||
|
||||
public long getExpiresIn() {
|
||||
return expiresIn;
|
||||
}
|
||||
@ -46,4 +59,6 @@ public class OutgoingTextMessage {
|
||||
public boolean isSecureMessage() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isOpenGroupInvitation() { return isOpenGroupInvitation; }
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
package org.session.libsession.messaging.messages.visible
|
||||
|
||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
|
||||
class OpenGroupInvitation() {
|
||||
var url: String? = null
|
||||
var name: String? = null
|
||||
|
||||
fun isValid(): Boolean {
|
||||
return (url != null && name != null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "OpenGroupInvitation"
|
||||
|
||||
fun fromProto(proto: SignalServiceProtos.DataMessage.OpenGroupInvitation): OpenGroupInvitation {
|
||||
return OpenGroupInvitation(proto.url, proto.name)
|
||||
}
|
||||
}
|
||||
|
||||
constructor(url: String?, serverName: String?): this() {
|
||||
this.url = url
|
||||
this.name = serverName
|
||||
}
|
||||
|
||||
fun toProto(): SignalServiceProtos.DataMessage.OpenGroupInvitation? {
|
||||
val openGroupInvitationProto = SignalServiceProtos.DataMessage.OpenGroupInvitation.newBuilder()
|
||||
openGroupInvitationProto.url = url
|
||||
openGroupInvitationProto.name = name
|
||||
return try {
|
||||
openGroupInvitationProto.build()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Couldn't construct open group invitation proto from: $this.")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ class VisibleMessage : Message() {
|
||||
var quote: Quote? = null
|
||||
var linkPreview: LinkPreview? = null
|
||||
var profile: Profile? = null
|
||||
var openGroupInvitation: OpenGroupInvitation? = null
|
||||
|
||||
override val isSelfSendValid: Boolean = true
|
||||
|
||||
@ -29,6 +30,7 @@ class VisibleMessage : Message() {
|
||||
override fun isValid(): Boolean {
|
||||
if (!super.isValid()) return false
|
||||
if (attachmentIDs.isNotEmpty()) return true
|
||||
if (openGroupInvitation != null) return true
|
||||
val text = text?.trim() ?: return false
|
||||
if (text.isNotEmpty()) return true
|
||||
return false
|
||||
@ -55,7 +57,12 @@ class VisibleMessage : Message() {
|
||||
val linkPreview = LinkPreview.fromProto(linkPreviewProto)
|
||||
result.linkPreview = linkPreview
|
||||
}
|
||||
// TODO: Contact
|
||||
val openGroupInvitationProto = if (dataMessage.hasOpenGroupInvitation()) dataMessage.openGroupInvitation else null
|
||||
if (openGroupInvitationProto != null) {
|
||||
val openGroupInvitation = OpenGroupInvitation.fromProto(openGroupInvitationProto)
|
||||
result.openGroupInvitation = openGroupInvitation
|
||||
}
|
||||
// TODO Contact
|
||||
val profile = Profile.fromProto(dataMessage)
|
||||
if (profile != null) { result.profile = profile }
|
||||
return result
|
||||
@ -66,7 +73,7 @@ class VisibleMessage : Message() {
|
||||
val proto = SignalServiceProtos.Content.newBuilder()
|
||||
val dataMessage: SignalServiceProtos.DataMessage.Builder
|
||||
// Profile
|
||||
val profileProto = profile?.let { it.toProto() }
|
||||
val profileProto = profile?.toProto()
|
||||
if (profileProto != null) {
|
||||
dataMessage = profileProto.toBuilder()
|
||||
} else {
|
||||
@ -75,15 +82,20 @@ class VisibleMessage : Message() {
|
||||
// Text
|
||||
if (text != null) { dataMessage.body = text }
|
||||
// Quote
|
||||
val quoteProto = quote?.let { it.toProto() }
|
||||
val quoteProto = quote?.toProto()
|
||||
if (quoteProto != null) {
|
||||
dataMessage.quote = quoteProto
|
||||
}
|
||||
// Link preview
|
||||
val linkPreviewProto = linkPreview?.let { it.toProto() }
|
||||
val linkPreviewProto = linkPreview?.toProto()
|
||||
if (linkPreviewProto != null) {
|
||||
dataMessage.addAllPreview(listOf(linkPreviewProto))
|
||||
}
|
||||
// Open group invitation
|
||||
val openGroupInvitationProto = openGroupInvitation?.toProto()
|
||||
if (openGroupInvitationProto != null) {
|
||||
dataMessage.openGroupInvitation = openGroupInvitationProto
|
||||
}
|
||||
// Attachments
|
||||
val database = MessagingModuleConfiguration.shared.messageDataProvider
|
||||
val attachments = attachmentIDs.mapNotNull { database.getSignalAttachmentPointer(it) }
|
||||
|
@ -7,6 +7,7 @@ import org.session.libsession.messaging.jobs.JobQueue
|
||||
import org.session.libsession.messaging.messages.Message
|
||||
import org.session.libsession.messaging.messages.control.*
|
||||
import org.session.libsession.messaging.messages.visible.Attachment
|
||||
import org.session.libsession.messaging.messages.visible.OpenGroupInvitation
|
||||
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment
|
||||
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage
|
||||
@ -52,6 +53,7 @@ fun MessageReceiver.handle(message: Message, proto: SignalServiceProtos.Content,
|
||||
}
|
||||
}
|
||||
|
||||
// region Control Messages
|
||||
private fun MessageReceiver.handleReadReceipt(message: ReadReceipt) {
|
||||
val context = MessagingModuleConfiguration.shared.context
|
||||
SSKEnvironment.shared.readReceiptManager.processReadReceipts(context, message.sender!!, message.timestamps!!, message.receivedTimestamp!!)
|
||||
@ -140,7 +142,9 @@ private fun handleConfigurationMessage(message: ConfigurationMessage) {
|
||||
}
|
||||
storage.addContacts(message.contacts)
|
||||
}
|
||||
//endregion
|
||||
|
||||
// region Visible Messages
|
||||
fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalServiceProtos.Content, openGroupID: String?) {
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
val context = MessagingModuleConfiguration.shared.context
|
||||
@ -233,7 +237,9 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS
|
||||
//Notify the user if needed
|
||||
SSKEnvironment.shared.notificationManager.updateNotification(context, threadID)
|
||||
}
|
||||
//endregion
|
||||
|
||||
// region Closed Groups
|
||||
private fun MessageReceiver.handleClosedGroupControlMessage(message: ClosedGroupControlMessage) {
|
||||
when (message.kind!!) {
|
||||
is ClosedGroupControlMessage.Kind.New -> handleNewClosedGroup(message)
|
||||
@ -557,4 +563,5 @@ fun MessageReceiver.disableLocalGroupAndUnsubscribe(groupPublicKey: String, grou
|
||||
storage.removeMember(groupID, Address.fromSerialized(userPublicKey))
|
||||
// Notify the PN server
|
||||
PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
@ -6,9 +6,9 @@ import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage
|
||||
import org.session.libsession.utilities.ExpirationUtil
|
||||
|
||||
object ClosedGroupUpdateMessageBuilder {
|
||||
object UpdateMessageBuilder {
|
||||
|
||||
fun buildGroupUpdateMessage(context: Context, updateMessageData: ClosedGroupUpdateMessageData, sender: String? = null, isOutgoing: Boolean = false): String {
|
||||
fun buildGroupUpdateMessage(context: Context, updateMessageData: UpdateMessageData, sender: String? = null, isOutgoing: Boolean = false): String {
|
||||
var message = ""
|
||||
val updateData = updateMessageData.kind ?: return message
|
||||
if (!isOutgoing && sender == null) return message
|
||||
@ -17,21 +17,21 @@ object ClosedGroupUpdateMessageBuilder {
|
||||
} else { context.getString(R.string.MessageRecord_you) }
|
||||
|
||||
when (updateData) {
|
||||
is ClosedGroupUpdateMessageData.Kind.GroupCreation -> {
|
||||
is UpdateMessageData.Kind.GroupCreation -> {
|
||||
message = if (isOutgoing) {
|
||||
context.getString(R.string.MessageRecord_you_created_a_new_group)
|
||||
} else {
|
||||
context.getString(R.string.MessageRecord_s_added_you_to_the_group, senderName)
|
||||
}
|
||||
}
|
||||
is ClosedGroupUpdateMessageData.Kind.GroupNameChange -> {
|
||||
is UpdateMessageData.Kind.GroupNameChange -> {
|
||||
message = if (isOutgoing) {
|
||||
context.getString(R.string.MessageRecord_you_renamed_the_group_to_s, updateData.name)
|
||||
} else {
|
||||
context.getString(R.string.MessageRecord_s_renamed_the_group_to_s, senderName, updateData.name)
|
||||
}
|
||||
}
|
||||
is ClosedGroupUpdateMessageData.Kind.GroupMemberAdded -> {
|
||||
is UpdateMessageData.Kind.GroupMemberAdded -> {
|
||||
val members = updateData.updatedMembers.joinToString(", ") {
|
||||
MessagingModuleConfiguration.shared.storage.getDisplayNameForRecipient(it) ?: it
|
||||
}
|
||||
@ -41,7 +41,7 @@ object ClosedGroupUpdateMessageBuilder {
|
||||
context.getString(R.string.MessageRecord_s_added_s_to_the_group, senderName, members)
|
||||
}
|
||||
}
|
||||
is ClosedGroupUpdateMessageData.Kind.GroupMemberRemoved -> {
|
||||
is UpdateMessageData.Kind.GroupMemberRemoved -> {
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
val userPublicKey = storage.getUserPublicKey()!!
|
||||
// 1st case: you are part of the removed members
|
||||
@ -63,7 +63,7 @@ object ClosedGroupUpdateMessageBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
is ClosedGroupUpdateMessageData.Kind.GroupMemberLeft -> {
|
||||
is UpdateMessageData.Kind.GroupMemberLeft -> {
|
||||
message = if (isOutgoing) {
|
||||
context.getString(R.string.MessageRecord_left_group)
|
||||
} else {
|
@ -9,7 +9,7 @@ import org.session.libsignal.utilities.logging.Log
|
||||
import java.util.*
|
||||
|
||||
// class used to save update messages details
|
||||
class ClosedGroupUpdateMessageData () {
|
||||
class UpdateMessageData () {
|
||||
|
||||
var kind: Kind? = null
|
||||
|
||||
@ -20,7 +20,8 @@ class ClosedGroupUpdateMessageData () {
|
||||
JsonSubTypes.Type(Kind.GroupNameChange::class, name = "GroupNameChange"),
|
||||
JsonSubTypes.Type(Kind.GroupMemberAdded::class, name = "GroupMemberAdded"),
|
||||
JsonSubTypes.Type(Kind.GroupMemberRemoved::class, name = "GroupMemberRemoved"),
|
||||
JsonSubTypes.Type(Kind.GroupMemberLeft::class, name = "GroupMemberLeft")
|
||||
JsonSubTypes.Type(Kind.GroupMemberLeft::class, name = "GroupMemberLeft"),
|
||||
JsonSubTypes.Type(Kind.OpenGroupInvitation::class, name = "OpenGroupInvitation")
|
||||
)
|
||||
sealed class Kind() {
|
||||
class GroupCreation(): Kind()
|
||||
@ -34,6 +35,9 @@ class ClosedGroupUpdateMessageData () {
|
||||
constructor(): this(Collections.emptyList())
|
||||
}
|
||||
class GroupMemberLeft(): Kind()
|
||||
class OpenGroupInvitation(val groupUrl: String, val groupName: String): Kind() {
|
||||
constructor(): this("", "")
|
||||
}
|
||||
}
|
||||
|
||||
constructor(kind: Kind): this() {
|
||||
@ -41,22 +45,26 @@ class ClosedGroupUpdateMessageData () {
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = ClosedGroupUpdateMessageData::class.simpleName
|
||||
val TAG = UpdateMessageData::class.simpleName
|
||||
|
||||
fun buildGroupUpdate(type: SignalServiceGroup.Type, name: String, members: Collection<String>): ClosedGroupUpdateMessageData? {
|
||||
fun buildGroupUpdate(type: SignalServiceGroup.Type, name: String, members: Collection<String>): UpdateMessageData? {
|
||||
return when(type) {
|
||||
SignalServiceGroup.Type.CREATION -> ClosedGroupUpdateMessageData(Kind.GroupCreation())
|
||||
SignalServiceGroup.Type.NAME_CHANGE -> ClosedGroupUpdateMessageData(Kind.GroupNameChange(name))
|
||||
SignalServiceGroup.Type.MEMBER_ADDED -> ClosedGroupUpdateMessageData(Kind.GroupMemberAdded(members))
|
||||
SignalServiceGroup.Type.MEMBER_REMOVED -> ClosedGroupUpdateMessageData(Kind.GroupMemberRemoved(members))
|
||||
SignalServiceGroup.Type.QUIT -> ClosedGroupUpdateMessageData(Kind.GroupMemberLeft())
|
||||
SignalServiceGroup.Type.CREATION -> UpdateMessageData(Kind.GroupCreation())
|
||||
SignalServiceGroup.Type.NAME_CHANGE -> UpdateMessageData(Kind.GroupNameChange(name))
|
||||
SignalServiceGroup.Type.MEMBER_ADDED -> UpdateMessageData(Kind.GroupMemberAdded(members))
|
||||
SignalServiceGroup.Type.MEMBER_REMOVED -> UpdateMessageData(Kind.GroupMemberRemoved(members))
|
||||
SignalServiceGroup.Type.QUIT -> UpdateMessageData(Kind.GroupMemberLeft())
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun fromJSON(json: String): ClosedGroupUpdateMessageData? {
|
||||
fun buildOpenGroupInvitation(url: String, name: String): UpdateMessageData {
|
||||
return UpdateMessageData(Kind.OpenGroupInvitation(url, name))
|
||||
}
|
||||
|
||||
fun fromJSON(json: String): UpdateMessageData? {
|
||||
return try {
|
||||
JsonUtil.fromJson(json, ClosedGroupUpdateMessageData::class.java)
|
||||
JsonUtil.fromJson(json, UpdateMessageData::class.java)
|
||||
} catch (e: JsonParseException) {
|
||||
Log.e(TAG, "${e.message}")
|
||||
null
|
@ -0,0 +1,36 @@
|
||||
package org.session.libsession.utilities
|
||||
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
object OpenGroupUrlParser {
|
||||
|
||||
sealed class Error(val description: String) : Exception(description) {
|
||||
object MalformedURL : Error("Malformed URL.")
|
||||
object NoRoom : Error("No room specified in the URL.")
|
||||
object NoPublicKey : Error("No public key specified in the URL.")
|
||||
object InvalidPublicKey : Error("Invalid public key provided.")
|
||||
}
|
||||
|
||||
private const val suffix = "/"
|
||||
private const val queryPrefix = "public_key"
|
||||
|
||||
fun parseUrl(string: String): V2OpenGroupInfo {
|
||||
// URL has to start with 'http://'
|
||||
val urlWithPrefix = if (!string.startsWith("http")) "http://$string" else string
|
||||
// If the URL is malformed, throw an exception
|
||||
val url = HttpUrl.parse(urlWithPrefix) ?: throw Error.MalformedURL
|
||||
// Parse components
|
||||
val server = HttpUrl.Builder().scheme(url.scheme()).host(url.host()).port(url.port()).build().toString().removeSuffix(suffix)
|
||||
val room = url.pathSegments().firstOrNull { !it.isNullOrEmpty() } ?: throw Error.NoRoom
|
||||
val publicKey = url.queryParameter(queryPrefix) ?: throw Error.NoPublicKey
|
||||
if (publicKey.length != 64) throw Error.InvalidPublicKey
|
||||
// Return
|
||||
return V2OpenGroupInfo(server,room,publicKey)
|
||||
}
|
||||
|
||||
fun trimQueryParameter(string: String): String {
|
||||
return string.substringBefore("?$queryPrefix")
|
||||
}
|
||||
}
|
||||
|
||||
class V2OpenGroupInfo(val server: String, val room: String, val serverPublicKey: String)
|
@ -0,0 +1,87 @@
|
||||
package org.session.libsession.utilities
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.Assert.*
|
||||
|
||||
class OpenGroupUrlParserTest {
|
||||
|
||||
@Test
|
||||
fun parseUrlTest() {
|
||||
val inputUrl = "https://sessionopengroup.co/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
|
||||
|
||||
val expectedHost = "https://sessionopengroup.co"
|
||||
val expectedRoom = "main"
|
||||
val expectedPublicKey = "658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
|
||||
|
||||
val result = OpenGroupUrlParser.parseUrl(inputUrl)
|
||||
assertEquals(expectedHost, result.server)
|
||||
assertEquals(expectedRoom, result.room)
|
||||
assertEquals(expectedPublicKey, result.serverPublicKey)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseUrlNoHttpTest() {
|
||||
val inputUrl = "sessionopengroup.co/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
|
||||
|
||||
val expectedHost = "http://sessionopengroup.co"
|
||||
val expectedRoom = "main"
|
||||
val expectedPublicKey = "658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
|
||||
|
||||
val result = OpenGroupUrlParser.parseUrl(inputUrl)
|
||||
assertEquals(expectedHost, result.server)
|
||||
assertEquals(expectedRoom, result.room)
|
||||
assertEquals(expectedPublicKey, result.serverPublicKey)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseUrlWithIpTest() {
|
||||
val inputUrl = "https://143.198.213.255:80/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
|
||||
|
||||
val expectedHost = "https://143.198.213.255:80"
|
||||
val expectedRoom = "main"
|
||||
val expectedPublicKey = "658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
|
||||
|
||||
val result = OpenGroupUrlParser.parseUrl(inputUrl)
|
||||
assertEquals(expectedHost, result.server)
|
||||
assertEquals(expectedRoom, result.room)
|
||||
assertEquals(expectedPublicKey, result.serverPublicKey)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseUrlWithIpAndNoHttpTest() {
|
||||
val inputUrl = "143.198.213.255/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
|
||||
|
||||
val expectedHost = "http://143.198.213.255"
|
||||
val expectedRoom = "main"
|
||||
val expectedPublicKey = "658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
|
||||
|
||||
val result = OpenGroupUrlParser.parseUrl(inputUrl)
|
||||
assertEquals(expectedHost, result.server)
|
||||
assertEquals(expectedRoom, result.room)
|
||||
assertEquals(expectedPublicKey, result.serverPublicKey)
|
||||
}
|
||||
|
||||
@Test(expected = OpenGroupUrlParser.Error.MalformedURL::class)
|
||||
fun parseUrlMalformedUrlTest() {
|
||||
val inputUrl = "file:sessionopengroup.co/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
|
||||
OpenGroupUrlParser.parseUrl(inputUrl)
|
||||
}
|
||||
|
||||
@Test(expected = OpenGroupUrlParser.Error.NoRoom::class)
|
||||
fun parseUrlNoRoomSpecifiedTest() {
|
||||
val inputUrl = "https://sessionopengroup.comain?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
|
||||
OpenGroupUrlParser.parseUrl(inputUrl)
|
||||
}
|
||||
|
||||
@Test(expected = OpenGroupUrlParser.Error.NoPublicKey::class)
|
||||
fun parseUrlNoPublicKeySpecifiedTest() {
|
||||
val inputUrl = "https://sessionopengroup.co/main"
|
||||
OpenGroupUrlParser.parseUrl(inputUrl)
|
||||
}
|
||||
|
||||
@Test(expected = OpenGroupUrlParser.Error.InvalidPublicKey::class)
|
||||
fun parseUrlInvalidPublicKeyProviedTest() {
|
||||
val inputUrl = "https://sessionopengroup.co/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adff"
|
||||
OpenGroupUrlParser.parseUrl(inputUrl)
|
||||
}
|
||||
}
|
@ -40,7 +40,7 @@ message Content {
|
||||
optional ReceiptMessage receiptMessage = 5;
|
||||
optional TypingMessage typingMessage = 6;
|
||||
optional ConfigurationMessage configurationMessage = 7;
|
||||
optional DataExtractionNotification dataExtractionNotification = 82;
|
||||
optional DataExtractionNotification dataExtractionNotification = 8;
|
||||
}
|
||||
|
||||
message KeyPair {
|
||||
@ -102,6 +102,13 @@ message DataMessage {
|
||||
optional string profilePicture = 2;
|
||||
}
|
||||
|
||||
message OpenGroupInvitation {
|
||||
// @required
|
||||
required string url = 1;
|
||||
// @required
|
||||
required string name = 3;
|
||||
}
|
||||
|
||||
message ClosedGroupControlMessage {
|
||||
|
||||
enum Type {
|
||||
@ -140,6 +147,7 @@ message DataMessage {
|
||||
optional Quote quote = 8;
|
||||
repeated Preview preview = 10;
|
||||
optional LokiProfile profile = 101;
|
||||
optional OpenGroupInvitation openGroupInvitation = 102;
|
||||
optional ClosedGroupControlMessage closedGroupControlMessage = 104;
|
||||
optional string syncTarget = 105;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user