Moved into libsession for ease of access to control message view creation

This commit is contained in:
alansley 2024-08-26 17:11:45 +10:00
parent b908a54a44
commit 3c576053a3
6 changed files with 56 additions and 46 deletions

View File

@ -227,7 +227,6 @@ dependencies {
ksp("com.google.dagger:hilt-compiler:$daggerHiltVersion") ksp("com.google.dagger:hilt-compiler:$daggerHiltVersion")
ksp("com.github.bumptech.glide:ksp:$glideVersion") ksp("com.github.bumptech.glide:ksp:$glideVersion")
implementation 'androidx.compose.material3:material3-android:1.2.1'
implementation("com.google.dagger:hilt-android:$daggerHiltVersion") implementation("com.google.dagger:hilt-android:$daggerHiltVersion")
implementation "androidx.appcompat:appcompat:$appcompatVersion" implementation "androidx.appcompat:appcompat:$appcompatVersion"
implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.recyclerview:recyclerview:1.2.1'

View File

@ -1072,24 +1072,6 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
updateUnreadCountIndicator() updateUnreadCountIndicator()
} }
// Method that takes a char sequence that contains one or more elements surrounded in bold tags
// like "Hello <b>world</b>" and returns a SpannableString that will display the appropriate
// elements in bold. If there are no such <b> or </b> elements then the original string is returned
// as a SpannableString without any bold highlighting.
private fun makeBoldBetweenTags(input: CharSequence): SpannableString {
val spannable = SpannableString(input)
var startIndex = 0
while (true) {
startIndex = input.indexOf("<b>", startIndex)
if (startIndex == -1) break
val endIndex = input.indexOf("</b>", startIndex + 3)
if (endIndex == -1) break
spannable.setSpan(StyleSpan(Typeface.BOLD),startIndex + 3, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
startIndex = endIndex + 4
}
return spannable
}
// Update placeholder / control messages in a conversation // Update placeholder / control messages in a conversation
private fun updatePlaceholder() { private fun updatePlaceholder() {
val recipient = viewModel.recipient ?: return Log.w("Loki", "recipient was null in placeholder update") val recipient = viewModel.recipient ?: return Log.w("Loki", "recipient was null in placeholder update")
@ -1134,7 +1116,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
binding.placeholderText.isVisible = showPlaceholder binding.placeholderText.isVisible = showPlaceholder
if (showPlaceholder) { if (showPlaceholder) {
if (!isNoteToSelf) { if (!isNoteToSelf) {
binding.placeholderText.text = makeBoldBetweenTags(txtCS) binding.placeholderText.text = org.session.libsession.utilities.Util.makeBoldBetweenTags(txtCS)
} else { } else {
binding.placeholderText.text = txtCS binding.placeholderText.text = txtCS
} }

View File

@ -1,9 +1,13 @@
package org.thoughtcrime.securesms.conversation.v2.utilities package org.thoughtcrime.securesms.conversation.v2.utilities
import android.graphics.Rect import android.graphics.Rect
import android.graphics.Typeface
import android.text.Layout import android.text.Layout
import android.text.SpannableString
import android.text.Spanned
import android.text.StaticLayout import android.text.StaticLayout
import android.text.TextPaint import android.text.TextPaint
import android.text.style.StyleSpan
import android.view.MotionEvent import android.view.MotionEvent
import android.widget.TextView import android.widget.TextView
import androidx.core.text.getSpans import androidx.core.text.getSpans

View File

@ -78,7 +78,7 @@ class SaveAttachmentTask @JvmOverloads constructor(context: Context, count: Int
// for unknown reasons it provides us with an empty filename when saving files. // for unknown reasons it provides us with an empty filename when saving files.
// TODO: Further investigation into root cause and fix! // TODO: Further investigation into root cause and fix!
if (fileName.isNullOrEmpty()) fileName = generateOutputFileName(contentType, attachment.date) if (fileName.isNullOrEmpty()) fileName = generateOutputFileName(contentType, attachment.date)
fileName = sanitizeOutputFileName(fileName) fileName = sanitizeOutputFileName(fileName)
val outputUri: Uri = getMediaStoreContentUriForType(contentType) val outputUri: Uri = getMediaStoreContentUriForType(contentType)
val mediaUri = createOutputUri(context, outputUri, contentType, fileName) val mediaUri = createOutputUri(context, outputUri, contentType, fileName)

View File

@ -1,6 +1,7 @@
package org.session.libsession.messaging.utilities package org.session.libsession.messaging.utilities
import android.content.Context import android.content.Context
import android.text.SpannableString
import com.squareup.phrase.Phrase import com.squareup.phrase.Phrase
import org.session.libsession.R import org.session.libsession.R
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
@ -23,6 +24,7 @@ import org.session.libsession.utilities.StringSubstitutionConstants.GROUP_NAME_K
import org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY import org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY
import org.session.libsession.utilities.StringSubstitutionConstants.OTHER_NAME_KEY import org.session.libsession.utilities.StringSubstitutionConstants.OTHER_NAME_KEY
import org.session.libsession.utilities.StringSubstitutionConstants.TIME_KEY import org.session.libsession.utilities.StringSubstitutionConstants.TIME_KEY
import org.session.libsession.utilities.Util
object UpdateMessageBuilder { object UpdateMessageBuilder {
const val TAG = "libsession" const val TAG = "libsession"
@ -33,29 +35,34 @@ object UpdateMessageBuilder {
?.displayName(Contact.ContactContext.REGULAR) ?.displayName(Contact.ContactContext.REGULAR)
?: truncateIdForDisplay(senderId) ?: truncateIdForDisplay(senderId)
fun buildGroupUpdateMessage(context: Context, updateMessageData: UpdateMessageData, senderId: String? = null, isOutgoing: Boolean = false): String { //@RequiresApi(Build.VERSION_CODES.P)
fun buildGroupUpdateMessage(context: Context, updateMessageData: UpdateMessageData, senderId: String? = null, isOutgoing: Boolean = false): CharSequence {
val updateData = updateMessageData.kind val updateData = updateMessageData.kind
if (updateData == null || !isOutgoing && senderId == null) return "" if (updateData == null || !isOutgoing && senderId == null) return ""
val senderName: String = if (isOutgoing) context.getString(R.string.you) else getSenderName(senderId!!)
return when (updateData) { return when (updateData) {
// --- Group created or joined --- // --- Group created or joined ---
is UpdateMessageData.Kind.GroupCreation -> { is UpdateMessageData.Kind.GroupCreation -> {
if (!isOutgoing) context.getString(R.string.groupInviteYou) if (!isOutgoing) {
else "" // We no longer add a string like `disappearingMessagesNewGroup` ("You created a new group") and leave the group with its default empty state Util.makeBoldBetweenTags(SpannableString(context.getString(R.string.groupInviteYou)))
} else {
"" // We no longer add a string like `disappearingMessagesNewGroup` ("You created a new group") and leave the group with its default empty state
}
} }
// --- Group name changed --- // --- Group name changed ---
is UpdateMessageData.Kind.GroupNameChange -> { is UpdateMessageData.Kind.GroupNameChange -> {
if (isOutgoing) { if (isOutgoing) {
Phrase.from(context, R.string.groupNameNew) val cs = Phrase.from(context, R.string.groupNameNew)
.put(GROUP_NAME_KEY, updateData.name) .put(GROUP_NAME_KEY, updateData.name)
.format().toString() .format()
Util.makeBoldBetweenTags(cs)
} }
else { else {
Phrase.from(context, R.string.groupNameNew) val cs = Phrase.from(context, R.string.groupNameNew)
.put(GROUP_NAME_KEY, updateData.name) .put(GROUP_NAME_KEY, updateData.name)
.format().toString() .format()
Util.makeBoldBetweenTags(cs)
} }
} }
@ -74,20 +81,20 @@ object UpdateMessageBuilder {
1 -> { 1 -> {
Phrase.from(context, R.string.groupMemberNew) Phrase.from(context, R.string.groupMemberNew)
.put(NAME_KEY, updateData.updatedMembers.elementAtOrNull(0)) .put(NAME_KEY, updateData.updatedMembers.elementAtOrNull(0))
.format().toString() .format()
} }
2 -> { 2 -> {
Phrase.from(context, R.string.groupMemberTwoNew) Phrase.from(context, R.string.groupMemberTwoNew)
.put(NAME_KEY, updateData.updatedMembers.elementAtOrNull(0)) .put(NAME_KEY, updateData.updatedMembers.elementAtOrNull(0))
.put(OTHER_NAME_KEY, updateData.updatedMembers.elementAtOrNull(1)) .put(OTHER_NAME_KEY, updateData.updatedMembers.elementAtOrNull(1))
.format().toString() .format()
} }
else -> { else -> {
val newMemberCountMinusOne = newMemberCount - 1 val newMemberCountMinusOne = newMemberCount - 1
Phrase.from(context, R.string.groupMemberMoreNew) Phrase.from(context, R.string.groupMemberMoreNew)
.put(NAME_KEY, updateData.updatedMembers.elementAtOrNull(0)) .put(NAME_KEY, updateData.updatedMembers.elementAtOrNull(0))
.put(COUNT_KEY, newMemberCountMinusOne) .put(COUNT_KEY, newMemberCountMinusOne)
.format().toString() .format()
} }
} }
} }
@ -98,15 +105,13 @@ object UpdateMessageBuilder {
// 1st case: you are part of the removed members // 1st case: you are part of the removed members
return if (userPublicKey in updateData.updatedMembers) { return if (userPublicKey in updateData.updatedMembers) {
if (isOutgoing) context.getString(R.string.groupMemberYouLeft) // You chose to leave if (isOutgoing) context.getString(R.string.groupMemberYouLeft) // You chose to leave
else Phrase.from(context, R.string.groupRemovedYou) // You were forced to leave else Phrase.from(context, R.string.groupRemovedYou) // You were forced to leave
.put(GROUP_NAME_KEY, updateData.groupName) .put(GROUP_NAME_KEY, updateData.groupName)
.format().toString() .format()
} }
else // 2nd case: you are not part of the removed members else // 2nd case: you are not part of the removed members
{ {
val members = updateData.updatedMembers.joinToString(", ", transform = ::getSenderName)
// a.) You are the person doing the removing of one or more members // a.) You are the person doing the removing of one or more members
if (isOutgoing) { if (isOutgoing) {
when (updateData.updatedMembers.size) { when (updateData.updatedMembers.size) {
@ -116,15 +121,15 @@ object UpdateMessageBuilder {
} }
1 -> Phrase.from(context, R.string.groupRemoved) 1 -> Phrase.from(context, R.string.groupRemoved)
.put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0))) .put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0)))
.format().toString() .format()
2 -> Phrase.from(context, R.string.groupRemovedTwo) 2 -> Phrase.from(context, R.string.groupRemovedTwo)
.put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0))) .put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0)))
.put(OTHER_NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(1))) .put(OTHER_NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(1)))
.format().toString() .format()
else -> Phrase.from(context, R.string.groupRemovedMore) else -> Phrase.from(context, R.string.groupRemovedMore)
.put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0))) .put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0)))
.put(COUNT_KEY, updateData.updatedMembers.size - 1) .put(COUNT_KEY, updateData.updatedMembers.size - 1)
.format().toString() .format()
} }
} }
else // b.) Someone else is the person doing the removing of one or more members else // b.) Someone else is the person doing the removing of one or more members
@ -140,15 +145,15 @@ object UpdateMessageBuilder {
} }
1 -> Phrase.from(context, R.string.groupRemoved) 1 -> Phrase.from(context, R.string.groupRemoved)
.put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0))) .put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0)))
.format().toString() .format()
2 -> Phrase.from(context, R.string.groupRemovedTwo) 2 -> Phrase.from(context, R.string.groupRemovedTwo)
.put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0))) .put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0)))
.put(OTHER_NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(1))) .put(OTHER_NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(1)))
.format().toString() .format()
else -> Phrase.from(context, R.string.groupRemovedMore) else -> Phrase.from(context, R.string.groupRemovedMore)
.put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0))) .put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0)))
.put(COUNT_KEY, updateData.updatedMembers.size - 1) .put(COUNT_KEY, updateData.updatedMembers.size - 1)
.format().toString() .format()
} }
} }
} }
@ -165,15 +170,15 @@ object UpdateMessageBuilder {
} }
1 -> Phrase.from(context, R.string.groupMemberLeft) 1 -> Phrase.from(context, R.string.groupMemberLeft)
.put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0))) .put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0)))
.format().toString() .format()
2 -> Phrase.from(context, R.string.groupMemberLeftTwo) 2 -> Phrase.from(context, R.string.groupMemberLeftTwo)
.put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0))) .put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0)))
.put(OTHER_NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(1))) .put(OTHER_NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(1)))
.format().toString() .format()
else -> Phrase.from(context, R.string.groupMemberLeftMore) else -> Phrase.from(context, R.string.groupMemberLeftMore)
.put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0))) .put(NAME_KEY, getSenderName(updateData.updatedMembers.elementAt(0)))
.put(COUNT_KEY, updateData.updatedMembers.size - 1) .put(COUNT_KEY, updateData.updatedMembers.size - 1)
.format().toString() .format()
} }
} }
} }

View File

@ -9,6 +9,7 @@ import android.os.Looper
import android.provider.Telephony import android.provider.Telephony
import android.text.Spannable import android.text.Spannable
import android.text.SpannableString import android.text.SpannableString
import android.text.Spanned
import android.text.TextUtils import android.text.TextUtils
import android.text.style.StyleSpan import android.text.style.StyleSpan
import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Log
@ -358,6 +359,25 @@ object Util {
val digitGroups = (Math.log10(sizeBytes.toDouble()) / Math.log10(1024.0)).toInt() val digitGroups = (Math.log10(sizeBytes.toDouble()) / Math.log10(1024.0)).toInt()
return DecimalFormat("#,##0.#").format(sizeBytes / Math.pow(1024.0, digitGroups.toDouble())) + " " + units[digitGroups] return DecimalFormat("#,##0.#").format(sizeBytes / Math.pow(1024.0, digitGroups.toDouble())) + " " + units[digitGroups]
} }
// Method that takes a char sequence that contains one or more elements surrounded in bold tags
// like "Hello <b>world</b>" and returns a SpannableString that will display the appropriate
// elements in bold. If there are no such <b> or </b> elements then the original string is returned
// as a SpannableString without any bold highlighting.
@JvmStatic
fun makeBoldBetweenTags(input: CharSequence): SpannableString {
val spannable = SpannableString(input)
var startIndex = 0
while (true) {
startIndex = input.indexOf("<b>", startIndex)
if (startIndex == -1) break
val endIndex = input.indexOf("</b>", startIndex + 3)
if (endIndex == -1) break
spannable.setSpan(StyleSpan(Typeface.BOLD),startIndex + 3, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
startIndex = endIndex + 4
}
return spannable
}
} }
fun <T, R> T.runIf(condition: Boolean, block: T.() -> R): R where T: R = if (condition) block() else this fun <T, R> T.runIf(condition: Boolean, block: T.() -> R): R where T: R = if (condition) block() else this