Fix Search

This commit is contained in:
bemusementpark 2024-07-04 14:48:31 +09:30
parent ca66d115a3
commit a4ee521ee0
6 changed files with 65 additions and 124 deletions

View File

@ -8,30 +8,9 @@ import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.provider.Telephony.Mms.Addr
import android.widget.Toast
import androidx.activity.viewModels
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.width
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
@ -56,11 +35,9 @@ import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.snode.SnodeAPI
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.AppTextSecurePreferences
import org.session.libsession.utilities.GroupUtil
import org.session.libsession.utilities.ProfilePictureModifiedEvent
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.groupByNotNull
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.ThreadUtils
@ -87,25 +64,12 @@ import org.thoughtcrime.securesms.messagerequests.MessageRequestsActivity
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.notifications.PushRegistry
import org.thoughtcrime.securesms.recoverypassword.RecoveryPasswordActivity
import org.thoughtcrime.securesms.permissions.Permissions
import org.thoughtcrime.securesms.preferences.SettingsActivity
import org.thoughtcrime.securesms.recoverypassword.RecoveryPasswordActivity
import org.thoughtcrime.securesms.showMuteDialog
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.ui.Divider
import org.thoughtcrime.securesms.ui.LocalDimensions
import org.thoughtcrime.securesms.ui.color.LocalColors
import org.thoughtcrime.securesms.ui.color.Colors
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.SessionColorsParameterProvider
import org.thoughtcrime.securesms.ui.SessionShieldIcon
import org.thoughtcrime.securesms.ui.base
import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.h4
import org.thoughtcrime.securesms.ui.h8
import org.thoughtcrime.securesms.ui.setThemedContent
import org.thoughtcrime.securesms.ui.small
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
import org.thoughtcrime.securesms.util.IP2Country
import org.thoughtcrime.securesms.util.disableClipping
@ -147,41 +111,27 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
private val globalSearchAdapter = GlobalSearchAdapter { model ->
when (model) {
is GlobalSearchAdapter.Model.Message -> {
val threadId = model.messageResult.threadId
val timestamp = model.messageResult.sentTimestampMs
val author = model.messageResult.messageRecipient.address
val intent = Intent(this, ConversationActivityV2::class.java)
intent.putExtra(ConversationActivityV2.THREAD_ID, threadId)
intent.putExtra(ConversationActivityV2.SCROLL_MESSAGE_ID, timestamp)
intent.putExtra(ConversationActivityV2.SCROLL_MESSAGE_AUTHOR, author)
push(intent)
}
is GlobalSearchAdapter.Model.SavedMessages -> {
val intent = Intent(this, ConversationActivityV2::class.java)
intent.putExtra(ConversationActivityV2.ADDRESS, Address.fromSerialized(model.currentUserPublicKey))
push(intent)
}
is GlobalSearchAdapter.Model.Contact -> {
val address = model.contact.accountID
val intent = Intent(this, ConversationActivityV2::class.java)
intent.putExtra(ConversationActivityV2.ADDRESS, Address.fromSerialized(address))
push(intent)
}
is GlobalSearchAdapter.Model.GroupConversation -> {
val groupAddress = Address.fromSerialized(model.groupRecord.encodedId)
val threadId = threadDb.getThreadIdIfExistsFor(Recipient.from(this, groupAddress, false))
if (threadId >= 0) {
val intent = Intent(this, ConversationActivityV2::class.java)
intent.putExtra(ConversationActivityV2.THREAD_ID, threadId)
push(intent)
is GlobalSearchAdapter.Model.Message -> push<ConversationActivityV2> {
model.messageResult.run {
putExtra(ConversationActivityV2.THREAD_ID, threadId)
putExtra(ConversationActivityV2.SCROLL_MESSAGE_ID, sentTimestampMs)
putExtra(ConversationActivityV2.SCROLL_MESSAGE_AUTHOR, messageRecipient.address)
}
}
else -> {
Log.d("Loki", "callback with model: $model")
is GlobalSearchAdapter.Model.SavedMessages -> push<ConversationActivityV2> {
putExtra(ConversationActivityV2.ADDRESS, Address.fromSerialized(model.currentUserPublicKey))
}
is GlobalSearchAdapter.Model.Contact -> push<ConversationActivityV2> {
putExtra(ConversationActivityV2.ADDRESS, model.contact.accountID.let(Address::fromSerialized))
}
is GlobalSearchAdapter.Model.GroupConversation -> model.groupRecord.encodedId
.let { Recipient.from(this, Address.fromSerialized(it), false) }
.let(threadDb::getThreadIdIfExistsFor)
.takeIf { it >= 0 }
?.let {
push<ConversationActivityV2> { putExtra(ConversationActivityV2.THREAD_ID, it) }
}
else -> Log.d("Loki", "callback with model: $model")
}
}
@ -321,24 +271,14 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
.flatMap { (key, contacts) ->
listOf(
GlobalSearchAdapter.Model.SubHeader(key)
) + contacts.sortedBy { it.name ?: it.value.accountID }.map { it.value }.map(GlobalSearchAdapter.Model::Contact)
) + contacts.sortedBy { it.name ?: it.value.accountID }.map { it.value }.map { GlobalSearchAdapter.Model.Contact(it, it.accountID == publicKey) }
}
} else {
val currentUserPublicKey = publicKey
val contactAndGroupList = result.contacts.map(GlobalSearchAdapter.Model::Contact) +
val contactAndGroupList = result.contacts.map { GlobalSearchAdapter.Model.Contact(it, it.accountID == publicKey) } +
result.threads.map(GlobalSearchAdapter.Model::GroupConversation)
val contactResults = contactAndGroupList.toMutableList()
if (contactResults.isEmpty()) {
contactResults.add(GlobalSearchAdapter.Model.SavedMessages(currentUserPublicKey))
}
val userIndex = contactResults.indexOfFirst { it is GlobalSearchAdapter.Model.Contact && it.contact.accountID == currentUserPublicKey }
if (userIndex >= 0) {
contactResults[userIndex] = GlobalSearchAdapter.Model.SavedMessages(currentUserPublicKey)
}
if (contactResults.isNotEmpty()) {
contactResults.add(0, GlobalSearchAdapter.Model.Header(R.string.conversations))
}
@ -348,7 +288,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
.associateWith { mmsSmsDatabase.getUnreadCount(it) }
val messageResults: MutableList<GlobalSearchAdapter.Model> = result.messages
.map { GlobalSearchAdapter.Model.Message(it, unreadThreadMap[it.threadId] ?: 0) }
.map { GlobalSearchAdapter.Model.Message(it, unreadThreadMap[it.threadId] ?: 0, it.conversationRecipient.isLocalNumber) }
.toMutableList()
if (messageResults.isNotEmpty()) {

View File

@ -17,7 +17,7 @@ import org.thoughtcrime.securesms.ui.GetString
import java.security.InvalidParameterException
import org.session.libsession.messaging.contacts.Contact as ContactModel
class GlobalSearchAdapter (private val modelCallback: (Model)->Unit): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
class GlobalSearchAdapter(private val modelCallback: (Model)->Unit): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
const val HEADER_VIEW_TYPE = 0
@ -122,15 +122,8 @@ class GlobalSearchAdapter (private val modelCallback: (Model)->Unit): RecyclerVi
}
binding.root.setOnClickListener { modelCallback(model) }
}
}
data class MessageModel(
val threadRecipient: Recipient,
val messageRecipient: Recipient,
val messageSnippet: String
)
sealed class Model {
data class Header(val title: GetString): Model() {
constructor(@StringRes title: Int): this(GetString(title))
@ -141,8 +134,8 @@ class GlobalSearchAdapter (private val modelCallback: (Model)->Unit): RecyclerVi
constructor(title: String): this(GetString(title))
}
data class SavedMessages(val currentUserPublicKey: String): Model()
data class Contact(val contact: ContactModel): Model()
data class Contact(val contact: ContactModel, val isSelf: Boolean): Model()
data class GroupConversation(val groupRecord: GroupRecord): Model()
data class Message(val messageResult: MessageResult, val unread: Int): Model()
data class Message(val messageResult: MessageResult, val unread: Int, val isSelf: Boolean): Model()
}
}

View File

@ -65,7 +65,7 @@ fun ContentView.bindQuery(query: String, model: GlobalSearchAdapter.Model) {
))
binding.searchResultSubtitle.text = textSpannable
binding.searchResultSubtitle.isVisible = true
binding.searchResultTitle.text = model.messageResult.conversationRecipient.toShortString()
binding.searchResultTitle.text = model.messageResult.conversationRecipient.getSearchName()
}
is GroupConversation -> {
binding.searchResultTitle.text = getHighlight(
@ -74,8 +74,7 @@ fun ContentView.bindQuery(query: String, model: GlobalSearchAdapter.Model) {
)
val membersString = model.groupRecord.members.joinToString { address ->
val recipient = Recipient.from(binding.root.context, address, false)
recipient.name ?: "${address.serialize().take(4)}...${address.serialize().takeLast(4)}"
Recipient.from(binding.root.context, address, false).getSearchName()
}
binding.searchResultSubtitle.text = getHighlight(query, membersString)
}
@ -106,16 +105,16 @@ fun ContentView.bindModel(query: String?, model: GroupConversation) {
}
}
fun ContentView.bindModel(query: String?, model: ContactModel) {
binding.searchResultProfilePicture.isVisible = true
binding.searchResultSubtitle.isVisible = false
binding.searchResultTimestamp.isVisible = false
binding.searchResultSubtitle.text = null
val recipient =
Recipient.from(binding.root.context, Address.fromSerialized(model.contact.accountID), false)
binding.searchResultProfilePicture.update(recipient)
val nameString = model.contact.getSearchName()
binding.searchResultTitle.text = getHighlight(query, nameString)
fun ContentView.bindModel(query: String?, model: ContactModel) = binding.run {
searchResultProfilePicture.isVisible = true
searchResultSubtitle.isVisible = false
searchResultTimestamp.isVisible = false
searchResultSubtitle.text = null
val recipient = Recipient.from(root.context, Address.fromSerialized(model.contact.accountID), false)
searchResultProfilePicture.update(recipient)
val nameString = if (model.isSelf) root.context.getString(R.string.note_to_self)
else model.contact.getSearchName()
searchResultTitle.text = getHighlight(query, nameString)
}
fun ContentView.bindModel(model: SavedMessages) {
@ -126,32 +125,39 @@ fun ContentView.bindModel(model: SavedMessages) {
binding.searchResultProfilePicture.isVisible = true
}
fun ContentView.bindModel(query: String?, model: Message) {
binding.searchResultProfilePicture.isVisible = true
binding.searchResultTimestamp.isVisible = true
fun ContentView.bindModel(query: String?, model: Message) = binding.apply {
searchResultProfilePicture.isVisible = true
searchResultTimestamp.isVisible = true
// val hasUnreads = model.unread > 0
// binding.unreadCountIndicator.isVisible = hasUnreads
// unreadCountIndicator.isVisible = hasUnreads
// if (hasUnreads) {
// binding.unreadCountTextView.text = model.unread.toString()
// unreadCountTextView.text = model.unread.toString()
// }
binding.searchResultTimestamp.text = DateUtils.getDisplayFormattedTimeSpanString(binding.root.context, Locale.getDefault(), model.messageResult.sentTimestampMs)
binding.searchResultProfilePicture.update(model.messageResult.conversationRecipient)
searchResultTimestamp.text = DateUtils.getDisplayFormattedTimeSpanString(root.context, Locale.getDefault(), model.messageResult.sentTimestampMs)
searchResultProfilePicture.update(model.messageResult.conversationRecipient)
val textSpannable = SpannableStringBuilder()
if (model.messageResult.conversationRecipient != model.messageResult.messageRecipient) {
// group chat, bind
val text = "${model.messageResult.messageRecipient.getSearchName()}: "
val text = "${model.messageResult.messageRecipient.toShortString()}: "
textSpannable.append(text)
}
textSpannable.append(getHighlight(
query,
model.messageResult.bodySnippet
))
binding.searchResultSubtitle.text = textSpannable
binding.searchResultTitle.text = model.messageResult.conversationRecipient.toShortString()
binding.searchResultSubtitle.isVisible = true
searchResultSubtitle.text = textSpannable
searchResultTitle.text = if (model.isSelf) root.context.getString(R.string.note_to_self)
else model.messageResult.conversationRecipient.getSearchName()
searchResultSubtitle.isVisible = true
}
fun Recipient.getSearchName(): String = name ?: address.serialize().let(::truncateIdForDisplay)
fun Recipient.getSearchName(): String =
name?.takeIf { it.isNotEmpty() && !it.looksLikeAccountId }
?: address.serialize().let(::truncateIdForDisplay)
fun Contact.getSearchName(): String = nickname?.takeIf { it.isNotEmpty() }
?: name?.takeIf { it.isNotEmpty() } ?: truncateIdForDisplay(accountID)
fun Contact.getSearchName(): String =
nickname?.takeIf { it.isNotEmpty() && !it.looksLikeAccountId }
?: name?.takeIf { it.isNotEmpty() && !it.looksLikeAccountId }
?: truncateIdForDisplay(accountID)
private val String.looksLikeAccountId: Boolean get() = length > 60 && all { it.isDigit() || it.isLetter() }

View File

@ -57,10 +57,12 @@ class PickDisplayNameActivity : BaseActionBarActivity() {
viewModel.states.collectAsState().value,
viewModel::onChange,
viewModel::onContinue,
viewModel::dismissDialog
) { viewModel.dismissDialog(); finish() }
viewModel::dismissDialog,
quit = { viewModel.dismissDialog(); finish() }
)
}
@Deprecated("Deprecated in Java")
override fun onBackPressed() {
if (viewModel.onBackPressed()) return

View File

@ -108,5 +108,5 @@ data class ThemeState (
)
inline fun <reified T: Activity> Activity.show() = Intent(this, T::class.java).also(::startActivity).let { overridePendingTransition(R.anim.slide_from_bottom, R.anim.fade_scale_out) }
inline fun <reified T: Activity> Activity.push() = Intent(this, T::class.java).also(::startActivity).let { overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out) }
inline fun <reified T: Activity> Activity.push(modify: Intent.() -> Unit = {}) = Intent(this, T::class.java).also(modify).also(::startActivity).let { overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out) }
inline fun <reified T: Activity> Context.start(modify: Intent.() -> Unit = {}) = Intent(this, T::class.java).also(modify).apply { addFlags(FLAG_ACTIVITY_SINGLE_TOP) }.let(::startActivity)

View File

@ -1,4 +1,4 @@
package org.session.libsession.utilities
fun truncateIdForDisplay(id: String): String =
id.takeIf { it.length > 8 }?.apply{ "${take(4)}${takeLast(4)}" } ?: id
id.takeIf { it.length > 8 }?.run{ "${take(4)}${takeLast(4)}" } ?: id