mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-25 11:20:47 +01:00
Add support for an 'About' field on your profile.
This commit is contained in:
@@ -30,7 +30,7 @@ public class EmojiKeyboardProvider implements MediaKeyboardProvider,
|
||||
{
|
||||
private static final KeyEvent DELETE_KEY_EVENT = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
|
||||
|
||||
private static final String RECENT_STORAGE_KEY = "pref_recent_emoji2";
|
||||
public static final String RECENT_STORAGE_KEY = "pref_recent_emoji2";
|
||||
|
||||
private final Context context;
|
||||
private final List<EmojiPageModel> models;
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser;
|
||||
import org.thoughtcrime.securesms.util.StringUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -72,4 +79,15 @@ public final class EmojiUtil {
|
||||
String canonical = VARIATION_MAP.get(emoji);
|
||||
return canonical != null ? canonical : emoji;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the provided emoji string into a single drawable, if possible.
|
||||
*/
|
||||
public static @Nullable Drawable convertToDrawable(@NonNull Context context, @Nullable String emoji) {
|
||||
if (Util.isEmpty(emoji)) {
|
||||
return null;
|
||||
} else {
|
||||
return EmojiProvider.getInstance(context).getEmojiDrawable(emoji);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.CursorUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
|
||||
@@ -42,6 +43,7 @@ public class ContactRepository {
|
||||
static final String NUMBER_TYPE_COLUMN = "number_type";
|
||||
static final String LABEL_COLUMN = "label";
|
||||
static final String CONTACT_TYPE_COLUMN = "contact_type";
|
||||
static final String ABOUT_COLUMN = "about";
|
||||
|
||||
static final int NORMAL_TYPE = 0;
|
||||
static final int PUSH_TYPE = 1;
|
||||
@@ -52,18 +54,18 @@ public class ContactRepository {
|
||||
|
||||
/** Maps the recipient results to the legacy contact column names */
|
||||
private static final List<Pair<String, ValueMapper>> SEARCH_CURSOR_MAPPERS = new ArrayList<Pair<String, ValueMapper>>() {{
|
||||
add(new Pair<>(ID_COLUMN, cursor -> cursor.getLong(cursor.getColumnIndexOrThrow(RecipientDatabase.ID))));
|
||||
add(new Pair<>(ID_COLUMN, cursor -> CursorUtil.requireLong(cursor, RecipientDatabase.ID)));
|
||||
|
||||
add(new Pair<>(NAME_COLUMN, cursor -> {
|
||||
String system = cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.SYSTEM_DISPLAY_NAME));
|
||||
String profile = cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.SEARCH_PROFILE_NAME));
|
||||
String system = CursorUtil.requireString(cursor, RecipientDatabase.SYSTEM_DISPLAY_NAME);
|
||||
String profile = CursorUtil.requireString(cursor, RecipientDatabase.SEARCH_PROFILE_NAME);
|
||||
|
||||
return Util.getFirstNonEmpty(system, profile);
|
||||
}));
|
||||
|
||||
add(new Pair<>(NUMBER_COLUMN, cursor -> {
|
||||
String phone = cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.PHONE));
|
||||
String email = cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.EMAIL));
|
||||
String phone = CursorUtil.requireString(cursor, RecipientDatabase.PHONE);
|
||||
String email = CursorUtil.requireString(cursor, RecipientDatabase.EMAIL);
|
||||
|
||||
if (phone != null) {
|
||||
phone = PhoneNumberFormatter.prettyPrint(phone);
|
||||
@@ -72,14 +74,31 @@ public class ContactRepository {
|
||||
return Util.getFirstNonEmpty(phone, email);
|
||||
}));
|
||||
|
||||
add(new Pair<>(NUMBER_TYPE_COLUMN, cursor -> cursor.getInt(cursor.getColumnIndexOrThrow(RecipientDatabase.SYSTEM_PHONE_TYPE))));
|
||||
add(new Pair<>(NUMBER_TYPE_COLUMN, cursor -> CursorUtil.requireInt(cursor, RecipientDatabase.SYSTEM_PHONE_TYPE)));
|
||||
|
||||
add(new Pair<>(LABEL_COLUMN, cursor -> cursor.getString(cursor.getColumnIndexOrThrow(RecipientDatabase.SYSTEM_PHONE_LABEL))));
|
||||
add(new Pair<>(LABEL_COLUMN, cursor -> CursorUtil.requireString(cursor, RecipientDatabase.SYSTEM_PHONE_LABEL)));
|
||||
|
||||
add(new Pair<>(CONTACT_TYPE_COLUMN, cursor -> {
|
||||
int registered = cursor.getInt(cursor.getColumnIndexOrThrow(RecipientDatabase.REGISTERED));
|
||||
int registered = CursorUtil.requireInt(cursor, RecipientDatabase.REGISTERED);
|
||||
return registered == RecipientDatabase.RegisteredState.REGISTERED.getId() ? PUSH_TYPE : NORMAL_TYPE;
|
||||
}));
|
||||
|
||||
add(new Pair<>(ABOUT_COLUMN, cursor -> {
|
||||
String aboutEmoji = CursorUtil.requireString(cursor, RecipientDatabase.ABOUT_EMOJI);
|
||||
String about = CursorUtil.requireString(cursor, RecipientDatabase.ABOUT);
|
||||
|
||||
if (aboutEmoji != null) {
|
||||
if (about != null) {
|
||||
return aboutEmoji + " " + about;
|
||||
} else {
|
||||
return aboutEmoji;
|
||||
}
|
||||
} else if (about != null) {
|
||||
return about;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}));
|
||||
}};
|
||||
|
||||
public ContactRepository(@NonNull Context context) {
|
||||
@@ -106,7 +125,7 @@ public class ContactRepository {
|
||||
|
||||
if (shouldAdd) {
|
||||
MatrixCursor selfCursor = new MatrixCursor(RecipientDatabase.SEARCH_PROJECTION_NAMES);
|
||||
selfCursor.addRow(new Object[]{ self.getId().serialize(), noteToSelfTitle, null, self.getE164().or(""), self.getEmail().orNull(), null, -1, RecipientDatabase.RegisteredState.REGISTERED.getId(), noteToSelfTitle });
|
||||
selfCursor.addRow(new Object[]{ self.getId().serialize(), noteToSelfTitle, self.getE164().or(""), self.getEmail().orNull(), null, -1, RecipientDatabase.RegisteredState.REGISTERED.getId(), self.getAbout(), self.getAboutEmoji(), noteToSelfTitle, noteToSelfTitle });
|
||||
|
||||
cursor = cursor == null ? selfCursor : new MergeCursor(new Cursor[]{ cursor, selfCursor });
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.ViewHolde
|
||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.CursorUtil;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration.StickyHeaderAdapter;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
@@ -97,7 +98,7 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
public abstract void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean checkboxVisible);
|
||||
public abstract void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, String about, int color, boolean checkboxVisible);
|
||||
public abstract void unbind(@NonNull GlideRequests glideRequests);
|
||||
public abstract void setChecked(boolean checked);
|
||||
public abstract void setEnabled(boolean enabled);
|
||||
@@ -117,8 +118,8 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
||||
return (ContactSelectionListItem) itemView;
|
||||
}
|
||||
|
||||
public void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean checkBoxVisible) {
|
||||
getView().set(glideRequests, recipientId, type, name, number, label, color, checkBoxVisible);
|
||||
public void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, String about, int color, boolean checkBoxVisible) {
|
||||
getView().set(glideRequests, recipientId, type, name, number, label, about, color, checkBoxVisible);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -147,7 +148,7 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean checkboxVisible) {
|
||||
public void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, String about, int color, boolean checkboxVisible) {
|
||||
this.label.setText(name);
|
||||
}
|
||||
|
||||
@@ -204,13 +205,14 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
||||
|
||||
@Override
|
||||
public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
|
||||
String rawId = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.ID_COLUMN));
|
||||
String rawId = CursorUtil.requireString(cursor, ContactRepository.ID_COLUMN);
|
||||
RecipientId id = rawId != null ? RecipientId.from(rawId) : null;
|
||||
int contactType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactRepository.CONTACT_TYPE_COLUMN));
|
||||
String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NAME_COLUMN ));
|
||||
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_COLUMN));
|
||||
int numberType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_TYPE_COLUMN ));
|
||||
String label = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.LABEL_COLUMN ));
|
||||
int contactType = CursorUtil.requireInt(cursor, ContactRepository.CONTACT_TYPE_COLUMN);
|
||||
String name = CursorUtil.requireString(cursor, ContactRepository.NAME_COLUMN);
|
||||
String number = CursorUtil.requireString(cursor, ContactRepository.NUMBER_COLUMN);
|
||||
int numberType = CursorUtil.requireInt(cursor, ContactRepository.NUMBER_TYPE_COLUMN);
|
||||
String about = CursorUtil.requireString(cursor, ContactRepository.ABOUT_COLUMN);
|
||||
String label = CursorUtil.requireString(cursor, ContactRepository.LABEL_COLUMN);
|
||||
String labelText = ContactsContract.CommonDataKinds.Phone.getTypeLabel(getContext().getResources(),
|
||||
numberType, label).toString();
|
||||
|
||||
@@ -220,7 +222,7 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
||||
boolean currentContact = currentContacts.contains(id);
|
||||
|
||||
viewHolder.unbind(glideRequests);
|
||||
viewHolder.bind(glideRequests, id, contactType, name, number, labelText, color, multiSelect || currentContact);
|
||||
viewHolder.bind(glideRequests, id, contactType, name, number, labelText, about, color, multiSelect || currentContact);
|
||||
viewHolder.setEnabled(true);
|
||||
|
||||
if (currentContact) {
|
||||
@@ -239,10 +241,10 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
String rawId = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.ID_COLUMN));
|
||||
String rawId = CursorUtil.requireString(cursor, ContactRepository.ID_COLUMN);
|
||||
RecipientId id = rawId != null ? RecipientId.from(rawId) : null;
|
||||
int numberType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_TYPE_COLUMN));
|
||||
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_COLUMN));
|
||||
int numberType = CursorUtil.requireInt(cursor, ContactRepository.NUMBER_TYPE_COLUMN);
|
||||
String number = CursorUtil.requireString(cursor, ContactRepository.NUMBER_COLUMN);
|
||||
|
||||
viewHolder.setEnabled(true);
|
||||
|
||||
@@ -258,7 +260,7 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
||||
|
||||
@Override
|
||||
public int getItemViewType(@NonNull Cursor cursor) {
|
||||
if (cursor.getInt(cursor.getColumnIndexOrThrow(ContactRepository.CONTACT_TYPE_COLUMN)) == ContactRepository.DIVIDER_TYPE) {
|
||||
if (CursorUtil.requireInt(cursor, ContactRepository.CONTACT_TYPE_COLUMN) == ContactRepository.DIVIDER_TYPE) {
|
||||
return VIEW_TYPE_DIVIDER;
|
||||
} else {
|
||||
return VIEW_TYPE_CONTACT;
|
||||
@@ -317,7 +319,7 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
||||
}
|
||||
|
||||
Cursor cursor = getCursorAtPositionOrThrow(position);
|
||||
String letter = cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NAME_COLUMN));
|
||||
String letter = CursorUtil.requireString(cursor, ContactRepository.NAME_COLUMN);
|
||||
|
||||
if (letter != null) {
|
||||
letter = letter.trim();
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
@@ -65,6 +66,7 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
||||
String name,
|
||||
String number,
|
||||
String label,
|
||||
String about,
|
||||
int color,
|
||||
boolean checkboxVisible)
|
||||
{
|
||||
@@ -87,7 +89,7 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
||||
this.numberView.setTextColor(color);
|
||||
this.contactPhotoImage.setAvatar(glideRequests, recipientSnapshot, false);
|
||||
|
||||
setText(recipientSnapshot, type, name, number, label);
|
||||
setText(recipientSnapshot, type, name, number, label, about);
|
||||
|
||||
this.checkBox.setVisibility(checkboxVisible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
@@ -110,7 +112,7 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private void setText(@Nullable Recipient recipient, int type, String name, String number, String label) {
|
||||
private void setText(@Nullable Recipient recipient, int type, String name, String number, String label, @Nullable String about) {
|
||||
if (number == null || number.isEmpty()) {
|
||||
this.nameView.setEnabled(false);
|
||||
this.numberView.setText("");
|
||||
@@ -120,7 +122,7 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
||||
this.numberView.setText(getGroupMemberCount(recipient));
|
||||
this.labelView.setVisibility(View.GONE);
|
||||
} else if (type == ContactRepository.PUSH_TYPE) {
|
||||
this.numberView.setText(number);
|
||||
this.numberView.setText(!Util.isEmpty(about) ? about : number);
|
||||
this.nameView.setEnabled(true);
|
||||
this.labelView.setVisibility(View.GONE);
|
||||
} else if (type == ContactRepository.NEW_USERNAME_TYPE) {
|
||||
@@ -129,7 +131,7 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
||||
this.labelView.setText(label);
|
||||
this.labelView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
this.numberView.setText(number);
|
||||
this.numberView.setText(!Util.isEmpty(about) ? about : number);
|
||||
this.nameView.setEnabled(true);
|
||||
this.labelView.setText(label != null && !label.equals("null") ? label : "");
|
||||
this.labelView.setVisibility(View.VISIBLE);
|
||||
|
||||
@@ -71,7 +71,8 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
ContactRepository.NUMBER_COLUMN,
|
||||
ContactRepository.NUMBER_TYPE_COLUMN,
|
||||
ContactRepository.LABEL_COLUMN,
|
||||
ContactRepository.CONTACT_TYPE_COLUMN};
|
||||
ContactRepository.CONTACT_TYPE_COLUMN,
|
||||
ContactRepository.ABOUT_COLUMN};
|
||||
|
||||
private static final int RECENT_CONVERSATION_MAX = 25;
|
||||
|
||||
@@ -212,7 +213,8 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
"",
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||
"",
|
||||
ContactRepository.DIVIDER_TYPE });
|
||||
ContactRepository.DIVIDER_TYPE,
|
||||
"" });
|
||||
return recentsHeader;
|
||||
}
|
||||
|
||||
@@ -223,7 +225,8 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
"",
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||
"",
|
||||
ContactRepository.DIVIDER_TYPE });
|
||||
ContactRepository.DIVIDER_TYPE,
|
||||
"" });
|
||||
return contactsHeader;
|
||||
}
|
||||
|
||||
@@ -234,7 +237,8 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
"",
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||
"",
|
||||
ContactRepository.DIVIDER_TYPE });
|
||||
ContactRepository.DIVIDER_TYPE,
|
||||
"" });
|
||||
return groupHeader;
|
||||
}
|
||||
|
||||
@@ -245,7 +249,8 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
"",
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||
"",
|
||||
ContactRepository.DIVIDER_TYPE });
|
||||
ContactRepository.DIVIDER_TYPE,
|
||||
"" });
|
||||
return contactsHeader;
|
||||
}
|
||||
|
||||
@@ -256,7 +261,8 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
"",
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||
"",
|
||||
ContactRepository.DIVIDER_TYPE });
|
||||
ContactRepository.DIVIDER_TYPE,
|
||||
"" });
|
||||
return contactsHeader;
|
||||
}
|
||||
|
||||
@@ -281,7 +287,8 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
stringId,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||
"",
|
||||
ContactRepository.RECENT_TYPE });
|
||||
ContactRepository.RECENT_TYPE,
|
||||
recipient.getCombinedAboutAndEmoji() });
|
||||
}
|
||||
}
|
||||
return recentConversations;
|
||||
@@ -316,7 +323,8 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
groupRecord.getId(),
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
|
||||
"",
|
||||
ContactRepository.NORMAL_TYPE });
|
||||
ContactRepository.NORMAL_TYPE,
|
||||
"" });
|
||||
}
|
||||
}
|
||||
return groupContacts;
|
||||
@@ -329,7 +337,8 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
filter,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
|
||||
"\u21e2",
|
||||
ContactRepository.NEW_PHONE_TYPE});
|
||||
ContactRepository.NEW_PHONE_TYPE,
|
||||
"" });
|
||||
return newNumberCursor;
|
||||
}
|
||||
|
||||
@@ -340,7 +349,8 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
filter,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
|
||||
"\u21e2",
|
||||
ContactRepository.NEW_USERNAME_TYPE});
|
||||
ContactRepository.NEW_USERNAME_TYPE,
|
||||
"" });
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@@ -368,7 +378,8 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_COLUMN)),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.NUMBER_TYPE_COLUMN)),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactRepository.LABEL_COLUMN)),
|
||||
ContactRepository.NORMAL_TYPE});
|
||||
ContactRepository.NORMAL_TYPE,
|
||||
"" });
|
||||
}
|
||||
}
|
||||
Log.i(TAG, "filterNonPushContacts() -> " + (System.currentTimeMillis() - startMillis) + "ms");
|
||||
|
||||
@@ -395,8 +395,8 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||
private LiveRecipient recipient;
|
||||
private long threadId;
|
||||
private int distributionType;
|
||||
private int reactWithAnyEmojiStartPage;
|
||||
private boolean isSecureText;
|
||||
private int reactWithAnyEmojiStartPage = -1;
|
||||
private boolean isDefaultSms = true;
|
||||
private boolean isMmsEnabled = true;
|
||||
private boolean isSecurityInitialized = false;
|
||||
@@ -488,7 +488,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||
return;
|
||||
}
|
||||
|
||||
reactWithAnyEmojiStartPage = 0;
|
||||
reactWithAnyEmojiStartPage = -1;
|
||||
if (!Util.isEmpty(composeText) || attachmentManager.isAttachmentPresent() || inputPanel.getQuote().isPresent()) {
|
||||
saveDraft();
|
||||
attachmentManager.clear(glideRequests, false);
|
||||
@@ -745,7 +745,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
|
||||
reactWithAnyEmojiStartPage = savedInstanceState.getInt(STATE_REACT_WITH_ANY_PAGE, 0);
|
||||
reactWithAnyEmojiStartPage = savedInstanceState.getInt(STATE_REACT_WITH_ANY_PAGE, -1);
|
||||
}
|
||||
|
||||
private void setVisibleThread(long threadId) {
|
||||
@@ -2262,6 +2262,10 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||
reactWithAnyEmojiStartPage = page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReactWithAnyEmojiSelected(@NonNull String emoji) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSearchMoveUpPressed() {
|
||||
searchViewModel.onMoveUp();
|
||||
|
||||
@@ -21,6 +21,7 @@ public class ConversationBannerView extends ConstraintLayout {
|
||||
|
||||
private AvatarImageView contactAvatar;
|
||||
private TextView contactTitle;
|
||||
private TextView contactAbout;
|
||||
private TextView contactSubtitle;
|
||||
private TextView contactDescription;
|
||||
|
||||
@@ -39,6 +40,7 @@ public class ConversationBannerView extends ConstraintLayout {
|
||||
|
||||
contactAvatar = findViewById(R.id.message_request_avatar);
|
||||
contactTitle = findViewById(R.id.message_request_title);
|
||||
contactAbout = findViewById(R.id.message_request_about);
|
||||
contactSubtitle = findViewById(R.id.message_request_subtitle);
|
||||
contactDescription = findViewById(R.id.message_request_description);
|
||||
|
||||
@@ -53,6 +55,11 @@ public class ConversationBannerView extends ConstraintLayout {
|
||||
contactTitle.setText(title);
|
||||
}
|
||||
|
||||
public void setAbout(@Nullable String about) {
|
||||
contactAbout.setText(about);
|
||||
contactAbout.setVisibility(TextUtils.isEmpty(about) ? GONE : VISIBLE);
|
||||
}
|
||||
|
||||
public void setSubtitle(@Nullable CharSequence subtitle) {
|
||||
contactSubtitle.setText(subtitle);
|
||||
contactSubtitle.setVisibility(TextUtils.isEmpty(subtitle) ? GONE : VISIBLE);
|
||||
|
||||
@@ -126,6 +126,7 @@ import org.thoughtcrime.securesms.stickers.StickerLocator;
|
||||
import org.thoughtcrime.securesms.stickers.StickerPackPreviewActivity;
|
||||
import org.thoughtcrime.securesms.util.CachedInflater;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.HtmlUtil;
|
||||
import org.thoughtcrime.securesms.util.RemoteDeleteUtil;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
@@ -417,7 +418,6 @@ public class ConversationFragment extends LoggingFragment {
|
||||
}
|
||||
|
||||
private static void presentMessageRequestProfileView(@NonNull Context context, @NonNull MessageRequestViewModel.RecipientInfo recipientInfo, @Nullable ConversationBannerView conversationBanner) {
|
||||
|
||||
if (conversationBanner == null) {
|
||||
return;
|
||||
}
|
||||
@@ -434,6 +434,7 @@ public class ConversationFragment extends LoggingFragment {
|
||||
|
||||
String title = isSelf ? context.getString(R.string.note_to_self) : recipient.getDisplayNameOrUsername(context);
|
||||
conversationBanner.setTitle(title);
|
||||
conversationBanner.setAbout(recipient.getCombinedAboutAndEmoji());
|
||||
|
||||
if (recipient.isGroup()) {
|
||||
if (pendingMemberCount > 0) {
|
||||
|
||||
@@ -138,6 +138,8 @@ public class RecipientDatabase extends Database {
|
||||
private static final String LAST_SESSION_RESET = "last_session_reset";
|
||||
private static final String WALLPAPER = "wallpaper";
|
||||
private static final String WALLPAPER_URI = "wallpaper_file";
|
||||
public static final String ABOUT = "about";
|
||||
public static final String ABOUT_EMOJI = "about_emoji";
|
||||
|
||||
public static final String SEARCH_PROFILE_NAME = "search_signal_profile";
|
||||
private static final String SORT_NAME = "sort_name";
|
||||
@@ -162,12 +164,14 @@ public class RecipientDatabase extends Database {
|
||||
FORCE_SMS_SELECTION,
|
||||
CAPABILITIES,
|
||||
STORAGE_SERVICE_ID, DIRTY,
|
||||
MENTION_SETTING, WALLPAPER, WALLPAPER_URI
|
||||
MENTION_SETTING, WALLPAPER, WALLPAPER_URI,
|
||||
MENTION_SETTING,
|
||||
ABOUT, ABOUT_EMOJI
|
||||
};
|
||||
|
||||
private static final String[] ID_PROJECTION = new String[]{ID};
|
||||
private static final String[] SEARCH_PROJECTION = new String[]{ID, SYSTEM_DISPLAY_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, "COALESCE(" + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ") AS " + SEARCH_PROFILE_NAME, "COALESCE(" + nullIfEmpty(SYSTEM_DISPLAY_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ") AS " + SORT_NAME};
|
||||
public static final String[] SEARCH_PROJECTION_NAMES = new String[]{ID, SYSTEM_DISPLAY_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, SEARCH_PROFILE_NAME, SORT_NAME};
|
||||
private static final String[] SEARCH_PROJECTION = new String[]{ID, SYSTEM_DISPLAY_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, "COALESCE(" + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ") AS " + SEARCH_PROFILE_NAME, "COALESCE(" + nullIfEmpty(SYSTEM_DISPLAY_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ") AS " + SORT_NAME};
|
||||
public static final String[] SEARCH_PROJECTION_NAMES = new String[]{ID, SYSTEM_DISPLAY_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, SEARCH_PROFILE_NAME, SORT_NAME};
|
||||
private static final String[] TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
|
||||
.map(columnName -> TABLE_NAME + "." + columnName)
|
||||
.toList().toArray(new String[0]);
|
||||
@@ -359,7 +363,9 @@ public class RecipientDatabase extends Database {
|
||||
LAST_GV1_MIGRATE_REMINDER + " INTEGER DEFAULT 0, " +
|
||||
LAST_SESSION_RESET + " BLOB DEFAULT NULL, " +
|
||||
WALLPAPER + " BLOB DEFAULT NULL, " +
|
||||
WALLPAPER_URI + " TEXT DEFAULT NULL);";
|
||||
WALLPAPER_URI + " TEXT DEFAULT NULL, " +
|
||||
ABOUT + " TEXT DEFAULT NULL, " +
|
||||
ABOUT_EMOJI + " TEXT DEFAULT NULL);";
|
||||
|
||||
private static final String INSIGHTS_INVITEE_LIST = "SELECT " + TABLE_NAME + "." + ID +
|
||||
" FROM " + TABLE_NAME +
|
||||
@@ -1274,6 +1280,8 @@ public class RecipientDatabase extends Database {
|
||||
String storageKeyRaw = CursorUtil.requireString(cursor, STORAGE_SERVICE_ID);
|
||||
int mentionSettingId = CursorUtil.requireInt(cursor, MENTION_SETTING);
|
||||
byte[] wallpaper = CursorUtil.requireBlob(cursor, WALLPAPER);
|
||||
String about = CursorUtil.requireString(cursor, ABOUT);
|
||||
String aboutEmoji = CursorUtil.requireString(cursor, ABOUT_EMOJI);
|
||||
|
||||
MaterialColor color;
|
||||
byte[] profileKey = null;
|
||||
@@ -1359,6 +1367,8 @@ public class RecipientDatabase extends Database {
|
||||
storageKey,
|
||||
MentionSetting.fromId(mentionSettingId),
|
||||
chatWallpaper,
|
||||
about,
|
||||
aboutEmoji,
|
||||
getSyncExtras(cursor));
|
||||
}
|
||||
|
||||
@@ -1777,6 +1787,16 @@ public class RecipientDatabase extends Database {
|
||||
}
|
||||
}
|
||||
|
||||
public void setAbout(@NonNull RecipientId id, @Nullable String about, @Nullable String emoji) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(ABOUT, about);
|
||||
contentValues.put(ABOUT_EMOJI, emoji);
|
||||
|
||||
if (update(id, contentValues)) {
|
||||
Recipient.live(id).refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public void setProfileSharing(@NonNull RecipientId id, @SuppressWarnings("SameParameterValue") boolean enabled) {
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
contentValues.put(PROFILE_SHARING, enabled ? 1 : 0);
|
||||
@@ -2938,6 +2958,8 @@ public class RecipientDatabase extends Database {
|
||||
private final byte[] storageId;
|
||||
private final MentionSetting mentionSetting;
|
||||
private final ChatWallpaper wallpaper;
|
||||
private final String about;
|
||||
private final String aboutEmoji;
|
||||
private final SyncExtras syncExtras;
|
||||
|
||||
RecipientSettings(@NonNull RecipientId id,
|
||||
@@ -2976,6 +2998,8 @@ public class RecipientDatabase extends Database {
|
||||
@Nullable byte[] storageId,
|
||||
@NonNull MentionSetting mentionSetting,
|
||||
@Nullable ChatWallpaper wallpaper,
|
||||
@Nullable String about,
|
||||
@Nullable String aboutEmoji,
|
||||
@NonNull SyncExtras syncExtras)
|
||||
{
|
||||
this.id = id;
|
||||
@@ -3016,6 +3040,8 @@ public class RecipientDatabase extends Database {
|
||||
this.storageId = storageId;
|
||||
this.mentionSetting = mentionSetting;
|
||||
this.wallpaper = wallpaper;
|
||||
this.about = about;
|
||||
this.aboutEmoji = aboutEmoji;
|
||||
this.syncExtras = syncExtras;
|
||||
}
|
||||
|
||||
@@ -3167,6 +3193,14 @@ public class RecipientDatabase extends Database {
|
||||
return wallpaper;
|
||||
}
|
||||
|
||||
public @Nullable String getAbout() {
|
||||
return about;
|
||||
}
|
||||
|
||||
public @Nullable String getAboutEmoji() {
|
||||
return aboutEmoji;
|
||||
}
|
||||
|
||||
public @NonNull SyncExtras getSyncExtras() {
|
||||
return syncExtras;
|
||||
}
|
||||
|
||||
@@ -170,8 +170,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
||||
private static final int CLEAR_PROFILE_KEY_CREDENTIALS = 86;
|
||||
private static final int LAST_RESET_SESSION_TIME = 87;
|
||||
private static final int WALLPAPER = 88;
|
||||
private static final int ABOUT = 89;
|
||||
|
||||
private static final int DATABASE_VERSION = 88;
|
||||
private static final int DATABASE_VERSION = 89;
|
||||
private static final String DATABASE_NAME = "signal.db";
|
||||
|
||||
private final Context context;
|
||||
@@ -1252,6 +1253,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
||||
db.execSQL("ALTER TABLE recipient ADD COLUMN wallpaper_file TEXT DEFAULT NULL");
|
||||
}
|
||||
|
||||
if (oldVersion < ABOUT) {
|
||||
db.execSQL("ALTER TABLE recipient ADD COLUMN about TEXT DEFAULT NULL");
|
||||
db.execSQL("ALTER TABLE recipient ADD COLUMN about_emoji TEXT DEFAULT NULL");
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
|
||||
@@ -15,9 +15,12 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.LifecycleRecyclerAdapter;
|
||||
import org.thoughtcrime.securesms.util.LifecycleViewHolder;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@@ -166,6 +169,7 @@ final class GroupMemberListAdapter extends LifecycleRecyclerAdapter<GroupMemberL
|
||||
final Context context;
|
||||
final AvatarImageView avatar;
|
||||
final TextView recipient;
|
||||
final EmojiTextView about;
|
||||
final CheckBox selected;
|
||||
final PopupMenuView popupMenu;
|
||||
final View popupMenuContainer;
|
||||
@@ -187,6 +191,7 @@ final class GroupMemberListAdapter extends LifecycleRecyclerAdapter<GroupMemberL
|
||||
this.context = itemView.getContext();
|
||||
this.avatar = itemView.findViewById(R.id.recipient_avatar);
|
||||
this.recipient = itemView.findViewById(R.id.recipient_name);
|
||||
this.about = itemView.findViewById(R.id.recipient_about);
|
||||
this.selected = itemView.findViewById(R.id.recipient_selected);
|
||||
this.popupMenu = itemView.findViewById(R.id.popupMenu);
|
||||
this.popupMenuContainer = itemView.findViewById(R.id.popupMenuProgressContainer);
|
||||
@@ -201,12 +206,14 @@ final class GroupMemberListAdapter extends LifecycleRecyclerAdapter<GroupMemberL
|
||||
void bindRecipient(@NonNull Recipient recipient) {
|
||||
String displayName = recipient.isSelf() ? context.getString(R.string.GroupMembersDialog_you)
|
||||
: recipient.getDisplayName(itemView.getContext());
|
||||
bindImageAndText(recipient, displayName);
|
||||
bindImageAndText(recipient, displayName, recipient.getCombinedAboutAndEmoji());
|
||||
}
|
||||
|
||||
void bindImageAndText(@NonNull Recipient recipient, @NonNull String displayText) {
|
||||
void bindImageAndText(@NonNull Recipient recipient, @NonNull String displayText, @Nullable String about) {
|
||||
this.recipient.setText(displayText);
|
||||
this.avatar.setRecipient(recipient);
|
||||
this.about.setText(about);
|
||||
this.about.setVisibility(Util.isEmpty(about) ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
void bindRecipientClick(@NonNull Recipient recipient) {
|
||||
@@ -333,7 +340,7 @@ final class GroupMemberListAdapter extends LifecycleRecyclerAdapter<GroupMemberL
|
||||
|
||||
GroupMemberEntry.PendingMember pendingMember = (GroupMemberEntry.PendingMember) memberEntry;
|
||||
|
||||
bindImageAndText(pendingMember.getInvitee(), pendingMember.getInvitee().getDisplayNameOrUsername(context));
|
||||
bindImageAndText(pendingMember.getInvitee(), pendingMember.getInvitee().getDisplayNameOrUsername(context), pendingMember.getInvitee().getCombinedAboutAndEmoji());
|
||||
bindRecipientClick(pendingMember.getInvitee());
|
||||
|
||||
if (pendingMember.isCancellable() && adminActionsListener != null) {
|
||||
@@ -370,7 +377,7 @@ final class GroupMemberListAdapter extends LifecycleRecyclerAdapter<GroupMemberL
|
||||
pendingMembers.getInviteCount(),
|
||||
displayName, pendingMembers.getInviteCount());
|
||||
|
||||
bindImageAndText(inviter, displayText);
|
||||
bindImageAndText(inviter, displayText, inviter.getAbout());
|
||||
|
||||
if (pendingMembers.isCancellable() && adminActionsListener != null) {
|
||||
popupMenu.setMenu(R.menu.others_invite_pending_menu,
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.util.StreamDetails;
|
||||
|
||||
@@ -58,10 +59,12 @@ public final class ProfileUploadJob extends BaseJob {
|
||||
|
||||
ProfileKey profileKey = ProfileKeyUtil.getSelfProfileKey();
|
||||
ProfileName profileName = Recipient.self().getProfileName();
|
||||
String about = Optional.fromNullable(Recipient.self().getAbout()).or("");
|
||||
String aboutEmoji = Optional.fromNullable(Recipient.self().getAboutEmoji()).or("");
|
||||
String avatarPath;
|
||||
|
||||
try (StreamDetails avatar = AvatarHelper.getSelfProfileAvatarStream(context)) {
|
||||
avatarPath = accountManager.setVersionedProfile(Recipient.self().getUuid().get(), profileKey, profileName.serialize(), avatar).orNull();
|
||||
avatarPath = accountManager.setVersionedProfile(Recipient.self().getUuid().get(), profileKey, profileName.serialize(), about, aboutEmoji, avatar).orNull();
|
||||
}
|
||||
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileAvatar(Recipient.self().getId(), avatarPath);
|
||||
|
||||
@@ -75,6 +75,7 @@ public class RefreshOwnProfileJob extends BaseJob {
|
||||
SignalServiceProfile profile = profileAndCredential.getProfile();
|
||||
|
||||
setProfileName(profile.getName());
|
||||
setProfileAbout(profile.getAbout(), profile.getAboutEmoji());
|
||||
setProfileAvatar(profile.getAvatar());
|
||||
setProfileCapabilities(profile.getCapabilities());
|
||||
Optional<ProfileKeyCredential> profileKeyCredential = profileAndCredential.getProfileKeyCredential();
|
||||
@@ -117,6 +118,18 @@ public class RefreshOwnProfileJob extends BaseJob {
|
||||
}
|
||||
}
|
||||
|
||||
private void setProfileAbout(@Nullable String encryptedAbout, @Nullable String encryptedEmoji) {
|
||||
try {
|
||||
ProfileKey profileKey = ProfileKeyUtil.getSelfProfileKey();
|
||||
String plaintextAbout = ProfileUtil.decryptName(profileKey, encryptedAbout);
|
||||
String plaintextEmoji = ProfileUtil.decryptName(profileKey, encryptedEmoji);
|
||||
|
||||
DatabaseFactory.getRecipientDatabase(context).setAbout(Recipient.self().getId(), plaintextAbout, plaintextEmoji);
|
||||
} catch (InvalidCiphertextException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setProfileAvatar(@Nullable String avatar) {
|
||||
ApplicationDependencies.getJobManager().add(new RetrieveProfileAvatarJob(Recipient.self(), avatar));
|
||||
}
|
||||
|
||||
@@ -328,6 +328,7 @@ public class RetrieveProfileJob extends BaseJob {
|
||||
ProfileKey recipientProfileKey = ProfileKeyUtil.profileKeyOrNull(recipient.getProfileKey());
|
||||
|
||||
setProfileName(recipient, profile.getName());
|
||||
setProfileAbout(recipient, profile.getAbout(), profile.getAboutEmoji());
|
||||
setProfileAvatar(recipient, profile.getAvatar());
|
||||
clearUsername(recipient);
|
||||
setProfileCapabilities(recipient, profile.getCapabilities());
|
||||
@@ -454,6 +455,20 @@ public class RetrieveProfileJob extends BaseJob {
|
||||
}
|
||||
}
|
||||
|
||||
private void setProfileAbout(@NonNull Recipient recipient, @Nullable String encryptedAbout, @Nullable String encryptedEmoji) {
|
||||
try {
|
||||
ProfileKey profileKey = ProfileKeyUtil.profileKeyOrNull(recipient.getProfileKey());
|
||||
if (profileKey == null) return;
|
||||
|
||||
String plaintextAbout = ProfileUtil.decryptName(profileKey, encryptedAbout);
|
||||
String plaintextEmoji = ProfileUtil.decryptName(profileKey, encryptedEmoji);
|
||||
|
||||
DatabaseFactory.getRecipientDatabase(context).setAbout(recipient.getId(), plaintextAbout, plaintextEmoji);
|
||||
} catch (InvalidCiphertextException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setProfileAvatar(Recipient recipient, String profileAvatar) {
|
||||
if (recipient.getProfileKey() == null) return;
|
||||
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package org.thoughtcrime.securesms.profiles.manage;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
import org.signal.core.util.BreakIteratorCompat;
|
||||
import org.signal.core.util.EditTextUtil;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
|
||||
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.StringUtil;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||
import org.thoughtcrime.securesms.util.text.AfterTextChanged;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
|
||||
|
||||
/**
|
||||
* Let's you edit the 'About' section of your profile.
|
||||
*/
|
||||
public class EditAboutFragment extends Fragment implements ManageProfileActivity.EmojiController {
|
||||
|
||||
public static final int ABOUT_MAX_GLYPHS = 100;
|
||||
public static final int ABOUT_LIMIT_DISPLAY_THRESHOLD = 75;
|
||||
|
||||
private static final String KEY_SELECTED_EMOJI = "selected_emoji";
|
||||
|
||||
private ImageView emojiView;
|
||||
private EditText bodyView;
|
||||
private TextView countView;
|
||||
|
||||
private String selectedEmoji;
|
||||
|
||||
@Override
|
||||
public @NonNull View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.edit_about_fragment, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
this.emojiView = view.findViewById(R.id.edit_about_emoji);
|
||||
this.bodyView = view.findViewById(R.id.edit_about_body);
|
||||
this.countView = view.findViewById(R.id.edit_about_count);
|
||||
|
||||
|
||||
view.<Toolbar>findViewById(R.id.toolbar)
|
||||
.setNavigationOnClickListener(v -> Navigation.findNavController(view)
|
||||
.popBackStack());
|
||||
|
||||
EditTextUtil.addGraphemeClusterLimitFilter(bodyView, ABOUT_MAX_GLYPHS);
|
||||
this.bodyView.addTextChangedListener(new AfterTextChanged(editable -> {
|
||||
trimFieldToMaxByteLength(editable);
|
||||
presentCount(editable.toString());
|
||||
}));
|
||||
|
||||
this.emojiView.setOnClickListener(v -> {
|
||||
ReactWithAnyEmojiBottomSheetDialogFragment.createForAboutSelection()
|
||||
.show(requireFragmentManager(), "BOTTOM");
|
||||
});
|
||||
|
||||
view.findViewById(R.id.edit_about_save).setOnClickListener(this::onSaveClicked);
|
||||
|
||||
if (savedInstanceState != null && savedInstanceState.containsKey(KEY_SELECTED_EMOJI)) {
|
||||
onEmojiSelected(savedInstanceState.getString(KEY_SELECTED_EMOJI, ""));
|
||||
} else {
|
||||
this.bodyView.setText(Recipient.self().getAbout());
|
||||
onEmojiSelected(Optional.fromNullable(Recipient.self().getAboutEmoji()).or(""));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
outState.putString(KEY_SELECTED_EMOJI, selectedEmoji);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEmojiSelected(@NonNull String emoji) {
|
||||
Drawable drawable = EmojiUtil.convertToDrawable(requireContext(), emoji);
|
||||
if (drawable != null) {
|
||||
this.emojiView.setImageDrawable(drawable);
|
||||
this.selectedEmoji = emoji;
|
||||
}
|
||||
}
|
||||
|
||||
private void presentCount(@NonNull String aboutBody) {
|
||||
BreakIteratorCompat breakIterator = BreakIteratorCompat.getInstance();
|
||||
breakIterator.setText(aboutBody);
|
||||
int glyphCount = breakIterator.countBreaks();
|
||||
|
||||
if (glyphCount >= ABOUT_LIMIT_DISPLAY_THRESHOLD) {
|
||||
this.countView.setVisibility(View.VISIBLE);
|
||||
this.countView.setText(getResources().getString(R.string.EditAboutFragment_count, glyphCount, ABOUT_MAX_GLYPHS));
|
||||
} else {
|
||||
this.countView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void onSaveClicked(View view) {
|
||||
SimpleTask.run(getViewLifecycleOwner().getLifecycle(), () -> {
|
||||
DatabaseFactory.getRecipientDatabase(requireContext()).setAbout(Recipient.self().getId(), bodyView.getText().toString(), selectedEmoji);
|
||||
ApplicationDependencies.getJobManager().add(new ProfileUploadJob());
|
||||
return null;
|
||||
}, (nothing) -> {
|
||||
Navigation.findNavController(view).popBackStack();
|
||||
});
|
||||
}
|
||||
|
||||
public static void trimFieldToMaxByteLength(Editable s) {
|
||||
int trimmedLength = StringUtil.trimToFit(s.toString(), ProfileCipher.MAX_POSSIBLE_ABOUT_LENGTH).length();
|
||||
|
||||
if (s.length() > trimmedLength) {
|
||||
s.delete(trimmedLength, s.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,21 +5,24 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.NavGraph;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
import org.thoughtcrime.securesms.BaseActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
|
||||
import org.thoughtcrime.securesms.profiles.edit.EditProfileFragmentDirections;
|
||||
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
|
||||
/**
|
||||
* Activity that manages the local user's profile, as accessed via the settings.
|
||||
*/
|
||||
public class ManageProfileActivity extends BaseActivity {
|
||||
public class ManageProfileActivity extends BaseActivity implements ReactWithAnyEmojiBottomSheetDialogFragment.Callback {
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
|
||||
|
||||
@@ -61,4 +64,26 @@ public class ManageProfileActivity extends BaseActivity {
|
||||
super.onResume();
|
||||
dynamicTheme.onResume(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReactWithAnyEmojiDialogDismissed() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReactWithAnyEmojiPageChanged(int page) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReactWithAnyEmojiSelected(@NonNull String emoji) {
|
||||
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().getPrimaryNavigationFragment();
|
||||
Fragment activeFragment = navHostFragment.getChildFragmentManager().getPrimaryNavigationFragment();
|
||||
|
||||
if (activeFragment instanceof EmojiController) {
|
||||
((EmojiController) activeFragment).onEmojiSelected(emoji);
|
||||
}
|
||||
}
|
||||
|
||||
interface EmojiController {
|
||||
void onEmojiSelected(@NonNull String emoji);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms.profiles.manage;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -13,6 +14,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
@@ -21,6 +23,7 @@ import com.bumptech.glide.Glide;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.LoggingFragment;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
|
||||
import org.thoughtcrime.securesms.mediasend.AvatarSelectionActivity;
|
||||
import org.thoughtcrime.securesms.mediasend.AvatarSelectionBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
@@ -44,7 +47,7 @@ public class ManageProfileFragment extends LoggingFragment {
|
||||
private View usernameContainer;
|
||||
private TextView aboutView;
|
||||
private View aboutContainer;
|
||||
private TextView aboutEmojiView;
|
||||
private ImageView aboutEmojiView;
|
||||
private AlertDialog avatarProgress;
|
||||
|
||||
private ManageProfileViewModel viewModel;
|
||||
@@ -65,6 +68,7 @@ public class ManageProfileFragment extends LoggingFragment {
|
||||
this.usernameContainer = view.findViewById(R.id.manage_profile_username_container);
|
||||
this.aboutView = view.findViewById(R.id.manage_profile_about);
|
||||
this.aboutContainer = view.findViewById(R.id.manage_profile_about_container);
|
||||
this.aboutEmojiView = view.findViewById(R.id.manage_profile_about_icon);
|
||||
|
||||
initializeViewModel();
|
||||
|
||||
@@ -78,6 +82,10 @@ public class ManageProfileFragment extends LoggingFragment {
|
||||
this.usernameContainer.setOnClickListener(v -> {
|
||||
Navigation.findNavController(v).navigate(ManageProfileFragmentDirections.actionManageUsername());
|
||||
});
|
||||
|
||||
this.aboutContainer.setOnClickListener(v -> {
|
||||
Navigation.findNavController(v).navigate(ManageProfileFragmentDirections.actionManageAbout());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,13 +110,8 @@ public class ManageProfileFragment extends LoggingFragment {
|
||||
viewModel.getAvatar().observe(getViewLifecycleOwner(), this::presentAvatar);
|
||||
viewModel.getProfileName().observe(getViewLifecycleOwner(), this::presentProfileName);
|
||||
viewModel.getEvents().observe(getViewLifecycleOwner(), this::presentEvent);
|
||||
|
||||
if (viewModel.shouldShowAbout()) {
|
||||
viewModel.getAbout().observe(getViewLifecycleOwner(), this::presentAbout);
|
||||
viewModel.getAboutEmoji().observe(getViewLifecycleOwner(), this::presentAboutEmoji);
|
||||
} else {
|
||||
aboutContainer.setVisibility(View.GONE);
|
||||
}
|
||||
viewModel.getAbout().observe(getViewLifecycleOwner(), this::presentAbout);
|
||||
viewModel.getAboutEmoji().observe(getViewLifecycleOwner(), this::presentAboutEmoji);
|
||||
|
||||
if (viewModel.shouldShowUsername()) {
|
||||
viewModel.getUsername().observe(getViewLifecycleOwner(), this::presentUsername);
|
||||
@@ -156,14 +159,26 @@ public class ManageProfileFragment extends LoggingFragment {
|
||||
|
||||
private void presentAbout(@Nullable String about) {
|
||||
if (about == null || about.isEmpty()) {
|
||||
aboutView.setHint(R.string.ManageProfileFragment_about);
|
||||
aboutView.setText(R.string.ManageProfileFragment_about);
|
||||
aboutView.setTextColor(requireContext().getResources().getColor(R.color.signal_text_secondary));
|
||||
} else {
|
||||
aboutView.setText(about);
|
||||
aboutView.setTextColor(requireContext().getResources().getColor(R.color.signal_text_primary));
|
||||
}
|
||||
}
|
||||
|
||||
private void presentAboutEmoji(@NonNull String aboutEmoji) {
|
||||
if (aboutEmoji == null || aboutEmoji.isEmpty()) {
|
||||
aboutEmojiView.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_compose_24, null));
|
||||
} else {
|
||||
Drawable emoji = EmojiUtil.convertToDrawable(requireContext(), aboutEmoji);
|
||||
|
||||
if (emoji != null) {
|
||||
aboutEmojiView.setImageDrawable(emoji);
|
||||
} else {
|
||||
aboutEmojiView.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_compose_24, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void presentEvent(@NonNull ManageProfileViewModel.Event event) {
|
||||
|
||||
@@ -100,10 +100,6 @@ class ManageProfileViewModel extends ViewModel {
|
||||
return FeatureFlags.usernames();
|
||||
}
|
||||
|
||||
public boolean shouldShowAbout() {
|
||||
return FeatureFlags.about();
|
||||
}
|
||||
|
||||
public void onAvatarSelected(@NonNull Context context, @Nullable Media media) {
|
||||
if (media == null) {
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
@@ -136,6 +132,8 @@ class ManageProfileViewModel extends ViewModel {
|
||||
private void onRecipientChanged(@NonNull Recipient recipient) {
|
||||
profileName.postValue(recipient.getProfileName());
|
||||
username.postValue(recipient.getUsername().orNull());
|
||||
about.postValue(recipient.getAbout());
|
||||
aboutEmoji.postValue(recipient.getAboutEmoji());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -45,9 +45,14 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
|
||||
EmojiPageViewGridAdapter.VariationSelectorListener
|
||||
{
|
||||
|
||||
private static final String REACTION_STORAGE_KEY = "reactions_recent_emoji";
|
||||
private static final String ABOUT_STORAGE_KEY = EmojiKeyboardProvider.RECENT_STORAGE_KEY;
|
||||
|
||||
private static final String ARG_MESSAGE_ID = "arg_message_id";
|
||||
private static final String ARG_IS_MMS = "arg_is_mms";
|
||||
private static final String ARG_START_PAGE = "arg_start_page";
|
||||
private static final String ARG_SHADOWS = "arg_shadows";
|
||||
private static final String ARG_RECENT_KEY = "arg_recent_key";
|
||||
|
||||
private ReactWithAnyEmojiViewModel viewModel;
|
||||
private TextSwitcher categoryLabel;
|
||||
@@ -65,6 +70,22 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
|
||||
args.putLong(ARG_MESSAGE_ID, messageRecord.getId());
|
||||
args.putBoolean(ARG_IS_MMS, messageRecord.isMms());
|
||||
args.putInt(ARG_START_PAGE, startingPage);
|
||||
args.putBoolean(ARG_SHADOWS, false);
|
||||
args.putString(ARG_RECENT_KEY, REACTION_STORAGE_KEY);
|
||||
fragment.setArguments(args);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public static DialogFragment createForAboutSelection() {
|
||||
DialogFragment fragment = new ReactWithAnyEmojiBottomSheetDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
|
||||
args.putLong(ARG_MESSAGE_ID, -1);
|
||||
args.putBoolean(ARG_IS_MMS, false);
|
||||
args.putInt(ARG_START_PAGE, -1);
|
||||
args.putBoolean(ARG_SHADOWS, true);
|
||||
args.putString(ARG_RECENT_KEY, ABOUT_STORAGE_KEY);
|
||||
fragment.setArguments(args);
|
||||
|
||||
return fragment;
|
||||
@@ -79,10 +100,13 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
boolean shadows = requireArguments().getBoolean(ARG_SHADOWS);
|
||||
if (ThemeUtil.isDarkTheme(requireContext())) {
|
||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.Theme_Signal_BottomSheetDialog_Fixed_ReactWithAny);
|
||||
setStyle(DialogFragment.STYLE_NORMAL, shadows ? R.style.Theme_Signal_BottomSheetDialog_Fixed_ReactWithAny
|
||||
: R.style.Theme_Signal_BottomSheetDialog_Fixed_ReactWithAny_Shadowless);
|
||||
} else {
|
||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.Theme_Signal_Light_BottomSheetDialog_Fixed_ReactWithAny);
|
||||
setStyle(DialogFragment.STYLE_NORMAL, shadows ? R.style.Theme_Signal_Light_BottomSheetDialog_Fixed_ReactWithAny
|
||||
: R.style.Theme_Signal_Light_BottomSheetDialog_Fixed_ReactWithAny_Shadowless);
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -168,15 +192,18 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
|
||||
if (savedInstanceState == null) {
|
||||
FrameLayout container = requireDialog().findViewById(R.id.container);
|
||||
LayoutInflater layoutInflater = LayoutInflater.from(requireContext());
|
||||
View statusBarShader = layoutInflater.inflate(R.layout.react_with_any_emoji_status_fade, container, false);
|
||||
TabLayout categoryTabs = (TabLayout) layoutInflater.inflate(R.layout.react_with_any_emoji_tabs, container, false);
|
||||
|
||||
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtil.getStatusBarHeight(container));
|
||||
|
||||
statusBarShader.setLayoutParams(params);
|
||||
container.addView(statusBarShader, 0);
|
||||
if (!requireArguments().getBoolean(ARG_SHADOWS)) {
|
||||
View statusBarShader = layoutInflater.inflate(R.layout.react_with_any_emoji_status_fade, container, false);
|
||||
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtil.getStatusBarHeight(container));
|
||||
|
||||
statusBarShader.setLayoutParams(params);
|
||||
container.addView(statusBarShader, 0);
|
||||
}
|
||||
|
||||
container.addView(categoryTabs);
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(container, (v, insets) -> insets.consumeSystemWindowInsets());
|
||||
|
||||
new TabLayoutMediator(categoryTabs, categoryPager, (tab, position) -> {
|
||||
@@ -203,7 +230,7 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
|
||||
|
||||
private void initializeViewModel() {
|
||||
Bundle args = requireArguments();
|
||||
ReactWithAnyEmojiRepository repository = new ReactWithAnyEmojiRepository(requireContext());
|
||||
ReactWithAnyEmojiRepository repository = new ReactWithAnyEmojiRepository(requireContext(), args.getString(ARG_RECENT_KEY));
|
||||
ReactWithAnyEmojiViewModel.Factory factory = new ReactWithAnyEmojiViewModel.Factory(reactionsLoader, repository, args.getLong(ARG_MESSAGE_ID), args.getBoolean(ARG_IS_MMS));
|
||||
|
||||
viewModel = ViewModelProviders.of(this, factory).get(ReactWithAnyEmojiViewModel.class);
|
||||
@@ -212,6 +239,7 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
|
||||
@Override
|
||||
public void onEmojiSelected(String emoji) {
|
||||
viewModel.onEmojiSelected(emoji);
|
||||
callback.onReactWithAnyEmojiSelected(emoji);
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@@ -239,7 +267,8 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
|
||||
}
|
||||
|
||||
private int getStartingPage(boolean firstPageHasContent) {
|
||||
return requireArguments().getInt(ARG_START_PAGE, firstPageHasContent ? 0 : 1);
|
||||
int startPage = requireArguments().getInt(ARG_START_PAGE);
|
||||
return startPage >= 0 ? startPage : (firstPageHasContent ? 0 : 1);
|
||||
}
|
||||
|
||||
private class OnPageChanged extends ViewPager2.OnPageChangeCallback {
|
||||
@@ -253,5 +282,6 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
|
||||
public interface Callback {
|
||||
void onReactWithAnyEmojiDialogDismissed();
|
||||
void onReactWithAnyEmojiPageChanged(int page);
|
||||
void onReactWithAnyEmojiSelected(@NonNull String emoji);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,15 +32,13 @@ final class ReactWithAnyEmojiRepository {
|
||||
|
||||
private static final String TAG = Log.tag(ReactWithAnyEmojiRepository.class);
|
||||
|
||||
private static final String RECENT_STORAGE_KEY = "reactions_recent_emoji";
|
||||
|
||||
private final Context context;
|
||||
private final RecentEmojiPageModel recentEmojiPageModel;
|
||||
private final Context context;
|
||||
private final RecentEmojiPageModel recentEmojiPageModel;
|
||||
private final List<ReactWithAnyEmojiPage> emojiPages;
|
||||
|
||||
ReactWithAnyEmojiRepository(@NonNull Context context) {
|
||||
ReactWithAnyEmojiRepository(@NonNull Context context, @NonNull String storageKey) {
|
||||
this.context = context;
|
||||
this.recentEmojiPageModel = new RecentEmojiPageModel(context, RECENT_STORAGE_KEY);
|
||||
this.recentEmojiPageModel = new RecentEmojiPageModel(context, storageKey);
|
||||
this.emojiPages = new LinkedList<>();
|
||||
|
||||
emojiPages.addAll(Stream.of(EmojiUtil.getDisplayPages())
|
||||
|
||||
@@ -36,8 +36,10 @@ public final class ReactWithAnyEmojiViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
void onEmojiSelected(@NonNull String emoji) {
|
||||
SignalStore.emojiValues().setPreferredVariation(emoji);
|
||||
repository.addEmojiToMessage(emoji, messageId, isMms);
|
||||
if (messageId > 0) {
|
||||
SignalStore.emojiValues().setPreferredVariation(emoji);
|
||||
repository.addEmojiToMessage(emoji, messageId, isMms);
|
||||
}
|
||||
}
|
||||
|
||||
static class Factory implements ViewModelProvider.Factory {
|
||||
|
||||
@@ -108,6 +108,8 @@ public class Recipient {
|
||||
private final byte[] storageId;
|
||||
private final MentionSetting mentionSetting;
|
||||
private final ChatWallpaper wallpaper;
|
||||
private final String about;
|
||||
private final String aboutEmoji;
|
||||
|
||||
|
||||
/**
|
||||
@@ -342,6 +344,8 @@ public class Recipient {
|
||||
this.storageId = null;
|
||||
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
||||
this.wallpaper = null;
|
||||
this.about = null;
|
||||
this.aboutEmoji = null;
|
||||
}
|
||||
|
||||
public Recipient(@NonNull RecipientId id, @NonNull RecipientDetails details, boolean resolved) {
|
||||
@@ -385,6 +389,8 @@ public class Recipient {
|
||||
this.storageId = details.storageId;
|
||||
this.mentionSetting = details.mentionSetting;
|
||||
this.wallpaper = details.wallpaper;
|
||||
this.about = details.about;
|
||||
this.aboutEmoji = details.aboutEmoji;
|
||||
}
|
||||
|
||||
public @NonNull RecipientId getId() {
|
||||
@@ -870,6 +876,28 @@ public class Recipient {
|
||||
return contactUri != null;
|
||||
}
|
||||
|
||||
public @Nullable String getAbout() {
|
||||
return about;
|
||||
}
|
||||
|
||||
public @Nullable String getAboutEmoji() {
|
||||
return aboutEmoji;
|
||||
}
|
||||
|
||||
public @Nullable String getCombinedAboutAndEmoji() {
|
||||
if (aboutEmoji != null) {
|
||||
if (about != null) {
|
||||
return aboutEmoji + " " + about;
|
||||
} else {
|
||||
return aboutEmoji;
|
||||
}
|
||||
} else if (about != null) {
|
||||
return about;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If this recipient is missing crucial data, this will return a populated copy. Otherwise it
|
||||
* returns itself.
|
||||
@@ -985,7 +1013,9 @@ public class Recipient {
|
||||
insightsBannerTier == other.insightsBannerTier &&
|
||||
Arrays.equals(storageId, other.storageId) &&
|
||||
mentionSetting == other.mentionSetting &&
|
||||
Objects.equals(wallpaper, other.wallpaper);
|
||||
Objects.equals(wallpaper, other.wallpaper) &&
|
||||
Objects.equals(about, other.about) &&
|
||||
Objects.equals(aboutEmoji, other.aboutEmoji);
|
||||
}
|
||||
|
||||
private static boolean allContentsAreTheSame(@NonNull List<Recipient> a, @NonNull List<Recipient> b) {
|
||||
|
||||
@@ -67,6 +67,8 @@ public class RecipientDetails {
|
||||
final byte[] storageId;
|
||||
final MentionSetting mentionSetting;
|
||||
final ChatWallpaper wallpaper;
|
||||
final String about;
|
||||
final String aboutEmoji;
|
||||
|
||||
public RecipientDetails(@Nullable String name,
|
||||
@NonNull Optional<Long> groupAvatarId,
|
||||
@@ -113,6 +115,8 @@ public class RecipientDetails {
|
||||
this.storageId = settings.getStorageId();
|
||||
this.mentionSetting = settings.getMentionSetting();
|
||||
this.wallpaper = settings.getWallpaper();
|
||||
this.about = settings.getAbout();
|
||||
this.aboutEmoji = settings.getAboutEmoji();
|
||||
|
||||
if (name == null) this.name = settings.getSystemDisplayName();
|
||||
else this.name = name;
|
||||
@@ -161,6 +165,8 @@ public class RecipientDetails {
|
||||
this.storageId = null;
|
||||
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
||||
this.wallpaper = null;
|
||||
this.about = null;
|
||||
this.aboutEmoji = null;
|
||||
}
|
||||
|
||||
public static @NonNull RecipientDetails forIndividual(@NonNull Context context, @NonNull RecipientSettings settings) {
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.thoughtcrime.securesms.recipients.RecipientExporter;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.thoughtcrime.securesms.util.BottomSheetUtil;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
@@ -55,6 +56,7 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
||||
private RecipientDialogViewModel viewModel;
|
||||
private AvatarImageView avatar;
|
||||
private TextView fullName;
|
||||
private TextView about;
|
||||
private TextView usernameNumber;
|
||||
private Button messageButton;
|
||||
private Button secureCallButton;
|
||||
@@ -102,6 +104,7 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
||||
|
||||
avatar = view.findViewById(R.id.rbs_recipient_avatar);
|
||||
fullName = view.findViewById(R.id.rbs_full_name);
|
||||
about = view.findViewById(R.id.rbs_about);
|
||||
usernameNumber = view.findViewById(R.id.rbs_username_number);
|
||||
messageButton = view.findViewById(R.id.rbs_message_button);
|
||||
secureCallButton = view.findViewById(R.id.rbs_secure_call_button);
|
||||
@@ -158,6 +161,14 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
||||
TextViewCompat.setCompoundDrawableTintList(fullName, ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.signal_text_primary)));
|
||||
}
|
||||
|
||||
String aboutText = recipient.getCombinedAboutAndEmoji();
|
||||
if (!Util.isEmpty(aboutText)) {
|
||||
about.setText(aboutText);
|
||||
about.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
about.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
String usernameNumberString = recipient.hasAUserSetDisplayName(requireContext()) && !recipient.isSelf()
|
||||
? recipient.getSmsAddress().transform(PhoneNumberFormatter::prettyPrint).or("").trim()
|
||||
: "";
|
||||
|
||||
@@ -51,6 +51,7 @@ import org.thoughtcrime.securesms.recipients.RecipientExporter;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.ui.notifications.CustomNotificationsDialogFragment;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.LifecycleCursorWrapper;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
@@ -71,6 +72,7 @@ public class ManageRecipientFragment extends LoggingFragment {
|
||||
private GroupMemberListView sharedGroupList;
|
||||
private Toolbar toolbar;
|
||||
private TextView title;
|
||||
private TextView about;
|
||||
private TextView subtitle;
|
||||
private ViewGroup internalDetails;
|
||||
private TextView internalDetailsText;
|
||||
@@ -132,6 +134,7 @@ public class ManageRecipientFragment extends LoggingFragment {
|
||||
contactText = view.findViewById(R.id.recipient_contact_text);
|
||||
contactIcon = view.findViewById(R.id.recipient_contact_icon);
|
||||
title = view.findViewById(R.id.name);
|
||||
about = view.findViewById(R.id.about);
|
||||
subtitle = view.findViewById(R.id.username_number);
|
||||
internalDetails = view.findViewById(R.id.recipient_internal_details);
|
||||
internalDetailsText = view.findViewById(R.id.recipient_internal_details_text);
|
||||
@@ -303,6 +306,10 @@ public class ManageRecipientFragment extends LoggingFragment {
|
||||
});
|
||||
}
|
||||
|
||||
String aboutText = recipient.getCombinedAboutAndEmoji();
|
||||
about.setText(aboutText);
|
||||
about.setVisibility(Util.isEmpty(aboutText) ? View.GONE : View.VISIBLE);
|
||||
|
||||
disappearingMessagesCard.setVisibility(recipient.isRegistered() ? View.VISIBLE : View.GONE);
|
||||
addToAGroup.setVisibility(recipient.isRegistered() ? View.VISIBLE : View.GONE);
|
||||
|
||||
|
||||
@@ -71,7 +71,6 @@ public final class FeatureFlags {
|
||||
private static final String AUTOMATIC_SESSION_INTERVAL = "android.automaticSessionResetInterval";
|
||||
private static final String DEFAULT_MAX_BACKOFF = "android.defaultMaxBackoff";
|
||||
private static final String OKHTTP_AUTOMATIC_RETRY = "android.okhttpAutomaticRetry";
|
||||
private static final String ABOUT = "android.about";
|
||||
private static final String SHARE_SELECTION_LIMIT = "android.share.limit";
|
||||
|
||||
/**
|
||||
@@ -101,7 +100,6 @@ public final class FeatureFlags {
|
||||
AUTOMATIC_SESSION_INTERVAL,
|
||||
DEFAULT_MAX_BACKOFF,
|
||||
OKHTTP_AUTOMATIC_RETRY,
|
||||
ABOUT,
|
||||
SHARE_SELECTION_LIMIT
|
||||
);
|
||||
|
||||
@@ -141,7 +139,6 @@ public final class FeatureFlags {
|
||||
AUTOMATIC_SESSION_INTERVAL,
|
||||
DEFAULT_MAX_BACKOFF,
|
||||
OKHTTP_AUTOMATIC_RETRY,
|
||||
ABOUT,
|
||||
SHARE_SELECTION_LIMIT
|
||||
);
|
||||
|
||||
@@ -327,11 +324,6 @@ public final class FeatureFlags {
|
||||
return getBoolean(OKHTTP_AUTOMATIC_RETRY, false);
|
||||
}
|
||||
|
||||
/** Whether or not the 'About' section of the profile is enabled. */
|
||||
public static boolean about() {
|
||||
return getBoolean(ABOUT, false);
|
||||
}
|
||||
|
||||
/** Only for rendering debug info. */
|
||||
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
||||
return new TreeMap<>(REMOTE_VALUES);
|
||||
|
||||
Reference in New Issue
Block a user