Merge branch 'fix_profile_nulls' into open_groups_V2

# Conflicts:
#	app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java
#	app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java
#	libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt
This commit is contained in:
jubb
2021-04-28 13:53:50 +10:00
38 changed files with 687 additions and 382 deletions

View File

@@ -52,6 +52,8 @@ import androidx.viewpager.widget.ViewPager;
import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager;
import com.google.android.material.tabs.TabLayout;
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
import org.session.libsession.messaging.sending_receiving.MessageSender;
import org.session.libsession.messaging.threads.Address;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.MediaDatabase;
@@ -351,6 +353,12 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
saveTask.executeOnExecutor(THREAD_POOL_EXECUTOR,
attachments.toArray(new SaveAttachmentTask.Attachment[attachments.size()]));
actionMode.finish();
// Sending a Data extraction notification (for incoming attachments only)
boolean containsIncoming = mediaRecords.parallelStream().anyMatch(m -> !m.isOutgoing());
if (containsIncoming) {
//TODO uncomment line below when Data extraction will be activated
//sendMediaSavedNotificationIfNeeded();
}
}
}.execute();
})
@@ -358,6 +366,16 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
}, mediaRecords.size());
}
/**
* Send a MediaSaved notification to the recipient
*/
private void sendMediaSavedNotificationIfNeeded() {
// we don't send media saved notification for groups
if (recipient.isGroupRecipient()) return;
DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis()));
MessageSender.send(message, recipient.getAddress());
}
@SuppressLint("StaticFieldLeak")
private void handleDeleteMedia(@NonNull Collection<MediaDatabase.MediaRecord> mediaRecords) {
int recordCount = mediaRecords.size();

View File

@@ -39,7 +39,8 @@ import android.view.Window;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
import org.session.libsession.messaging.sending_receiving.MessageSender;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
@@ -52,7 +53,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
import org.session.libsession.messaging.threads.Address;
import org.session.libsession.messaging.threads.recipients.Recipient;
@@ -352,11 +352,26 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
saveTask.executeOnExecutor(
AsyncTask.THREAD_POOL_EXECUTOR,
new Attachment(mediaItem.uri, mediaItem.type, saveDate, null));
// Sending a Data extraction notification (for incoming attachments only)
if(!mediaItem.outgoing) {
//TODO uncomment line below when Data extraction will be activated
//sendMediaSavedNotificationIfNeeded();
}
})
.execute();
});
}
/**
* Send a MediaSaved notification to the recipient
*/
private void sendMediaSavedNotificationIfNeeded() {
// we don't send media saved notification for groups
if (conversationRecipient.isGroupRecipient()) return;
DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis()));
MessageSender.send(message, conversationRecipient.getAddress());
}
@SuppressLint("StaticFieldLeak")
private void deleteMedia() {
MediaItem mediaItem = getCurrentMediaItem();

View File

@@ -177,6 +177,8 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.MmsException;
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
import org.session.libsession.messaging.messages.signal.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.mms.QuoteId;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
@@ -186,6 +188,9 @@ import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.search.model.MessageResult;
import org.session.libsession.messaging.sending_receiving.MessageSender;
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
@@ -803,15 +808,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient, expirationTime);
ExpirationTimerUpdate message = new ExpirationTimerUpdate(null, expirationTime);
ExpirationTimerUpdate message = new ExpirationTimerUpdate(expirationTime);
message.setRecipient(recipient.getAddress().serialize()); // we need the recipient in ExpiringMessageManager.insertOutgoingExpirationTimerMessage
message.setSentTimestamp(System.currentTimeMillis());
OutgoingExpirationUpdateMessage outgoingMessage = OutgoingExpirationUpdateMessage.from(message, recipient);
try {
message.setId(DatabaseFactory.getMmsDatabase(ConversationActivity.this).insertMessageOutbox(outgoingMessage, getAllocatedThreadId(ConversationActivity.this), false, null));
MessageSender.send(message, recipient.getAddress());
} catch (MmsException e) {
Log.w(TAG, e);
}
ExpiringMessageManager expiringMessageManager = ApplicationContext.getInstance(getApplicationContext()).getExpiringMessageManager();
expiringMessageManager.setExpirationTimer(message);
MessageSender.send(message, recipient.getAddress());
return null;
}

View File

@@ -59,6 +59,7 @@ import com.annimon.stream.Stream;
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
import org.session.libsession.messaging.messages.visible.Quote;
import org.session.libsession.messaging.messages.visible.VisibleMessage;
import org.session.libsession.messaging.opengroups.OpenGroupAPI;
@@ -751,6 +752,11 @@ public class ConversationFragment extends Fragment
if (!Util.isEmpty(attachments)) {
SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity());
saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, attachments.toArray(new SaveAttachmentTask.Attachment[0]));
// Sending a Data extraction notification (for incoming attachments only)
if(!message.isOutgoing()) {
//TODO uncomment line below when Data extraction will be activated
//sendMediaSavedNotificationIfNeeded();
}
return;
}
@@ -763,6 +769,16 @@ public class ConversationFragment extends Fragment
});
}
/**
* Send a MediaSaved notification to the recipient
*/
private void sendMediaSavedNotificationIfNeeded() {
// we don't send media saved notification for groups
if (recipient.isGroupRecipient()) return;
DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis()));
MessageSender.send(message, recipient.getAddress());
}
@Override
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
Log.i(TAG, "onCreateLoader");

View File

@@ -14,11 +14,10 @@ import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage;
import org.thoughtcrime.securesms.BindableConversationItem;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt;
import org.thoughtcrime.securesms.loki.utilities.GroupDescription;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.DateUtils;
import org.session.libsignal.libsignal.util.guava.Optional;
@@ -106,6 +105,8 @@ public class ConversationUpdateItem extends LinearLayout
else if (messageRecord.isCallLog()) setCallRecord(messageRecord);
else if (messageRecord.isJoined()) setJoinedRecord(messageRecord);
else if (messageRecord.isExpirationTimerUpdate()) setTimerRecord(messageRecord);
else if (messageRecord.isScreenshotExtraction()) setDataExtractionRecord(messageRecord, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT);
else if (messageRecord.isMediaSavedExtraction()) setDataExtractionRecord(messageRecord, DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED);
else if (messageRecord.isEndSession()) setEndSessionRecord(messageRecord);
else if (messageRecord.isIdentityUpdate()) setIdentityRecord(messageRecord);
else if (messageRecord.isIdentityVerified() ||
@@ -149,6 +150,22 @@ public class ConversationUpdateItem extends LinearLayout
date.setVisibility(GONE);
}
private void setDataExtractionRecord(final MessageRecord messageRecord, DataExtractionNotificationInfoMessage.Kind kind) {
@ColorInt int color = GeneralUtilitiesKt.getColorWithID(getResources(), R.color.text, getContext().getTheme());
if (kind == DataExtractionNotificationInfoMessage.Kind.SCREENSHOT) {
icon.setImageResource(R.drawable.quick_camera_dark);
} else if (kind == DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED) {
icon.setImageResource(R.drawable.ic_file_download_white_36dp);
}
icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY));
body.setText(messageRecord.getDisplayBody(getContext()));
title.setVisibility(VISIBLE);
body.setVisibility(VISIBLE);
date.setVisibility(GONE);
}
private void setIdentityRecord(final MessageRecord messageRecord) {
icon.setImageResource(R.drawable.ic_security_white_24dp);
icon.setColorFilter(new PorterDuffColorFilter(Color.parseColor("#757575"), PorterDuff.Mode.MULTIPLY));
@@ -174,8 +191,6 @@ public class ConversationUpdateItem extends LinearLayout
private void setGroupRecord(MessageRecord messageRecord) {
icon.setImageResource(R.drawable.ic_group_grey600_24dp);
icon.clearColorFilter();
GroupDescription.Companion.getDescription(getContext(), messageRecord.getBody()).addListener(this);
body.setText(messageRecord.getDisplayBody(getContext()));
title.setVisibility(GONE);

View File

@@ -514,15 +514,7 @@ public class MmsDatabase extends MessagingDatabase {
}
}
if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) {
return new OutgoingGroupMediaMessage(recipient, body, attachments, timestamp, 0, quote, contacts, previews);
} else if (Types.isExpirationTimerUpdate(outboxType)) {
return new OutgoingExpirationUpdateMessage(recipient, timestamp, expiresIn);
}
boolean expirationTimer = (outboxType & Types.EXPIRATION_TIMER_UPDATE_BIT) != 0;
OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, expirationTimer, distributionType, quote, contacts, previews, networkFailures, mismatches);
OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, distributionType, quote, contacts, previews, networkFailures, mismatches);
if (Types.isSecureType(outboxType)) {
return new OutgoingSecureMediaMessage(message);
@@ -532,8 +524,6 @@ public class MmsDatabase extends MessagingDatabase {
}
throw new NoSuchMessageException("No record found for id: " + messageId);
} catch (IOException e) {
throw new MmsException(e);
} finally {
if (cursor != null)
cursor.close();
@@ -689,8 +679,19 @@ public class MmsDatabase extends MessagingDatabase {
{
if (threadId == -1) {
if(retrieved.isGroup()) {
ByteString decodedGroupId = ((OutgoingGroupMediaMessage)retrieved).getGroupContext().getId();
String groupId = GroupUtil.doubleEncodeGroupID(decodedGroupId.toByteArray());
String decodedGroupId;
if (retrieved instanceof OutgoingExpirationUpdateMessage) {
decodedGroupId = ((OutgoingExpirationUpdateMessage)retrieved).getGroupId();
} else {
decodedGroupId = ((OutgoingGroupMediaMessage)retrieved).getGroupId();
}
String groupId;
try {
groupId = GroupUtil.doubleEncodeGroupID(decodedGroupId);
} catch (IOException e) {
Log.e(TAG, "Couldn't encrypt group ID");
throw new MmsException(e);
}
Recipient group = Recipient.from(context, Address.fromSerialized(groupId), false);
threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(group);
} else {
@@ -718,6 +719,14 @@ public class MmsDatabase extends MessagingDatabase {
type |= Types.EXPIRATION_TIMER_UPDATE_BIT;
}
if (retrieved.isScreenshotDataExtraction()) {
type |= Types.SCREENSHOT_EXTRACTION_BIT;
}
if (retrieved.isMediaSavedDataExtraction()) {
type |= Types.MEDIA_SAVED_EXTRACTION_BIT;
}
return insertMessageInbox(retrieved, "", threadId, type, serverTimestamp);
}
@@ -745,9 +754,8 @@ public class MmsDatabase extends MessagingDatabase {
if (message.isSecure()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT);
if (forceSms) type |= Types.MESSAGE_FORCE_SMS_BIT;
if (message.isGroup()) {
if (((OutgoingGroupMediaMessage)message).isGroupUpdate()) type |= Types.GROUP_UPDATE_BIT;
else if (((OutgoingGroupMediaMessage)message).isGroupQuit()) type |= Types.GROUP_QUIT_BIT;
if (message.isGroup() && message instanceof OutgoingGroupMediaMessage) {
if (((OutgoingGroupMediaMessage)message).isUpdateMessage()) type |= Types.GROUP_UPDATE_MESSAGE_BIT;
}
if (message.isExpirationUpdate()) {

View File

@@ -70,6 +70,11 @@ public interface MmsSmsColumns {
protected static final long GROUP_UPDATE_BIT = 0x10000;
protected static final long GROUP_QUIT_BIT = 0x20000;
protected static final long EXPIRATION_TIMER_UPDATE_BIT = 0x40000;
protected static final long GROUP_UPDATE_MESSAGE_BIT = 0x80000;
// Data Extraction Information
protected static final long MEDIA_SAVED_EXTRACTION_BIT = 0x01000;
protected static final long SCREENSHOT_EXTRACTION_BIT = 0x02000;
// Encrypted Storage Information XXX
public static final long ENCRYPTION_MASK = 0xFF000000;
@@ -197,6 +202,14 @@ public interface MmsSmsColumns {
return (type & EXPIRATION_TIMER_UPDATE_BIT) != 0;
}
public static boolean isMediaSavedExtraction(long type) {
return (type & MEDIA_SAVED_EXTRACTION_BIT) != 0;
}
public static boolean isScreenshotExtraction(long type) {
return (type & SCREENSHOT_EXTRACTION_BIT) != 0;
}
public static boolean isIncomingCall(long type) {
return type == INCOMING_CALL_TYPE;
}
@@ -213,6 +226,8 @@ public interface MmsSmsColumns {
return (type & GROUP_UPDATE_BIT) != 0;
}
public static boolean isGroupUpdateMessage(long type) { return (type & GROUP_UPDATE_MESSAGE_BIT) != 0; }
public static boolean isGroupQuit(long type) {
return (type & GROUP_QUIT_BIT) != 0;
}

View File

@@ -347,8 +347,7 @@ public class SmsDatabase extends MessagingDatabase {
type |= Types.SECURE_MESSAGE_BIT;
} else if (message.isGroup()) {
type |= Types.SECURE_MESSAGE_BIT;
if (((IncomingGroupMessage)message).isUpdate()) type |= Types.GROUP_UPDATE_BIT;
else if (((IncomingGroupMessage)message).isQuit()) type |= Types.GROUP_QUIT_BIT;
if (((IncomingGroupMessage)message).isUpdateMessage()) type |= Types.GROUP_UPDATE_MESSAGE_BIT;
}
if (message.isPush()) type |= Types.PUSH_MESSAGE_BIT;

View File

@@ -17,11 +17,15 @@ import org.session.libsession.messaging.opengroups.OpenGroup
import org.session.libsession.messaging.opengroups.OpenGroupV2
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage
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.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.UpdateMessageBuilder
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
@@ -41,6 +45,7 @@ import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
import org.thoughtcrime.securesms.loki.utilities.get
import org.thoughtcrime.securesms.loki.utilities.getString
import org.thoughtcrime.securesms.mms.PartAuthority
import java.util.*
class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol {
override fun getUserPublicKey(): String? {
@@ -84,6 +89,11 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
return recipient.profileKey
}
override fun getDisplayNameForRecipient(recipientPublicKey: String): String? {
val database = DatabaseFactory.getLokiUserDatabase(context)
return database.getDisplayName(recipientPublicKey)
}
override fun setProfileKeyForRecipient(recipientPublicKey: String, profileKey: ByteArray) {
val address = Address.fromSerialized(recipientPublicKey)
val recipient = Recipient.from(context, address, false)
@@ -139,7 +149,6 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
val linkPreviews: Optional<List<LinkPreview>> = if (linkPreview.isEmpty()) Optional.absent() else Optional.of(linkPreview.mapNotNull { it!! })
val mmsDatabase = DatabaseFactory.getMmsDatabase(context)
val insertResult = if (message.sender == getUserPublicKey()) {
val mediaMessage = OutgoingMediaMessage.from(message, targetRecipient, pointerAttachments, quote.orNull(), linkPreviews.orNull()?.firstOrNull())
mmsDatabase.beginTransaction()
mmsDatabase.insertSecureDecryptedMessageOutbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!)
@@ -457,33 +466,24 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
DatabaseFactory.getGroupDatabase(context).updateMembers(groupID, members)
}
override fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type0: SignalServiceProtos.GroupContext.Type, type1: SignalServiceGroup.Type, name: String, members: Collection<String>, admins: Collection<String>, sentTimestamp: Long) {
val groupContextBuilder = SignalServiceProtos.GroupContext.newBuilder()
.setId(ByteString.copyFrom(GroupUtil.getDecodedGroupIDAsData(groupID)))
.setType(type0)
.setName(name)
.addAllMembers(members)
.addAllAdmins(admins)
val group = SignalServiceGroup(type1, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList())
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 infoMessage = IncomingGroupMessage(m, groupContextBuilder.build(), "")
val updateData = UpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON()
val infoMessage = IncomingGroupMessage(m, groupID, updateData, true)
val smsDB = DatabaseFactory.getSmsDatabase(context)
smsDB.insertMessageInbox(infoMessage)
}
override fun insertOutgoingInfoMessage(context: Context, groupID: String, type: SignalServiceProtos.GroupContext.Type, name: String, members: Collection<String>, admins: Collection<String>, threadID: Long, sentTimestamp: Long) {
override fun insertOutgoingInfoMessage(context: Context, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection<String>, admins: Collection<String>, threadID: Long, sentTimestamp: Long) {
val userPublicKey = getUserPublicKey()
val recipient = Recipient.from(context, Address.fromSerialized(groupID), false)
val groupContextBuilder = SignalServiceProtos.GroupContext.newBuilder()
.setId(ByteString.copyFrom(GroupUtil.getDecodedGroupIDAsData(groupID)))
.setType(type)
.setName(name)
.addAllMembers(members)
.addAllAdmins(admins)
val infoMessage = OutgoingGroupMediaMessage(recipient, groupContextBuilder.build(), null, sentTimestamp, 0, false, null, listOf(), listOf())
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)
if (mmsSmsDB.getMessageFor(sentTimestamp,userPublicKey) != null) return
if (mmsSmsDB.getMessageFor(sentTimestamp, userPublicKey) != null) return
val infoMessageID = mmsDB.insertMessageOutbox(infoMessage, threadID, false, null)
mmsDB.markAsSent(infoMessageID, true)
}
@@ -652,4 +652,26 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
override fun getAttachmentThumbnailUri(attachmentId: AttachmentId): Uri {
return PartAuthority.getAttachmentThumbnailUri(attachmentId)
}
// Data Extraction Notification
override fun insertDataExtractionNotificationMessage(senderPublicKey: String, message: DataExtractionNotificationInfoMessage, sentTimestamp: Long) {
val database = DatabaseFactory.getMmsDatabase(context)
val address = fromSerialized(senderPublicKey)
val recipient = Recipient.from(context, address, false)
if (recipient.isBlocked) return
val mediaMessage = IncomingMediaMessage(address, sentTimestamp, -1,
0, false,
false,
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.of(message))
database.insertSecureDecryptedMessageInbox(mediaMessage, -1)
}
}

View File

@@ -109,6 +109,7 @@ public abstract class DisplayRecord {
public boolean isLokiSessionRestoreDone() { return SmsDatabase.Types.isLokiSessionRestoreDoneType(type); }
// TODO isGroupUpdate and isGroupQuit are kept for compatibility with old update messages, they can be removed later on
public boolean isGroupUpdate() {
return SmsDatabase.Types.isGroupUpdate(type);
}
@@ -117,14 +118,33 @@ public abstract class DisplayRecord {
return SmsDatabase.Types.isGroupQuit(type);
}
public boolean isGroupUpdateMessage() {
return SmsDatabase.Types.isGroupUpdateMessage(type);
}
//TODO isGroupAction can be replaced by isGroupUpdateMessage in the code when the 2 functions above are removed
public boolean isGroupAction() {
return isGroupUpdate() || isGroupQuit();
return isGroupUpdate() || isGroupQuit() || isGroupUpdateMessage();
}
public boolean isExpirationTimerUpdate() {
return SmsDatabase.Types.isExpirationTimerUpdate(type);
}
// Data extraction
public boolean isMediaSavedExtraction() {
return MmsSmsColumns.Types.isMediaSavedExtraction(type);
}
public boolean isScreenshotExtraction() {
return MmsSmsColumns.Types.isScreenshotExtraction(type);
}
public boolean isDataExtraction() {
return isMediaSavedExtraction() || isScreenshotExtraction();
}
public boolean isCallLog() {
return SmsDatabase.Types.isCallLog(type);
}

View File

@@ -24,14 +24,16 @@ import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import network.loki.messenger.R;
import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage;
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;
import org.session.libsession.database.documents.NetworkFailure;
import org.session.libsession.messaging.threads.recipients.Recipient;
import org.session.libsession.utilities.ExpirationUtil;
import org.thoughtcrime.securesms.loki.utilities.GroupDescription;
import java.util.List;
@@ -90,39 +92,25 @@ public abstract class MessageRecord extends DisplayRecord {
@Override
public SpannableString getDisplayBody(@NonNull Context context) {
if (isGroupUpdate() && isOutgoing()) {
if(isGroupUpdateMessage()) {
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(UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, getIndividualRecipient().getAddress().serialize(), isOutgoing()));
} else if (isDataExtraction()) {
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()) {
return new SpannableString(context.getString(R.string.MessageRecord_you_updated_group));
} else if (isGroupUpdate()) {
return new SpannableString(GroupDescription.Companion.getDescription(context, getBody()).toString(getIndividualRecipient()));
return new SpannableString(context.getString(R.string.MessageRecord_s_updated_group, getIndividualRecipient().toShortString()));
} else if (isGroupQuit() && isOutgoing()) {
return new SpannableString(context.getString(R.string.MessageRecord_left_group));
} else if (isGroupQuit()) {
return new SpannableString(context.getString(R.string.ConversationItem_group_action_left, getIndividualRecipient().toShortString()));
} else if (isIncomingCall()) {
return new SpannableString(context.getString(R.string.MessageRecord_s_called_you, getIndividualRecipient().toShortString()));
} else if (isOutgoingCall()) {
return new SpannableString(context.getString(R.string.MessageRecord_you_called));
} else if (isMissedCall()) {
return new SpannableString(context.getString(R.string.MessageRecord_missed_call));
} else if (isJoined()) {
return new SpannableString(context.getString(R.string.MessageRecord_s_joined_signal, getIndividualRecipient().toShortString()));
} else if (isExpirationTimerUpdate()) {
int seconds = (int)(getExpiresIn() / 1000);
if (seconds <= 0) {
return isOutgoing() ? new SpannableString(context.getString(R.string.MessageRecord_you_disabled_disappearing_messages))
: new SpannableString(context.getString(R.string.MessageRecord_s_disabled_disappearing_messages, getIndividualRecipient().toShortString()));
}
String time = ExpirationUtil.getExpirationDisplayValue(context, seconds);
return isOutgoing() ? new SpannableString(context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time))
: new SpannableString(context.getString(R.string.MessageRecord_s_set_disappearing_message_time_to_s, getIndividualRecipient().toShortString(), time));
} else if (isIdentityUpdate()) {
return new SpannableString(context.getString(R.string.MessageRecord_your_safety_number_with_s_has_changed, getIndividualRecipient().toShortString()));
} else if (isIdentityVerified()) {
if (isOutgoing()) return new SpannableString(context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_verified, getIndividualRecipient().toShortString()));
else return new SpannableString(context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device, getIndividualRecipient().toShortString()));
} else if (isIdentityDefault()) {
if (isOutgoing()) return new SpannableString(context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_unverified, getIndividualRecipient().toShortString()));
else return new SpannableString(context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_unverified_from_another_device, getIndividualRecipient().toShortString()));
}
return new SpannableString(getBody());
@@ -175,7 +163,7 @@ public abstract class MessageRecord extends DisplayRecord {
}
public boolean isUpdate() {
return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() ||
return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() || isDataExtraction() ||
isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() || isLokiSessionRestoreSent() || isLokiSessionRestoreDone();
}

View File

@@ -28,6 +28,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.session.libsession.messaging.threads.recipients.Recipient;
import org.session.libsession.messaging.utilities.UpdateMessageBuilder;
import org.session.libsession.messaging.utilities.UpdateMessageData;
import org.session.libsession.utilities.ExpirationUtil;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase;
@@ -73,7 +75,7 @@ public class ThreadRecord extends DisplayRecord {
@Override
public SpannableString getDisplayBody(@NonNull Context context) {
Recipient recipient = getRecipient();
if (isGroupUpdate()) {
if (isGroupUpdate() || isGroupUpdateMessage()) {
return emphasisAdded(context.getString(R.string.ThreadRecord_group_updated));
} else if (isGroupQuit()) {
return emphasisAdded(context.getString(R.string.ThreadRecord_left_the_group));
@@ -103,12 +105,16 @@ public class ThreadRecord extends DisplayRecord {
} else if (SmsDatabase.Types.isJoinedType(type)) {
return emphasisAdded(context.getString(R.string.ThreadRecord_s_is_on_signal, getRecipient().toShortString()));
} else if (SmsDatabase.Types.isExpirationTimerUpdate(type)) {
int seconds = (int)(getExpiresIn() / 1000);
int seconds = (int) (getExpiresIn() / 1000);
if (seconds <= 0) {
return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_messages_disabled));
}
String time = ExpirationUtil.getExpirationDisplayValue(context, seconds);
return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_message_time_updated_to_s, time));
} else if (MmsSmsColumns.Types.isMediaSavedExtraction(type)) {
return emphasisAdded(context.getString(R.string.ThreadRecord_media_saved_by_s, getRecipient().toShortString()));
} else if (MmsSmsColumns.Types.isScreenshotExtraction(type)) {
return emphasisAdded(context.getString(R.string.ThreadRecord_s_took_a_screenshot, getRecipient().toShortString()));
} else if (SmsDatabase.Types.isIdentityUpdate(type)) {
if (getRecipient().isGroupRecipient()) return emphasisAdded(context.getString(R.string.ThreadRecord_safety_number_changed));
else return emphasisAdded(context.getString(R.string.ThreadRecord_your_safety_number_with_s_has_changed, getRecipient().toShortString()));

View File

@@ -32,6 +32,10 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderM
set(newValue) { field = newValue; invalidateOptionsMenu() }
private var members = listOf<String>()
set(value) { field = value; selectContactsAdapter.members = value }
private val publicKey: String
get() {
return TextSecurePreferences.getLocalNumber(this)!!
}
private val selectContactsAdapter by lazy {
SelectContactsAdapter(this, GlideApp.with(this))
@@ -72,7 +76,8 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderM
}
private fun update(members: List<String>) {
this.members = members
//if there is a Note to self conversation, it loads self in the list, so we need to remove it here
this.members = members.minus(publicKey)
mainContentContainer.visibility = if (members.isEmpty()) View.GONE else View.VISIBLE
emptyStateContainer.visibility = if (members.isEmpty()) View.VISIBLE else View.GONE
invalidateOptionsMenu()

View File

@@ -36,6 +36,27 @@ class SelectContactsAdapter(private val context: Context, private val glide: Gli
isSelected)
}
override fun onBindViewHolder(viewHolder: ViewHolder,
position: Int,
payloads: MutableList<Any>) {
if (payloads.isNotEmpty()) {
// Because these updates can be batched,
// there can be multiple payloads for a single bind
when (payloads[0]) {
Payload.MEMBER_CLICKED -> {
val member = members[position]
val isSelected = selectedMembers.contains(member)
viewHolder.view.toggleCheckbox(isSelected)
}
}
} else {
// When payload list is empty,
// or we don't have logic to handle a given type,
// default to full bind:
this.onBindViewHolder(viewHolder, position)
}
}
private fun onMemberClick(member: String) {
if (selectedMembers.contains(member)) {
selectedMembers.remove(member)
@@ -43,6 +64,11 @@ class SelectContactsAdapter(private val context: Context, private val glide: Gli
selectedMembers.add(member)
}
val index = members.indexOf(member)
notifyItemChanged(index)
notifyItemChanged(index, Payload.MEMBER_CLICKED)
}
}
// define below the different events used to notify the adapter
enum class Payload {
MEMBER_CLICKED
}
}

View File

@@ -9,11 +9,11 @@ import nl.komponents.kovenant.all
import nl.komponents.kovenant.functional.map
import org.session.libsession.messaging.jobs.MessageReceiveJob
import org.session.libsession.messaging.opengroups.OpenGroup
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPoller
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.service.loki.api.SnodeAPI
import org.session.libsignal.utilities.logging.Log
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.database.DatabaseFactory
import java.util.concurrent.TimeUnit
@@ -78,7 +78,7 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor
promises.addAll(privateChatsPromise.get())
// Closed groups
promises.addAll(ApplicationContext.getInstance(context).closedGroupPoller.pollOnce())
promises.addAll(ClosedGroupPoller().pollOnce())
// Open Groups
val openGroups = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().map { (_,chat)->

View File

@@ -9,7 +9,6 @@ import org.session.libsignal.libsignal.ecc.ECKeyPair
import org.session.libsignal.service.api.messages.SignalServiceGroup
import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage
import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
import org.session.libsignal.service.loki.utilities.toHexString
import org.thoughtcrime.securesms.database.DatabaseFactory
@@ -104,11 +103,11 @@ object ClosedGroupsProtocolV2 {
apiDB.addClosedGroupEncryptionKeyPair(encryptionKeyPair, groupPublicKey)
// Notify the user (if we didn't make the group)
if (userPublicKey != senderPublicKey) {
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp)
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp)
} else if (prevGroup == null) {
// only notify if we created this group
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID, sentTimestamp)
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, threadID, sentTimestamp)
}
// Notify the PN server
LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey)
@@ -156,14 +155,14 @@ object ClosedGroupsProtocolV2 {
MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey, newMembers)
}
}
val (contextType, signalType) =
if (senderLeft) GroupContext.Type.QUIT to SignalServiceGroup.Type.QUIT
else GroupContext.Type.UPDATE to SignalServiceGroup.Type.UPDATE
val type =
if (senderLeft) SignalServiceGroup.Type.QUIT
else SignalServiceGroup.Type.UPDATE
if (userPublicKey == senderPublicKey) {
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, contextType, name, members, admins, threadID, sentTimestamp)
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, type, name, members, admins, threadID, sentTimestamp)
} else {
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, contextType, signalType, name, members, admins, sentTimestamp)
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, type, name, members, admins, sentTimestamp)
}
}
@@ -191,9 +190,9 @@ object ClosedGroupsProtocolV2 {
groupDB.updateMembers(groupID, newMembers.map { Address.fromSerialized(it) })
if (userPublicKey == senderPublicKey) {
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID, sentTimestamp)
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, threadID, sentTimestamp)
} else {
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp)
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp)
}
if (userPublicKey in admins) {
// send current encryption key to the latest added members
@@ -230,9 +229,9 @@ object ClosedGroupsProtocolV2 {
// Notify the user
if (userPublicKey == senderPublicKey) {
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID, sentTimestamp)
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, threadID, sentTimestamp)
} else {
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp)
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp)
}
}
@@ -272,9 +271,9 @@ object ClosedGroupsProtocolV2 {
// Notify user
if (userLeft) {
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, GroupContext.Type.QUIT, name, members, admins, threadID, sentTimestamp)
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.QUIT, name, members, admins, threadID, sentTimestamp)
} else {
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.QUIT, SignalServiceGroup.Type.QUIT, name, members, admins, sentTimestamp)
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.QUIT, name, members, admins, sentTimestamp)
}
}
@@ -385,14 +384,13 @@ object ClosedGroupsProtocolV2 {
}
// Notify the user
val wasSenderRemoved = !members.contains(senderPublicKey)
val type0 = if (wasSenderRemoved) GroupContext.Type.QUIT else GroupContext.Type.UPDATE
val type1 = if (wasSenderRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.UPDATE
val type = if (wasSenderRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.UPDATE
val admins = group.admins.map { it.toString() }
if (userPublicKey == senderPublicKey) {
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, type0, name, members, admins, threadID, sentTimestamp)
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, type, name, members, admins, threadID, sentTimestamp)
} else {
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, type0, type1, name, members, admins, sentTimestamp)
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, type, name, members, admins, sentTimestamp)
}
}
// endregion

View File

@@ -1,103 +0,0 @@
package org.thoughtcrime.securesms.loki.utilities
import android.content.Context
import org.session.libsession.messaging.threads.Address
import org.session.libsession.messaging.threads.recipients.Recipient
import org.session.libsession.messaging.threads.recipients.RecipientModifiedListener
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.utilities.Base64
import org.session.libsignal.service.internal.push.SignalServiceProtos
import java.util.*
import network.loki.messenger.R
import org.session.libsignal.utilities.logging.Log
import java.io.IOException
class GroupDescription(context: Context, groupContext: SignalServiceProtos.GroupContext?) {
private val context: Context
private val groupContext: SignalServiceProtos.GroupContext?
private val newMembers: MutableList<Recipient>
private val removedMembers: MutableList<Recipient>
private var wasCurrentUserRemoved: Boolean = false
private fun toRecipient(hexEncodedPublicKey: String): Recipient {
val address = Address.fromSerialized(hexEncodedPublicKey)
return Recipient.from(context, address, false)
}
fun toString(sender: Recipient): String {
if (wasCurrentUserRemoved) {
return context.getString(R.string.GroupUtil_you_were_removed_from_group)
}
val description = StringBuilder()
description.append(context.getString(R.string.MessageRecord_s_updated_group, sender.toShortString()))
if (groupContext == null) {
return description.toString()
}
val title = groupContext.name
if (!newMembers.isEmpty()) {
description.append("\n")
description.append(context.resources.getQuantityString(R.plurals.GroupUtil_joined_the_group,
newMembers.size, toString(newMembers)))
}
if (!removedMembers.isEmpty()) {
description.append("\n")
description.append(context.resources.getQuantityString(R.plurals.GroupUtil_removed_from_the_group,
removedMembers.size, toString(removedMembers)))
}
if (title != null && !title.trim { it <= ' ' }.isEmpty()) {
val separator = if (!newMembers.isEmpty() || !removedMembers.isEmpty()) " " else "\n"
description.append(separator)
description.append(context.getString(R.string.GroupUtil_group_name_is_now, title))
}
return description.toString()
}
fun addListener(listener: RecipientModifiedListener?) {
if (!newMembers.isEmpty()) {
for (member in newMembers) {
member.addListener(listener)
}
}
}
private fun toString(recipients: List<Recipient>): String {
var result = ""
for (i in recipients.indices) {
result += recipients[i].toShortString()
if (i != recipients.size - 1) result += ", "
}
return result
}
init {
this.context = context.applicationContext
this.groupContext = groupContext
newMembers = LinkedList()
removedMembers = LinkedList()
if (groupContext != null) {
val newMembers = groupContext.newMembersList
for (member in newMembers) {
this.newMembers.add(toRecipient(member))
}
val removedMembers = groupContext.removedMembersList
for (member in removedMembers) {
this.removedMembers.add(toRecipient(member))
}
wasCurrentUserRemoved = removedMembers.contains(TextSecurePreferences.getLocalNumber(context))
}
}
companion object {
fun getDescription(context: Context, encodedGroup: String?): GroupDescription {
return if (encodedGroup == null) {
GroupDescription(context, null)
} else try {
val groupContext = SignalServiceProtos.GroupContext.parseFrom(Base64.decode(encodedGroup))
GroupDescription(context, groupContext)
} catch (e: IOException) {
Log.w("Loki", e)
GroupDescription(context, null)
}
}
}
}

View File

@@ -82,8 +82,13 @@ class UserView : LinearLayout {
}
}
fun toggleCheckbox(isSelected: Boolean = false) {
actionIndicatorImageView.visibility = View.VISIBLE
actionIndicatorImageView.setImageResource(if (isSelected) R.drawable.ic_circle_check else R.drawable.ic_circle)
}
fun unbind() {
}
// endregion
}
}

View File

@@ -2,22 +2,17 @@ package org.thoughtcrime.securesms.service;
import android.content.Context;
import com.google.protobuf.ByteString;
import org.jetbrains.annotations.NotNull;
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate;
import org.session.libsession.messaging.messages.signal.OutgoingGroupMediaMessage;
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
import org.session.libsession.messaging.messages.signal.OutgoingExpirationUpdateMessage;
import org.session.libsession.messaging.threads.Address;
import org.session.libsession.messaging.threads.DistributionTypes;
import org.session.libsession.messaging.threads.recipients.Recipient;
import org.session.libsession.utilities.GroupUtil;
import org.session.libsession.utilities.SSKEnvironment;
import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.messages.SignalServiceGroup;
import org.session.libsignal.service.internal.push.SignalServiceProtos;
import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext;
import org.session.libsignal.utilities.logging.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
@@ -28,7 +23,6 @@ import org.session.libsession.messaging.messages.signal.IncomingMediaMessage;
import org.thoughtcrime.securesms.mms.MmsException;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.TreeSet;
import java.util.concurrent.Executor;
@@ -79,8 +73,8 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
String senderPublicKey = message.getSender();
// Notify the user
if (userPublicKey.equals(senderPublicKey)) {
// sender is a linked device
if (senderPublicKey == null || userPublicKey.equals(senderPublicKey)) {
// sender is self or a linked device
insertOutgoingExpirationTimerMessage(message);
} else {
insertIncomingExpirationTimerMessage(message);
@@ -100,7 +94,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
int duration = message.getDuration();
Optional<SignalServiceGroup> groupInfo = Optional.absent();
Address address = Address.fromSerialized((message.getSyncTarget() != null && !message.getSyncTarget().isEmpty()) ? message.getSyncTarget() : senderPublicKey);
Address address = Address.fromSerialized(senderPublicKey);
Recipient recipient = Recipient.from(context, address, false);
// if the sender is blocked, we don't display the update, except if it's in a closed group
@@ -123,6 +117,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent(),
Optional.absent());
//insert the timer update message
database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
@@ -138,41 +133,21 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
private void insertOutgoingExpirationTimerMessage(ExpirationTimerUpdate message) {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
String senderPublicKey = message.getSender();
Long sentTimestamp = message.getSentTimestamp();
String groupId = message.getGroupPublicKey();
int duration = message.getDuration();
Address address = Address.fromSerialized((message.getSyncTarget() != null && !message.getSyncTarget().isEmpty()) ? message.getSyncTarget() : senderPublicKey);
Address address = Address.fromSerialized((message.getSyncTarget() != null && !message.getSyncTarget().isEmpty()) ? message.getSyncTarget() : message.getRecipient());
Recipient recipient = Recipient.from(context, address, false);
try {
OutgoingExpirationUpdateMessage timerUpdateMessage = new OutgoingExpirationUpdateMessage(recipient, sentTimestamp, duration * 1000L, groupId);
database.insertSecureDecryptedMessageOutbox(timerUpdateMessage, -1, sentTimestamp);
if (groupId != null) {
// conversation is a closed group
GroupContext groupContext = SignalServiceProtos.GroupContext.newBuilder()
.setId(ByteString.copyFrom(GroupUtil.getDecodedGroupIDAsData(groupId))).build();
OutgoingGroupMediaMessage infoMessage = new OutgoingGroupMediaMessage(recipient, groupContext, null, sentTimestamp, duration * 1000L, true, null, Collections.emptyList(), Collections.emptyList());
database.insertSecureDecryptedMessageOutbox(infoMessage, -1, sentTimestamp);
// we need the group ID as recipient for setExpireMessages below
recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.doubleEncodeGroupID(groupId)), false);
} else {
// conversation is a 1-1
OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipient,
null,
Collections.emptyList(),
message.getSentTimestamp(),
-1,
duration * 1000L,
true,
DistributionTypes.DEFAULT,
null,
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyList());
database.insertSecureDecryptedMessageOutbox(mediaMessage, -1, sentTimestamp);
}
//set the timer to the conversation
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, duration);

View File

@@ -1,4 +0,0 @@
package org.thoughtcrime.securesms.sskenvironment
class DataExtractionNotificationManager {
}

View File

@@ -418,6 +418,16 @@
<string name="MessageRecord_message_encrypted_with_a_legacy_protocol_version_that_is_no_longer_supported">Vous avez reçu un message chiffré avec une ancienne version de Signal qui nest plus prise en charge. Veuillez demander à lexpéditeur de mettre Session à jour vers la version la plus récente et de renvoyer son message.</string>
<string name="MessageRecord_left_group">Vous avez quitté le groupe</string>
<string name="MessageRecord_you_updated_group">Vous avez mis le groupe à jour.</string>
<string name="MessageRecord_you_created_a_new_group">Vous avez créée un nouveau groupe.</string>
<string name="MessageRecord_s_added_you_to_the_group">%1$s vous a ajouté au groupe.</string>
<string name="MessageRecord_you_renamed_the_group_to_s">Vous avez renommé le groupe en \'%1$s\'</string>
<string name="MessageRecord_s_renamed_the_group_to_s">%1$s a renommé le groupe en \'%2$s\'</string>
<string name="MessageRecord_you_added_s_to_the_group">Vous avez ajouté %1$s au groupe.</string>
<string name="MessageRecord_s_added_s_to_the_group">%1$s a ajouté %2$s au groupe.</string>
<string name="MessageRecord_you_removed_s_from_the_group">Vous avez supprimé %1$s du groupe.</string>
<string name="MessageRecord_s_removed_s_from_the_group">%1$s a supprimé %2$s du groupe.</string>
<string name="MessageRecord_you_were_removed_from_the_group">Vous avez été supprimé du groupe.</string>
<string name="MessageRecord_you">Vous</string>
<string name="MessageRecord_you_called">Vous avez appelé</string>
<string name="MessageRecord_called_you">Contact appelé</string>
<string name="MessageRecord_missed_call">Appel manqué</string>
@@ -430,6 +440,8 @@
<string name="MessageRecord_s_disabled_disappearing_messages">%1$s a désactivé les messages éphémères.</string>
<string name="MessageRecord_you_set_disappearing_message_time_to_s">Vous avez défini lexpiration des messages éphémères à %1$s.</string>
<string name="MessageRecord_s_set_disappearing_message_time_to_s">%1$s a défini lexpiration des messages éphémères à %2$s.</string>
<string name="MessageRecord_s_took_a_screenshot">%1$s a pris une capture décran.</string>
<string name="MessageRecord_media_saved_by_s">%1$s a sauvegardé un media.</string>
<string name="MessageRecord_your_safety_number_with_s_has_changed">Votre numéro de sécurité avec %s a changé.</string>
<string name="MessageRecord_you_marked_your_safety_number_with_s_verified">Vous avez marqué votre numéro de sécurité avec %s comme vérifié</string>
<string name="MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device">Vous avez marqué votre numéro de sécurité avec %s comme vérifié à partir dun autre appareil</string>
@@ -1483,4 +1495,35 @@ Vous avez reçu un message déchange de clés pour une version de protocole i
<string name="fragment_contact_selection_closed_groups_title">Groupes privés</string>
<string name="fragment_contact_selection_open_groups_title">Groupes publics</string>
<!-- Next round of translation -->
<string name="menu_apply_button">Appliquer</string>
<string name="menu_done_button">Terminé</string>
<string name="activity_edit_closed_group_title">Modifier le groupe</string>
<string name="activity_edit_closed_group_edit_text_hint">Saisissez un nouveau nom de groupe</string>
<string name="activity_edit_closed_group_edit_members">Membres</string>
<string name="activity_edit_closed_group_add_members">Ajouter des membres</string>
<string name="activity_edit_closed_group_group_name_missing_error">Le nom du groupe ne peut pas être vide</string>
<string name="activity_edit_closed_group_group_name_too_long_error">Veuillez saisir un nom plus court</string>
<string name="activity_edit_closed_group_not_enough_group_members_error">Les groupes doivent avoir au moins un membre</string>
<string name="activity_edit_closed_group_invalid_session_id_error">Un des membres du group a un Session ID invalide</string>
<string name="activity_edit_closed_group_confirm_removal">Êtes vous sûr de vouloir suprimer ce membre du groupe?</string>
<string name="activity_edit_closed_group_member_removed">Membre supprimé du groupe</string>
<string name="fragment_edit_group_bottom_sheet_remove">Supprimer le membre du groupe</string>
<string name="activity_select_contacts_title">Contacts</string>
<string name="dialog_ui_mode_title">Thème</string>
<string name="dialog_ui_mode_option_day">Jour</string>
<string name="dialog_ui_mode_option_night">Nuit</string>
<string name="dialog_ui_mode_option_system_default">Système</string>
<string name="activity_conversation_menu_copy_session_id">Copier le Session ID</string>
<string name="attachment">Pièce jointe</string>
<string name="attachment_type_voice_message">Message vocal</string>
<string name="details">Détails</string>
</resources>

View File

@@ -493,6 +493,16 @@
<string name="MessageRecord_message_encrypted_with_a_legacy_protocol_version_that_is_no_longer_supported">Received a message encrypted using an old version of Session that is no longer supported. Please ask the sender to update to the most recent version and resend the message.</string>
<string name="MessageRecord_left_group">You have left the group.</string>
<string name="MessageRecord_you_updated_group">You updated the group.</string>
<string name="MessageRecord_you_created_a_new_group">You created a new group.</string>
<string name="MessageRecord_s_added_you_to_the_group">%1$s added you to the group.</string>
<string name="MessageRecord_you_renamed_the_group_to_s">You renamed the group to \'%1$s\'</string>
<string name="MessageRecord_s_renamed_the_group_to_s">%1$s renamed the group to \'%2$s\'</string>
<string name="MessageRecord_you_added_s_to_the_group">You added %1$s to the group.</string>
<string name="MessageRecord_s_added_s_to_the_group">%1$s added %2$s to the group.</string>
<string name="MessageRecord_you_removed_s_from_the_group">You removed %1$s from the group.</string>
<string name="MessageRecord_s_removed_s_from_the_group">%1$s removed %2$s from the group.</string>
<string name="MessageRecord_you_were_removed_from_the_group">You were removed from the group.</string>
<string name="MessageRecord_you">You</string>
<string name="MessageRecord_you_called">You called</string>
<string name="MessageRecord_called_you">Contact called</string>
<string name="MessageRecord_missed_call">Missed call</string>
@@ -505,6 +515,8 @@
<string name="MessageRecord_s_disabled_disappearing_messages">%1$s disabled disappearing messages.</string>
<string name="MessageRecord_you_set_disappearing_message_time_to_s">You set the disappearing message timer to %1$s</string>
<string name="MessageRecord_s_set_disappearing_message_time_to_s">%1$s set the disappearing message timer to %2$s</string>
<string name="MessageRecord_s_took_a_screenshot">%1$s took a screenshot.</string>
<string name="MessageRecord_media_saved_by_s">Media saved by %1$s.</string>
<string name="MessageRecord_your_safety_number_with_s_has_changed">Your safety number with %s has changed.</string>
<string name="MessageRecord_you_marked_your_safety_number_with_s_verified">You marked your safety number with %s verified</string>
<string name="MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device">You marked your safety number with %s verified from another device</string>
@@ -703,6 +715,8 @@
<string name="ThreadRecord_s_is_on_signal">%s is on Session!</string>
<string name="ThreadRecord_disappearing_messages_disabled">Disappearing messages disabled</string>
<string name="ThreadRecord_disappearing_message_time_updated_to_s">Disappearing message time set to %s</string>
<string name="ThreadRecord_s_took_a_screenshot">%s took a screenshot.</string>
<string name="ThreadRecord_media_saved_by_s">Media saved by %s.</string>
<string name="ThreadRecord_safety_number_changed">Safety number changed</string>
<string name="ThreadRecord_your_safety_number_with_s_has_changed">Your safety number with %s has changed.</string>
<string name="ThreadRecord_you_marked_verified">You marked verified</string>