Show system contact icon in more places.

This commit is contained in:
Cody Henthorne 2024-02-27 13:25:04 -05:00 committed by Alex Hart
parent c31a7152bc
commit 591d499462
13 changed files with 91 additions and 141 deletions

View file

@ -9,7 +9,6 @@ import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.SimpleEmojiTextView;
import org.thoughtcrime.securesms.recipients.Recipient;
@ -20,8 +19,6 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class FromTextView extends SimpleEmojiTextView {
private static final String TAG = Log.tag(FromTextView.class);
public FromTextView(Context context) {
super(context);
}
@ -31,22 +28,18 @@ public class FromTextView extends SimpleEmojiTextView {
}
public void setText(Recipient recipient) {
setText(recipient, true);
setText(recipient, null);
}
public void setText(Recipient recipient, boolean read) {
setText(recipient, read, null);
public void setText(Recipient recipient, @Nullable CharSequence suffix) {
setText(recipient, recipient.getDisplayName(getContext()), suffix);
}
public void setText(Recipient recipient, boolean read, @Nullable String suffix) {
setText(recipient, recipient.getDisplayName(getContext()), read, suffix);
public void setText(Recipient recipient, @Nullable CharSequence fromString, @Nullable CharSequence suffix) {
setText(recipient, fromString, suffix, true);
}
public void setText(Recipient recipient, @Nullable CharSequence fromString, boolean read, @Nullable String suffix) {
setText(recipient, fromString, read, suffix, true);
}
public void setText(Recipient recipient, @Nullable CharSequence fromString, boolean read, @Nullable String suffix, boolean asThread) {
public void setText(Recipient recipient, @Nullable CharSequence fromString, @Nullable CharSequence suffix, boolean asThread) {
SpannableStringBuilder builder = new SpannableStringBuilder();
if (asThread && recipient.isSelf()) {

View file

@ -2,11 +2,11 @@ package org.thoughtcrime.securesms.components.settings.conversation.preferences
import android.content.ClipData
import android.content.Context
import android.graphics.drawable.InsetDrawable
import android.text.SpannableStringBuilder
import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import org.signal.core.util.dp
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.settings.PreferenceModel
@ -53,17 +53,28 @@ object BioTextPreference {
}
return SpannableStringBuilder(name).apply {
var appendedToName = false
if (recipient.showVerified()) {
SpanUtil.appendCenteredImageSpan(this, ContextUtil.requireDrawable(context, R.drawable.ic_official_28), 28, 28)
appendedToName = true
} else if (recipient.isSystemContact) {
val drawable = ContextUtil.requireDrawable(context, R.drawable.symbol_person_circle_24).apply {
setTint(ContextCompat.getColor(context, R.color.signal_colorOnSurface))
}
SpanUtil.appendCenteredImageSpan(this, drawable, 24, 24)
appendedToName = true
}
if (recipient.isIndividual && !recipient.isSelf) {
val drawable = ContextUtil.requireDrawable(context, R.drawable.symbol_chevron_right_24_color_on_secondary_container)
drawable.setBounds(0, 0, 24.dp, 24.dp)
val drawable = ContextUtil.requireDrawable(context, R.drawable.symbol_chevron_right_24).apply {
setBounds(0, 0, 24.dp, 24.dp)
setTint(ContextCompat.getColor(context, R.color.signal_colorOutline))
}
val insetDrawable = InsetDrawable(drawable, 0, 0, 0, 4.dp)
SpanUtil.appendBottomImageSpan(this, insetDrawable, 24, 28)
if (!appendedToName) {
append(" ")
}
append(SpanUtil.buildCenteredImageSpan(drawable))
}
}
}

View file

@ -1,12 +1,16 @@
package org.thoughtcrime.securesms.components.settings.conversation.preferences
import android.text.SpannableStringBuilder
import android.view.View
import android.widget.TextView
import androidx.core.content.ContextCompat
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.BadgeImageView
import org.thoughtcrime.securesms.components.AvatarImageView
import org.thoughtcrime.securesms.components.settings.PreferenceModel
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.ContextUtil
import org.thoughtcrime.securesms.util.SpanUtil
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder
@ -56,7 +60,16 @@ object RecipientPreference {
name.text = if (model.recipient.isSelf) {
context.getString(R.string.Recipient_you)
} else {
model.recipient.getDisplayName(context)
if (model.recipient.isSystemContact) {
SpannableStringBuilder(model.recipient.getDisplayName(context)).apply {
val drawable = ContextUtil.requireDrawable(context, R.drawable.symbol_person_circle_24).apply {
setTint(ContextCompat.getColor(context, R.color.signal_colorOnSurface))
}
SpanUtil.appendCenteredImageSpan(this, drawable, 16, 16)
}
} else {
model.recipient.getDisplayName(context)
}
}
val aboutText = model.recipient.combinedAboutAndEmoji

View file

@ -1,11 +1,13 @@
package org.thoughtcrime.securesms.contacts.paged
import android.content.Context
import android.text.SpannableStringBuilder
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.TextView
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.content.ContextCompat
import com.google.android.material.button.MaterialButton
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
@ -28,6 +30,8 @@ import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.database.model.StoryViewState
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.ContextUtil
import org.thoughtcrime.securesms.util.SpanUtil
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel
@ -500,7 +504,19 @@ open class ContactSearchAdapter(
return
}
name.setText(getRecipient(model))
val recipient = getRecipient(model)
val suffix: CharSequence? = if (recipient.isSystemContact && !recipient.showVerified()) {
SpannableStringBuilder().apply {
val drawable = ContextUtil.requireDrawable(context, R.drawable.symbol_person_circle_24).apply {
setTint(ContextCompat.getColor(context, R.color.signal_colorOnSurface))
}
SpanUtil.appendCenteredImageSpan(this, drawable, 16, 16)
}
} else {
null
}
name.setText(recipient, suffix)
badge.setBadgeFromRecipient(getRecipient(model))
bindAvatar(model)

View file

@ -19,6 +19,7 @@ package org.thoughtcrime.securesms.conversationlist;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
@ -78,6 +79,7 @@ import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.search.MessageResult;
import org.thoughtcrime.securesms.util.ContextUtil;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
@ -211,7 +213,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
@NonNull Set<Long> typingThreads,
@NonNull ConversationSet selectedConversations)
{
bindThread(lifecycleOwner, thread, glideRequests, locale, typingThreads, selectedConversations, null);
bindThread(lifecycleOwner, thread, glideRequests, locale, typingThreads, selectedConversations, null, false);
}
public void bindThread(@NonNull LifecycleOwner lifecycleOwner,
@ -220,7 +222,8 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
@NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@NonNull ConversationSet selectedConversations,
@Nullable String highlightSubstring)
@Nullable String highlightSubstring,
boolean appendSystemContactIcon)
{
this.threadId = thread.getThreadId();
this.requestManager = requestManager;
@ -234,12 +237,20 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
observeDisplayBody(null, null);
joinMembersDisposable.dispose();
SpannableStringBuilder suffix = null;
if (appendSystemContactIcon && recipient.get().isSystemContact() && !recipient.get().showVerified()) {
suffix = new SpannableStringBuilder();
Drawable drawable = ContextUtil.requireDrawable(getContext(), R.drawable.symbol_person_circle_24);
drawable.setTint(ContextCompat.getColor(getContext(), R.color.signal_colorOnSurface));
SpanUtil.appendCenteredImageSpan(suffix, drawable, 16, 16);
}
if (highlightSubstring != null) {
String name = recipient.get().isSelf() ? getContext().getString(R.string.note_to_self) : recipient.get().getDisplayName(getContext());
this.fromView.setText(recipient.get(), SearchUtil.getHighlightedSpan(locale, searchStyleFactory, name, highlightSubstring, SearchUtil.MATCH_ALL), true, null);
this.fromView.setText(recipient.get(), SearchUtil.getHighlightedSpan(locale, searchStyleFactory, name, highlightSubstring, SearchUtil.MATCH_ALL), suffix);
} else {
this.fromView.setText(recipient.get(), false);
this.fromView.setText(recipient.get(), suffix);
}
this.typingThreads = typingThreads;
@ -299,7 +310,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
joinMembersDisposable.dispose();
setSubjectViewText(null);
fromView.setText(recipient.get(), recipient.get().getDisplayName(getContext()), false, null, false);
fromView.setText(recipient.get(), recipient.get().getDisplayName(getContext()), null, false);
setSubjectViewText(SearchUtil.getHighlightedSpan(locale, searchStyleFactory, messageResult.getBodySnippet(), highlightSubstring, SearchUtil.MATCH_ALL));
updateDateView = () -> dateView.setText(DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, messageResult.getReceivedTimestampMs()));
@ -332,7 +343,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
setSubjectViewText(SearchUtil.getHighlightedSpan(locale, searchStyleFactory, joined, highlightSubstring, SearchUtil.MATCH_ALL));
});
fromView.setText(recipient.get(), false);
fromView.setText(recipient.get());
updateDateView = () -> {
if (groupWithMembers.getDate() > 0) {
@ -554,9 +565,9 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
} else {
name = recipient.getDisplayName(getContext());
}
fromView.setText(recipient, SearchUtil.getHighlightedSpan(locale, searchStyleFactory, new SpannableString(name), highlightSubstring, SearchUtil.MATCH_ALL), true, null, thread != null);
fromView.setText(recipient, SearchUtil.getHighlightedSpan(locale, searchStyleFactory, new SpannableString(name), highlightSubstring, SearchUtil.MATCH_ALL), null, thread != null);
} else {
fromView.setText(recipient, false);
fromView.setText(recipient);
}
contactPhotoImage.setAvatar(requestManager, recipient, !batchMode, false);
setBadgeFromRecipient(recipient);

View file

@ -120,7 +120,8 @@ class ConversationListSearchAdapter(
Locale.getDefault(),
emptySet(),
ConversationSet(),
model.thread.query
model.thread.query,
true
)
}
}

View file

@ -60,7 +60,7 @@ class CameraContactSelectionAdapter extends RecyclerView.Adapter<CameraContactSe
}
void bind(@NonNull Recipient recipient, boolean isLast) {
name.setText(recipient, true, isLast ? null : ",");
name.setText(recipient, isLast ? null : ",");
}
}
}

View file

@ -5,7 +5,6 @@ import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@ -18,8 +17,8 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
@ -42,7 +41,6 @@ import org.thoughtcrime.securesms.recipients.RecipientExporter;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.recipients.ui.about.AboutSheet;
import org.thoughtcrime.securesms.recipients.ui.about.AboutSheetKt;
import org.thoughtcrime.securesms.util.BottomSheetUtil;
import org.thoughtcrime.securesms.util.ContextUtil;
import org.thoughtcrime.securesms.util.SpanUtil;
@ -187,17 +185,26 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
: recipient.getDisplayName(requireContext());
fullName.setVisibility(TextUtils.isEmpty(name) ? View.GONE : View.VISIBLE);
SpannableStringBuilder nameBuilder = new SpannableStringBuilder(name);
boolean appendedToName = false;
if (recipient.showVerified()) {
appendedToName = true;
SpanUtil.appendCenteredImageSpan(nameBuilder, ContextUtil.requireDrawable(requireContext(), R.drawable.ic_official_28), 28, 28);
} else if (recipient.isSystemContact()) {
appendedToName = true;
Drawable drawable = ContextUtil.requireDrawable(requireContext(), R.drawable.symbol_person_circle_24);
drawable.setTint(ContextCompat.getColor(requireContext(), R.color.signal_colorOnSurface));
SpanUtil.appendCenteredImageSpan(nameBuilder, drawable, 24, 24);
}
if (!recipient.isSelf() && recipient.isIndividual()) {
Drawable drawable = ContextUtil.requireDrawable(requireContext(), R.drawable.symbol_chevron_right_24_color_on_secondary_container);
Drawable drawable = ContextUtil.requireDrawable(requireContext(), R.drawable.symbol_chevron_right_24);
drawable.setBounds(0, 0, (int) DimensionUnit.DP.toPixels(24), (int) DimensionUnit.DP.toPixels(24));
drawable.setTint(ContextCompat.getColor(requireContext(), R.color.signal_colorOutline));
Drawable insetDrawable = new InsetDrawable(drawable, 0, 0, 0, (int) DimensionUnit.DP.toPixels(4));
SpanUtil.appendBottomImageSpan(nameBuilder, insetDrawable, 24, 28);
if (!appendedToName) {
nameBuilder.append(" ");
}
nameBuilder.append(SpanUtil.buildCenteredImageSpan(drawable));
fullName.setText(nameBuilder);
fullName.setOnClickListener(v -> {

View file

@ -1,5 +0,0 @@
package org.thoughtcrime.securesms.search
import org.thoughtcrime.securesms.recipients.Recipient
data class ContactSearchResult(val results: List<Recipient>, val query: String)

View file

@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.search;
import android.content.Context;
import android.database.Cursor;
import android.database.MergeCursor;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
@ -14,7 +13,6 @@ import androidx.annotation.WorkerThread;
import org.signal.core.util.CursorUtil;
import org.signal.core.util.StringUtil;
import org.signal.core.util.concurrent.LatestPrioritizedSerialExecutor;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.contacts.ContactRepository;
@ -52,7 +50,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import static org.thoughtcrime.securesms.database.SearchTable.SNIPPET_WRAP;
@ -72,8 +69,7 @@ public class SearchRepository {
private final MentionTable mentionTable;
private final MessageTable messageTable;
private final LatestPrioritizedSerialExecutor searchExecutor;
private final Executor serialExecutor;
private final Executor serialExecutor;
public SearchRepository(@NonNull String noteToSelfTitle) {
this.context = ApplicationDependencies.getApplication().getApplicationContext();
@ -84,7 +80,6 @@ public class SearchRepository {
this.mentionTable = SignalDatabase.mentions();
this.messageTable = SignalDatabase.messages();
this.contactRepository = new ContactRepository(context, noteToSelfTitle);
this.searchExecutor = new LatestPrioritizedSerialExecutor(SignalExecutors.BOUNDED);
this.serialExecutor = new SerialExecutor(SignalExecutors.BOUNDED);
}
@ -98,17 +93,6 @@ public class SearchRepository {
return new ThreadSearchResult(result, query);
}
public void queryContacts(@NonNull String query, @NonNull Consumer<ContactSearchResult> callback) {
searchExecutor.execute(1, () -> {
long start = System.currentTimeMillis();
List<Recipient> result = queryContacts(query);
Log.d(TAG, "[contacts] Search took " + (System.currentTimeMillis() - start) + " ms");
callback.accept(new ContactSearchResult(result, query));
});
}
@WorkerThread
public @NonNull MessageSearchResult queryMessagesSync(@NonNull String query) {
long start = System.currentTimeMillis();
@ -139,27 +123,6 @@ public class SearchRepository {
});
}
private List<Recipient> queryContacts(String query) {
if (Util.isEmpty(query)) {
return Collections.emptyList();
}
Cursor contacts = null;
try {
Cursor textSecureContacts = contactRepository.querySignalContacts(query);
Cursor systemContacts = contactRepository.queryNonSignalContacts(query);
contacts = new MergeCursor(new Cursor[] { textSecureContacts, systemContacts });
return readToList(contacts, new RecipientModelBuilder(), 250);
} finally {
if (contacts != null) {
contacts.close();
}
}
}
private @NonNull List<ThreadRecord> queryConversations(@NonNull String query, boolean unreadOnly) {
if (Util.isEmpty(query)) {
return Collections.emptyList();

View file

@ -1,45 +0,0 @@
package org.thoughtcrime.securesms.search
import org.thoughtcrime.securesms.conversationlist.model.ConversationFilter
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.recipients.Recipient
/**
* Represents an all-encompassing search result that can contain various result for different
* subcategories.
*/
data class SearchResult(
val query: String,
val contacts: List<Recipient>,
val conversations: List<ThreadRecord>,
val messages: List<MessageResult>,
val conversationFilter: ConversationFilter
) {
fun size(): Int {
return contacts.size + conversations.size + messages.size
}
val isEmpty: Boolean
get() = size() == 0
fun merge(result: ContactSearchResult): SearchResult {
return this.copy(contacts = result.results, query = result.query)
}
fun merge(result: ThreadSearchResult): SearchResult {
return this.copy(conversations = result.results, query = result.query)
}
fun merge(result: MessageSearchResult): SearchResult {
return this.copy(messages = result.results, query = result.query)
}
fun merge(conversationFilter: ConversationFilter): SearchResult {
return this.copy(conversationFilter = conversationFilter)
}
companion object {
@JvmField
val EMPTY = SearchResult("", emptyList(), emptyList(), emptyList(), ConversationFilter.OFF)
}
}

View file

@ -147,11 +147,6 @@ public final class SpanUtil {
builder.append(" ").append(SpanUtil.buildCenteredImageSpan(drawable));
}
public static void appendBottomImageSpan(@NonNull SpannableStringBuilder builder, @NonNull Drawable drawable, int width, int height) {
drawable.setBounds(0, 0, ViewUtil.dpToPx(width), ViewUtil.dpToPx(height));
builder.append(SpanUtil.buildImageSpan(drawable, DynamicDrawableSpan.ALIGN_BOTTOM));
}
public static CharSequence learnMore(@NonNull Context context,
@ColorInt int color,
@NonNull View.OnClickListener onLearnMoreClicked)

View file

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/signal_colorOnSecondaryContainer"
android:pathData="M8.881,4.381a0.875,0.875 0,0 1,1.238 0l7,7a0.875,0.875 0,0 1,0 1.238l-7,7A0.875,0.875 0,0 1,8.88 18.38L15.263,12 8.88,5.619a0.875,0.875 0,0 1,0 -1.238Z" />
</vector>