* Remove unused sizeResId

* Fix caching

* Prefix message with name in HomeActivity

* Hide sender prefix for note to self

* Hide sender prefix for control messages

* Remove problematic getLastMessage()

* Refactor snippet formatting

* Remove unused RecoveryPhraseRestoreActivity

* Fix unresolved theme attributes exception

* Fix dialog button style

* Investigation in progress

* Working fix push before cleanup

* Fixes #1346

* Removed unused logging imports

* Put back some whitespace

* Minor cleanup

* Fix NPE on null display name

* fix: disappearing viewmodel tests (#1432)

* SES-1354 - Video call self viewer not mirrored (#1397)

* Fixes #874

* Removed accidentally left in line

* Fixed issue - push before cleanup

* Cleaned up

* Removed cruft

---------

Co-authored-by: = <=>
Co-authored-by: AL-Session <160798022+AL-Session@users.noreply.github.com>

* SES-1145 - New messages are hidden under keyboard - MK3 (#1415)

* WIP

* Working - push before cleanup

* Fixes #1316

* Cleanup

* PR review adjustments

* Fixed scrolling when receiving an image based message while keyboard is up

* Prevent auto-scroll to last seen item pos in conversation view if <= 3

* Put back <=3 check to scroll

---------

Co-authored-by: = <=>
Co-authored-by: AL-Session <160798022+AL-Session@users.noreply.github.com>

* Fix missing parenthesis

* SES-789 - Scroll to bottom of long new message(s) (#1426)

* WIP

* Working - push before cleanup

* Fixes #1316

* Cleanup

* PR review adjustments

* Fixed scrolling when receiving an image based message while keyboard is up

* Prevent auto-scroll to last seen item pos in conversation view if <= 3

* Put back <=3 check to scroll

* Forced scrolling to bottom of long messages (both sent and received) when already at the bottom of the RecyclerView

* Fixes #1364

---------

Co-authored-by: = <=>
Co-authored-by: AL-Session <160798022+AL-Session@users.noreply.github.com>

* SES-1352 - User and group names allowing multi-line strings (#1395)

* Fix WIP

* Resolved issue - pushing before cleanup & PR tomorrow morning

* Enforced single line for new closed group names

* Fixes #1394

* Final cleanup prior to PR

* Added code to restore a previous contact nickname if an empty one is given

* Added initial limits to nicknames and group names, both creation and display

* Minor adjustments

* Adjusted max nickname and group name to 35 chars as per Kee's instructions

* Fixed closed group edit text able to get too wide and cut off buttons

---------

Co-authored-by: = <=>
Co-authored-by: AL-Session <160798022+AL-Session@users.noreply.github.com>
Co-authored-by: Al Lansley <al@oxen.io>

* SES-212 - Always show delivery status of last sent message - FINAL! (#1418)

* Fixes #1408

* Addressed PR feedback

* Cleanup

* PR adjustments

* Further PR adjustments

* Updated libsession-util

* Added fix for crash when no messages

* Ignoring dirty submodules so they don't show up in git

* Re-fixed display of delivery status on last sent message (got broken by disappearing messages)

* Removed ignore dirty modules line in .gitmodules as it all seems to be playing nice now

---------

Co-authored-by: AL-Session <160798022+AL-Session@users.noreply.github.com>
Co-authored-by: Al Lansley <al@oxen.io>

* fix: use a set for the from/to serialized lists (#1370)

* Fixes #1347 (#1396)

Co-authored-by: AL-Session <160798022+AL-Session@users.noreply.github.com>
Co-authored-by: Al Lansley <al@oxen.io>

* SES-1156 - Ban and delete functionality fix (#1428)

* WIP

* Investigation in progress

* End of day push

* WIP

* Fixes #1416

* Cleanup

* Added code to remove zombie messages caught in limbo during a ban & delete - still chock full o' debug while finding root cause

* Root cause debug WIP

* Push prior to cleanup

* Cleaned up for PR

* fix: mms delete, remove unnecessary values from sms

* Addressed PR feedback

* fix: fix unit tests

* Added '.run' folder with test setup

* Update README.md

Test commit for CI

* Re-added accidentally removed closing brace

---------

Co-authored-by: alansley <aclansley@gmail.com>
Co-authored-by: Al Lansley <alansley@users.noreply.github.com>
Co-authored-by: 0x330a <92654767+0x330a@users.noreply.github.com>

* SES-1356 - List of recently used reaction emojis is not accurate (#1400)

* WIP

* Further WIP

* Push prior to cleanup

* Fixes #1015

* Added limiting to the count of recently used emoji that we store

* Put back adjusted reaction pill layout to standard

* Adjusted recently used reaction emojis already in list to go to start of list

---------

Co-authored-by: = <=>

* SES-697 - Add loading state when exporting logs (#1402)

* WIP

* Fixes #1401

* Cleanup from PR view

* Final cleanup

* Removed commented line of code & re-ordered comment

* Addressed PR feedback

* Re-allowed loading of avatars to throw exceptions rather than return null on failure

---------

Co-authored-by: = <=>

* SES-1251 - App crash on non alphanumeric first char search (#1393)

* Investigation in progress

* Working fix push before cleanup

* Fixes #1346

* Removed unused logging imports

* Put back some whitespace

* Minor cleanup

* Push before cleanup

* Fixes #1346 - properly this time!

* SES1567 - Community message delivery status fix (#1442)

* Initial investigation

* WIP

* Continued work to track down cause of delivery status issue

* Fixes #1438

* Cleanup for PR

* Further cleanup

* Fixed merge conflict

* Addressed PR feedback

---------

Co-authored-by: alansley <aclansley@gmail.com>

* Tiny adjustment to center user name in Settings activity (#1446)

* Addressed PR feedback

* Cleanup

* Initial fix implemented

* Fixes #1448

* Addressed PR feedback

* SES1688 - Deleting last message in conversation, group, or community leaves the RecyclerView in a broken state (#1449)

* Initial fix implemented

* Fixes #1448

* Addressed PR feedback

* Handle case where there are no messages

* build: update build number

* Fix spacing when title is absent

* Hide reply button in MessageDetails for group invitations

* Remove reply from context menu for open group invitations

* Ignore swipe reply to open group invitation

* Fix multiple quote previews

* Fix message menu icons not visible in light theme

* Hide reply app bar menu item for open group invite

* SES-1727 Mentions text is the wrong colour (#1454)

* Fixes #1453

* Cleanup

* Code review adjustments

* Adjusted mentions to use the accent colour as their background colour when using light themes

---------

Co-authored-by: alansley <aclansley@gmail.com>

* Disable swipe to reply on open group invites

* Fix multiple link previews

* SES1718 - Message Sending Status (#1462)

* Investigation in progress

* Initial push for PR

* Fixes #1461

* Removed leftover debug comments

* Added minor optimisation to showMessageStatus method (bail early if the message isn't one we care about displaying details of to the user)

* Minor cleanup

* Tiny cleanup

* Addressed PR feedback

* Removed forgotten debug log line & forced delivery status elements to be removed on non-visible messages just in case

* Minor refactor to simplify 'VisibleMessageView.showStatusMessage'

---------

Co-authored-by: alansley <aclansley@gmail.com>

* Fix margins

* WIP

* Commit before converting SmsDatabase from Java to Kotlin

* Remove old expiration config strings from UpdateMessageBuilder

* Fix group expiration update config messages

* Fixed conversation view closing + hopefully wrong status text displayed + deletion of contact on removal of last message in 1-on-1 convo

* Cleanup for PR review

* Implemented PR feedback

* Don't start expiration for group expiration update messages

* Fix expiry update message for groups

* Correctly don't start disappear timer on group timer updates

* SES1813 - Fix regression test failures (#1473)

* Initial fix for regression test failure 1.1

* Added permissions fix for sharing documents which should allow for thumbnail generation

* Minor touch-up prior to merge into dev

* Fixes #1813

* Fixes #1472 - please ignore previous fixes 1813 statement, I'd used the Jira ticket number rather than creating a GitHub issue and using that

---------

Co-authored-by: alansley <aclansley@gmail.com>

---------

Co-authored-by: alansley <aclansley@gmail.com>
Co-authored-by: 0x330a <92654767+0x330a@users.noreply.github.com>
Co-authored-by: Al Lansley <alansley@users.noreply.github.com>
Co-authored-by: AL-Session <160798022+AL-Session@users.noreply.github.com>
Co-authored-by: Al Lansley <al@oxen.io>
This commit is contained in:
Andrew
2024-05-01 12:29:33 +09:30
committed by GitHub
parent 7bcf823740
commit d16faf94c9
123 changed files with 1376 additions and 1007 deletions

View File

@@ -8,9 +8,11 @@ import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import org.session.libsession.utilities.Address;
import org.session.libsignal.utilities.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -22,9 +24,9 @@ public class AvatarHelper {
private static final String AVATAR_DIRECTORY = "avatars";
public static InputStream getInputStreamFor(@NonNull Context context, @NonNull Address address)
throws IOException
throws FileNotFoundException
{
return new FileInputStream(getAvatarFile(context, address));
return new FileInputStream(getAvatarFile(context, address));
}
public static List<File> getAvatarFiles(@NonNull Context context) {

View File

@@ -8,6 +8,7 @@ import androidx.annotation.Nullable;
import org.session.libsession.utilities.Address;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
@@ -24,7 +25,7 @@ public class ProfileContactPhoto implements ContactPhoto {
}
@Override
public InputStream openInputStream(Context context) throws IOException {
public InputStream openInputStream(Context context) throws FileNotFoundException {
return AvatarHelper.getInputStreamFor(context, address);
}

View File

@@ -8,6 +8,7 @@ import android.graphics.drawable.LayerDrawable;
import android.widget.ImageView;
import androidx.annotation.DrawableRes;
import androidx.appcompat.content.res.AppCompatResources;
import com.amulyakhare.textdrawable.TextDrawable;
import com.makeramen.roundedimageview.RoundedDrawable;
@@ -31,7 +32,7 @@ public class ResourceContactPhoto implements FallbackContactPhoto {
@Override
public Drawable asDrawable(Context context, int color, boolean inverted) {
Drawable background = TextDrawable.builder().buildRound(" ", inverted ? Color.WHITE : color);
RoundedDrawable foreground = (RoundedDrawable) RoundedDrawable.fromDrawable(context.getResources().getDrawable(resourceId));
RoundedDrawable foreground = (RoundedDrawable) RoundedDrawable.fromDrawable(AppCompatResources.getDrawable(context, resourceId));
foreground.setScaleType(ImageView.ScaleType.CENTER_CROP);
@@ -39,8 +40,10 @@ public class ResourceContactPhoto implements FallbackContactPhoto {
foreground.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
Drawable gradient = context.getResources().getDrawable(ThemeUtil.isDarkTheme(context) ? R.drawable.avatar_gradient_dark
: R.drawable.avatar_gradient_light);
Drawable gradient = AppCompatResources.getDrawable(
context,
ThemeUtil.isDarkTheme(context) ? R.drawable.avatar_gradient_dark : R.drawable.avatar_gradient_light
);
return new ExpandingLayerDrawable(new Drawable[] {background, foreground, gradient});
}

View File

@@ -120,7 +120,9 @@ interface StorageProtocol {
fun markAsSyncing(timestamp: Long, author: String)
fun markAsSending(timestamp: Long, author: String)
fun markAsSent(timestamp: Long, author: String)
fun markAsSentToCommunity(threadID: Long, messageID: Long)
fun markUnidentified(timestamp: Long, author: String)
fun markUnidentifiedInCommunity(threadID: Long, messageID: Long)
fun markAsSyncFailed(timestamp: Long, author: String, error: Exception)
fun markAsSentFailed(timestamp: Long, author: String, error: Exception)
fun clearErrorMessage(messageID: Long)

View File

@@ -77,7 +77,7 @@ class Contact(val sessionID: String) {
companion object {
fun contextForRecipient(recipient: Recipient): ContactContext {
return if (recipient.isOpenGroupRecipient) ContactContext.OPEN_GROUP else ContactContext.REGULAR
return if (recipient.isCommunityRecipient) ContactContext.OPEN_GROUP else ContactContext.REGULAR
}
}
}

View File

@@ -22,7 +22,7 @@ class OpenGroupDeleteJob(private val messageServerIds: LongArray, private val th
override suspend fun execute(dispatcherName: String) {
val dataProvider = MessagingModuleConfiguration.shared.messageDataProvider
val numberToDelete = messageServerIds.size
Log.d(TAG, "Deleting $numberToDelete messages")
Log.d(TAG, "About to attempt to delete $numberToDelete messages")
// FIXME: This entire process should probably run in a transaction (with the attachment deletion happening only if it succeeded)
try {
@@ -42,6 +42,7 @@ class OpenGroupDeleteJob(private val messageServerIds: LongArray, private val th
delegate?.handleJobSucceeded(this, dispatcherName)
}
catch (e: Exception) {
Log.w(TAG, "OpenGroupDeleteJob failed: $e")
delegate?.handleJobFailed(this, dispatcherName, e)
}
}

View File

@@ -2,7 +2,7 @@ package org.session.libsession.messaging.mentions
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.contacts.Contact
import java.util.*
import java.util.Locale
object MentionsManager {
var userPublicKeyCache = mutableMapOf<Long, Set<String>>() // Thread ID to set of user hex encoded public keys

View File

@@ -43,14 +43,14 @@ sealed class Destination {
val groupPublicKey = GroupUtil.doubleDecodeGroupID(groupID).toHexString()
ClosedGroup(groupPublicKey)
}
address.isOpenGroup -> {
address.isCommunity -> {
val storage = MessagingModuleConfiguration.shared.storage
val threadID = storage.getThreadId(address)!!
storage.getOpenGroup(threadID)?.let {
OpenGroup(roomToken = it.room, server = it.server, fileIds = fileIds)
} ?: throw Exception("Missing open group for thread with ID: $threadID.")
}
address.isOpenGroupInbox -> {
address.isCommunityInbox -> {
val groupInboxId = GroupUtil.getDecodedGroupID(address.serialize()).split("!")
OpenGroupInbox(
groupInboxId.dropLast(2).joinToString("!"),

View File

@@ -1,7 +1,6 @@
package org.session.libsession.messaging.messages
import network.loki.messenger.libsession_util.util.ExpiryMode
import org.session.libsession.snode.SnodeAPI
data class ExpirationConfiguration(
val threadId: Long = -1,
@@ -11,7 +10,7 @@ data class ExpirationConfiguration(
val isEnabled = expiryMode.expirySeconds > 0
companion object {
val isNewConfigEnabled = SnodeAPI.nowWithOffset >= 1710284400000
val isNewConfigEnabled = true
}
}

View File

@@ -4,10 +4,11 @@ import com.google.protobuf.ByteString
import org.session.libsignal.utilities.Log
import org.session.libsignal.protos.SignalServiceProtos
class Profile() {
var displayName: String? = null
var profileKey: ByteArray? = null
class Profile(
var displayName: String? = null,
var profileKey: ByteArray? = null,
var profilePictureURL: String? = null
) {
companion object {
const val TAG = "Profile"
@@ -25,12 +26,6 @@ class Profile() {
}
}
constructor(displayName: String, profileKey: ByteArray? = null, profilePictureURL: String? = null) : this() {
this.displayName = displayName
this.profileKey = profileKey
this.profilePictureURL = profilePictureURL
}
fun toProto(): SignalServiceProtos.DataMessage? {
val displayName = displayName
if (displayName == null) {

View File

@@ -602,8 +602,7 @@ object OpenGroupApi {
// region Message Deletion
@JvmStatic
fun deleteMessage(serverID: Long, room: String, server: String): Promise<Unit, Exception> {
val request =
Request(verb = DELETE, room = room, server = server, endpoint = Endpoint.RoomMessageIndividual(room, serverID))
val request = Request(verb = DELETE, room = room, server = server, endpoint = Endpoint.RoomMessageIndividual(room, serverID))
return send(request).map {
Log.d("Loki", "Message deletion successful.")
}
@@ -659,7 +658,9 @@ object OpenGroupApi {
}
fun banAndDeleteAll(publicKey: String, room: String, server: String): Promise<Unit, Exception> {
val requests = mutableListOf<BatchRequestInfo<*>>(
// Ban request
BatchRequestInfo(
request = BatchRequest(
method = POST,
@@ -669,6 +670,7 @@ object OpenGroupApi {
endpoint = Endpoint.UserBan(publicKey),
responseType = object: TypeReference<Any>(){}
),
// Delete request
BatchRequestInfo(
request = BatchRequest(DELETE, "/room/$room/all/$publicKey"),
endpoint = Endpoint.RoomDeleteMessages(room, publicKey),

View File

@@ -39,6 +39,7 @@ import org.session.libsignal.crypto.PushTransportDetails
import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.IdPrefix
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.Namespace
import org.session.libsignal.utilities.defaultRequiresAuth
import org.session.libsignal.utilities.hasNamespaces
@@ -370,7 +371,7 @@ object MessageSender {
}
// Result Handling
fun handleSuccessfulMessageSend(message: Message, destination: Destination, isSyncMessage: Boolean = false, openGroupSentTimestamp: Long = -1) {
private fun handleSuccessfulMessageSend(message: Message, destination: Destination, isSyncMessage: Boolean = false, openGroupSentTimestamp: Long = -1) {
val storage = MessagingModuleConfiguration.shared.storage
val userPublicKey = storage.getUserPublicKey()!!
val timestamp = message.sentTimestamp!!
@@ -392,8 +393,10 @@ object MessageSender {
// in case any errors from previous sends
storage.clearErrorMessage(messageID)
// Track the open group server message ID
if (message.openGroupServerMessageID != null && (destination is Destination.LegacyOpenGroup || destination is Destination.OpenGroup)) {
val messageIsAddressedToCommunity = message.openGroupServerMessageID != null && (destination is Destination.LegacyOpenGroup || destination is Destination.OpenGroup)
if (messageIsAddressedToCommunity) {
val server: String
val room: String
when (destination) {
@@ -415,9 +418,26 @@ object MessageSender {
storage.setOpenGroupServerMessageID(messageID, message.openGroupServerMessageID!!, threadID, !(message as VisibleMessage).isMediaMessage())
}
}
// Mark the message as sent
storage.markAsSent(timestamp, userPublicKey)
storage.markUnidentified(timestamp, userPublicKey)
// Mark the message as sent.
// Note: When sending a message to a community the server modifies the message timestamp
// so when we go to look up the message in the local database by timestamp it fails and
// we're left with the message delivery status as "Sending" forever! As such, we use a
// pair of modified "markAsSentToCommunity" and "markUnidentifiedInCommunity" methods
// to retrieve the local message by thread & message ID rather than timestamp when
// handling community messages only so we can tick the delivery status over to 'Sent'.
// Fixed in: https://optf.atlassian.net/browse/SES-1567
if (messageIsAddressedToCommunity)
{
storage.markAsSentToCommunity(message.threadID!!, message.id!!)
storage.markUnidentifiedInCommunity(message.threadID!!, message.id!!)
}
else
{
storage.markAsSent(timestamp, userPublicKey)
storage.markUnidentified(timestamp, userPublicKey)
}
// Start the disappearing messages timer if needed
SSKEnvironment.shared.messageExpirationManager.maybeStartExpiration(message, startDisappearAfterRead = true)
} ?: run {

View File

@@ -10,11 +10,11 @@ import org.session.libsession.messaging.calls.CallMessageType.CALL_INCOMING
import org.session.libsession.messaging.calls.CallMessageType.CALL_MISSED
import org.session.libsession.messaging.calls.CallMessageType.CALL_OUTGOING
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.messages.ExpirationConfiguration.Companion.isNewConfigEnabled
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage.Kind.SCREENSHOT
import org.session.libsession.utilities.ExpirationUtil
import org.session.libsession.utilities.getExpirationTypeDisplayValue
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsession.utilities.truncateIdForDisplay
object UpdateMessageBuilder {
@@ -31,47 +31,35 @@ object UpdateMessageBuilder {
else getSenderName(senderId!!)
return when (updateData) {
is UpdateMessageData.Kind.GroupCreation -> 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 UpdateMessageData.Kind.GroupCreation -> {
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 UpdateMessageData.Kind.GroupNameChange -> 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 UpdateMessageData.Kind.GroupNameChange -> {
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 UpdateMessageData.Kind.GroupMemberAdded -> {
val members = updateData.updatedMembers.joinToString(", ", transform = ::getSenderName)
if (isOutgoing) {
context.getString(R.string.MessageRecord_you_added_s_to_the_group, members)
} else {
context.getString(R.string.MessageRecord_s_added_s_to_the_group, senderName, members)
}
if (isOutgoing) context.getString(R.string.MessageRecord_you_added_s_to_the_group, members)
else context.getString(R.string.MessageRecord_s_added_s_to_the_group, senderName, members)
}
is UpdateMessageData.Kind.GroupMemberRemoved -> {
val userPublicKey = storage.getUserPublicKey()!!
// 1st case: you are part of the removed members
return if (userPublicKey in updateData.updatedMembers) {
if (isOutgoing) {
context.getString(R.string.MessageRecord_left_group)
} else {
context.getString(R.string.MessageRecord_you_were_removed_from_the_group)
}
if (isOutgoing) context.getString(R.string.MessageRecord_left_group)
else context.getString(R.string.MessageRecord_you_were_removed_from_the_group)
} else {
// 2nd case: you are not part of the removed members
val members = updateData.updatedMembers.joinToString(", ", transform = ::getSenderName)
if (isOutgoing) {
context.getString(R.string.MessageRecord_you_removed_s_from_the_group, members)
} else {
context.getString(R.string.MessageRecord_s_removed_s_from_the_group, senderName, members)
}
if (isOutgoing) context.getString(R.string.MessageRecord_you_removed_s_from_the_group, members)
else context.getString(R.string.MessageRecord_s_removed_s_from_the_group, senderName, members)
}
}
is UpdateMessageData.Kind.GroupMemberLeft -> if (isOutgoing) {
context.getString(R.string.MessageRecord_left_group)
} else {
context.getString(R.string.ConversationItem_group_action_left, senderName)
is UpdateMessageData.Kind.GroupMemberLeft -> {
if (isOutgoing) context.getString(R.string.MessageRecord_left_group)
else context.getString(R.string.ConversationItem_group_action_left, senderName)
}
else -> return ""
}
@@ -80,7 +68,7 @@ object UpdateMessageBuilder {
fun buildExpirationTimerMessage(
context: Context,
duration: Long,
recipient: Recipient,
isGroup: Boolean,
senderId: String? = null,
isOutgoing: Boolean = false,
timestamp: Long,
@@ -89,44 +77,28 @@ object UpdateMessageBuilder {
if (!isOutgoing && senderId == null) return ""
val senderName = if (isOutgoing) context.getString(R.string.MessageRecord_you) else getSenderName(senderId!!)
return if (duration <= 0) {
if (isOutgoing) {
if (!isNewConfigEnabled) context.getString(R.string.MessageRecord_you_disabled_disappearing_messages)
else context.getString(if (recipient.is1on1) R.string.MessageRecord_you_turned_off_disappearing_messages_1_on_1 else R.string.MessageRecord_you_turned_off_disappearing_messages)
} else {
if (!isNewConfigEnabled) context.getString(R.string.MessageRecord_s_disabled_disappearing_messages, senderName)
else context.getString(if (recipient.is1on1) R.string.MessageRecord_s_turned_off_disappearing_messages_1_on_1 else R.string.MessageRecord_s_turned_off_disappearing_messages, senderName)
}
if (isOutgoing) context.getString(if (isGroup) R.string.MessageRecord_you_turned_off_disappearing_messages else R.string.MessageRecord_you_turned_off_disappearing_messages_1_on_1)
else context.getString(if (isGroup) R.string.MessageRecord_s_turned_off_disappearing_messages else R.string.MessageRecord_s_turned_off_disappearing_messages_1_on_1, senderName)
} else {
val time = ExpirationUtil.getExpirationDisplayValue(context, duration.toInt())
val action = context.getExpirationTypeDisplayValue(timestamp == expireStarted)
if (isOutgoing) {
if (!isNewConfigEnabled) context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time)
else context.getString(
if (recipient.is1on1) R.string.MessageRecord_you_set_messages_to_disappear_s_after_s_1_on_1 else R.string.MessageRecord_you_set_messages_to_disappear_s_after_s,
time,
action
)
} else {
if (!isNewConfigEnabled) context.getString(R.string.MessageRecord_s_set_disappearing_message_time_to_s, senderName, time)
else context.getString(
if (recipient.is1on1) R.string.MessageRecord_s_set_messages_to_disappear_s_after_s_1_on_1 else R.string.MessageRecord_s_set_messages_to_disappear_s_after_s,
senderName,
time,
action
)
}
val action = context.getExpirationTypeDisplayValue(timestamp >= expireStarted)
if (isOutgoing) context.getString(
if (isGroup) R.string.MessageRecord_you_set_messages_to_disappear_s_after_s else R.string.MessageRecord_you_set_messages_to_disappear_s_after_s_1_on_1,
time,
action
) else context.getString(
if (isGroup) R.string.MessageRecord_s_set_messages_to_disappear_s_after_s else R.string.MessageRecord_s_set_messages_to_disappear_s_after_s_1_on_1,
senderName,
time,
action
)
}
}
fun buildDataExtractionMessage(context: Context, kind: DataExtractionNotificationInfoMessage.Kind, senderId: String? = null): String {
val senderName = getSenderName(senderId!!)
return when (kind) {
DataExtractionNotificationInfoMessage.Kind.SCREENSHOT ->
context.getString(R.string.MessageRecord_s_took_a_screenshot, senderName)
DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED ->
context.getString(R.string.MessageRecord_media_saved_by_s, senderName)
}
}
fun buildDataExtractionMessage(context: Context, kind: DataExtractionNotificationInfoMessage.Kind, senderId: String? = null) = when (kind) {
SCREENSHOT -> R.string.MessageRecord_s_took_a_screenshot
MEDIA_SAVED -> R.string.MessageRecord_media_saved_by_s
}.let { context.getString(it, getSenderName(senderId!!)) }
fun buildCallMessage(context: Context, type: CallMessageType, sender: String): String =
when (type) {

View File

@@ -8,7 +8,6 @@ import androidx.annotation.VisibleForTesting
import org.session.libsignal.utilities.IdPrefix
import org.session.libsignal.utilities.Util
import org.session.libsignal.utilities.guava.Optional
import java.util.Collections
import java.util.LinkedList
import java.util.concurrent.atomic.AtomicReference
import java.util.regex.Matcher
@@ -23,17 +22,17 @@ class Address private constructor(address: String) : Parcelable, Comparable<Addr
get() = GroupUtil.isEncodedGroup(address)
val isClosedGroup: Boolean
get() = GroupUtil.isClosedGroup(address)
val isOpenGroup: Boolean
get() = GroupUtil.isOpenGroup(address)
val isOpenGroupInbox: Boolean
get() = GroupUtil.isOpenGroupInbox(address)
val isOpenGroupOutbox: Boolean
val isCommunity: Boolean
get() = GroupUtil.isCommunity(address)
val isCommunityInbox: Boolean
get() = GroupUtil.isCommunityInbox(address)
val isCommunityOutbox: Boolean
get() = address.startsWith(IdPrefix.BLINDED.value) || address.startsWith(IdPrefix.BLINDEDV2.value)
val isContact: Boolean
get() = !(isGroup || isOpenGroupInbox)
get() = !(isGroup || isCommunityInbox)
fun contactIdentifier(): String {
if (!isContact && !isOpenGroup) {
if (!isContact && !isCommunity) {
if (isGroup) throw AssertionError("Not e164, is group")
throw AssertionError("Not e164, unknown")
}
@@ -168,8 +167,9 @@ class Address private constructor(address: String) : Parcelable, Comparable<Addr
@JvmStatic
fun fromSerializedList(serialized: String, delimiter: Char): List<Address> {
val escapedAddresses = DelimiterUtil.split(serialized, delimiter)
val set = escapedAddresses.toSet().sorted()
val addresses: MutableList<Address> = LinkedList()
for (escapedAddress in escapedAddresses) {
for (escapedAddress in set) {
addresses.add(fromSerialized(DelimiterUtil.unescape(escapedAddress, delimiter)))
}
return addresses
@@ -177,9 +177,9 @@ class Address private constructor(address: String) : Parcelable, Comparable<Addr
@JvmStatic
fun toSerializedList(addresses: List<Address>, delimiter: Char): String {
Collections.sort(addresses)
val set = addresses.toSet().sorted()
val escapedAddresses: MutableList<String> = LinkedList()
for (address in addresses) {
for (address in set) {
escapedAddresses.add(DelimiterUtil.escape(address.serialize(), delimiter))
}
return Util.join(escapedAddresses, delimiter.toString() + "")

View File

@@ -22,7 +22,7 @@ class GroupRecord(
}
val isOpenGroup: Boolean
get() = Address.fromSerialized(encodedId).isOpenGroup
get() = Address.fromSerialized(encodedId).isCommunity
val isClosedGroup: Boolean
get() = Address.fromSerialized(encodedId).isClosedGroup

View File

@@ -8,12 +8,12 @@ import java.io.IOException
object GroupUtil {
const val CLOSED_GROUP_PREFIX = "__textsecure_group__!"
const val OPEN_GROUP_PREFIX = "__loki_public_chat_group__!"
const val OPEN_GROUP_INBOX_PREFIX = "__open_group_inbox__!"
const val COMMUNITY_PREFIX = "__loki_public_chat_group__!"
const val COMMUNITY_INBOX_PREFIX = "__open_group_inbox__!"
@JvmStatic
fun getEncodedOpenGroupID(groupID: ByteArray): String {
return OPEN_GROUP_PREFIX + Hex.toStringCondensed(groupID)
return COMMUNITY_PREFIX + Hex.toStringCondensed(groupID)
}
@JvmStatic
@@ -25,7 +25,7 @@ object GroupUtil {
@JvmStatic
fun getEncodedOpenGroupInboxID(groupInboxID: ByteArray): Address {
return Address.fromSerialized(OPEN_GROUP_INBOX_PREFIX + Hex.toStringCondensed(groupInboxID))
return Address.fromSerialized(COMMUNITY_INBOX_PREFIX + Hex.toStringCondensed(groupInboxID))
}
@JvmStatic
@@ -69,17 +69,17 @@ object GroupUtil {
}
fun isEncodedGroup(groupId: String): Boolean {
return groupId.startsWith(CLOSED_GROUP_PREFIX) || groupId.startsWith(OPEN_GROUP_PREFIX)
return groupId.startsWith(CLOSED_GROUP_PREFIX) || groupId.startsWith(COMMUNITY_PREFIX)
}
@JvmStatic
fun isOpenGroup(groupId: String): Boolean {
return groupId.startsWith(OPEN_GROUP_PREFIX)
fun isCommunity(groupId: String): Boolean {
return groupId.startsWith(COMMUNITY_PREFIX)
}
@JvmStatic
fun isOpenGroupInbox(groupId: String): Boolean {
return groupId.startsWith(OPEN_GROUP_INBOX_PREFIX)
fun isCommunityInbox(groupId: String): Boolean {
return groupId.startsWith(COMMUNITY_INBOX_PREFIX)
}
@JvmStatic

View File

@@ -842,7 +842,7 @@ interface TextSecurePreferences {
getDefaultSharedPreferences(context).edit().putString(key, value).apply()
}
private fun getIntegerPreference(context: Context, key: String, defaultValue: Int): Int {
fun getIntegerPreference(context: Context, key: String, defaultValue: Int): Int {
return getDefaultSharedPreferences(context).getInt(key, defaultValue)
}

View File

@@ -27,6 +27,10 @@ public class ThemeUtil {
return getAttributeText(context, R.attr.theme_type, "light").equals("dark");
}
public static boolean isLightTheme(@NonNull Context context) {
return getAttributeText(context, R.attr.theme_type, "light").equals("light");
}
public static boolean getThemedBoolean(@NonNull Context context, @AttrRes int attr) {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();

View File

@@ -459,16 +459,16 @@ public class Recipient implements RecipientModifiedListener {
}
public boolean is1on1() { return address.isContact() && !isLocalNumber; }
public boolean isOpenGroupRecipient() {
return address.isOpenGroup();
public boolean isCommunityRecipient() {
return address.isCommunity();
}
public boolean isOpenGroupOutboxRecipient() {
return address.isOpenGroupOutbox();
return address.isCommunityOutbox();
}
public boolean isOpenGroupInboxRecipient() {
return address.isOpenGroupInbox();
return address.isCommunityInbox();
}
public boolean isClosedGroupRecipient() {

View File

@@ -15,10 +15,6 @@
<string name="MessageRecord_s_called_you">%s vous a appelé·e</string>
<string name="MessageRecord_called_s">Vous avez appelé %s</string>
<string name="MessageRecord_missed_call_from">Appel manqué de %s</string>
<string name="MessageRecord_you_disabled_disappearing_messages">Vous avez désactivé les messages éphémères.</string>
<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 enregistré le média.</string>
<!-- expiration -->

View File

@@ -15,10 +15,6 @@
<string name="MessageRecord_s_called_you">%s vous a appelé·e</string>
<string name="MessageRecord_called_s">Vous avez appelé %s</string>
<string name="MessageRecord_missed_call_from">Appel manqué de %s</string>
<string name="MessageRecord_you_disabled_disappearing_messages">Vous avez désactivé les messages éphémères.</string>
<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 enregistré le média.</string>
<!-- expiration -->

View File

@@ -17,17 +17,13 @@
<string name="MessageRecord_missed_call_from">Missed call from %s</string>
<string name="MessageRecord_follow_setting">Follow Setting</string>
<string name="AccessibilityId_follow_setting">Follow setting</string>
<string name="MessageRecord_you_disabled_disappearing_messages">You disabled disappearing messages.</string>
<string name="MessageRecord_you_turned_off_disappearing_messages">You have turned off disappearing messages.</string>
<string name="MessageRecord_you_turned_off_disappearing_messages_1_on_1">You turned off disappearing messages. Messages you send will no longer disappear.</string>
<string name="MessageRecord_s_disabled_disappearing_messages">%1$s disabled disappearing messages.</string>
<string name="MessageRecord_s_turned_off_disappearing_messages">%1$s turned off disappearing messages.</string>
<string name="MessageRecord_s_turned_off_disappearing_messages_1_on_1">%1$s has turned off disappearing messages. Messages they send will no longer disappear.</string>
<string name="MessageRecord_you_set_disappearing_message_time_to_s">You set the disappearing message timer to %1$s</string>
<string name="MessageRecord_you_set_messages_to_disappear_s_after_s">You have set messages to disappear %1$s after they have been %2$s</string>
<string name="MessageRecord_you_set_messages_to_disappear_s_after_s_1_on_1">You set your messages to disappear %1$s after they have been %2$s.</string>
<string name="MessageRecord_you_changed_messages_to_disappear_s_after_s">You have changed messages to disappear %1$s after they have been %2$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_set_messages_to_disappear_s_after_s">%1$s has set messages to disappear %2$s after they have been %3$s</string>
<string name="MessageRecord_s_set_messages_to_disappear_s_after_s_1_on_1">%1$s has set their messages to disappear %2$s after they have been %3$s.</string>
<string name="MessageRecord_s_changed_messages_to_disappear_s_after_s">%1$s has changed messages to disappear %2$s after they have been %3$s</string>