Add support for an 'About' field on your profile.

fork-5.53.8
Greyson Parrelli 2021-01-21 12:35:00 -05:00
rodzic e80033c287
commit 7db16e6156
42 zmienionych plików z 709 dodań i 119 usunięć

Wyświetl plik

@ -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;

Wyświetl plik

@ -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);
}
}
}

Wyświetl plik

@ -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 });
}

Wyświetl plik

@ -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();

Wyświetl plik

@ -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);

Wyświetl plik

@ -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");

Wyświetl plik

@ -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();

Wyświetl plik

@ -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);

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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();

Wyświetl plik

@ -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,

Wyświetl plik

@ -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);

Wyświetl plik

@ -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));
}

Wyświetl plik

@ -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;

Wyświetl plik

@ -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());
}
}
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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())

Wyświetl plik

@ -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 {

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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()
: "";

Wyświetl plik

@ -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);

Wyświetl plik

@ -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);

Wyświetl plik

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="23dp"
android:height="18dp"
android:viewportWidth="23"
android:viewportHeight="18">
<path
android:pathData="M14,18C12.7735,18.0051 11.5592,17.757 10.433,17.2713C9.3068,16.7855 8.293,16.0725 7.455,15.177L8.545,14.148C9.2437,14.894 10.0887,15.488 11.0273,15.8927C11.9659,16.2974 12.9779,16.5041 14,16.5C15.9891,16.5 17.8968,15.7098 19.3033,14.3033C20.7098,12.8967 21.5,10.9891 21.5,9C21.5,7.0108 20.7098,5.1032 19.3033,3.6967C17.8968,2.2901 15.9891,1.5 14,1.5C12.9779,1.4958 11.9659,1.7025 11.0273,2.1072C10.0887,2.5119 9.2437,3.1059 8.545,3.852L7.455,2.823C8.293,1.9274 9.3068,1.2144 10.433,0.7286C11.5592,0.2429 12.7735,-0.0052 14,-0C16.3869,-0 18.6761,0.9482 20.364,2.636C22.0518,4.3238 23,6.613 23,9C23,11.3869 22.0518,13.6761 20.364,15.3639C18.6761,17.0517 16.3869,18 14,18ZM18.608,12.188C18.7242,12.0267 18.7715,11.8259 18.7396,11.6297C18.7078,11.4336 18.5993,11.2581 18.438,11.142C18.2767,11.0258 18.076,10.9784 17.8798,11.0103C17.6836,11.0422 17.5082,11.1507 17.392,11.312C16.9951,11.8352 16.4827,12.2597 15.8949,12.5524C15.307,12.8452 14.6594,12.9982 14.0027,12.9997C13.346,13.0011 12.6978,12.8509 12.1086,12.5608C11.5194,12.2707 11.0052,11.8484 10.606,11.327C10.5481,11.2474 10.4751,11.18 10.3911,11.1286C10.3071,11.0773 10.2139,11.0429 10.1167,11.0276C10.0194,11.0123 9.9202,11.0163 9.8245,11.0393C9.7288,11.0624 9.6386,11.104 9.559,11.162C9.4794,11.2199 9.412,11.2929 9.3607,11.3768C9.3093,11.4608 9.275,11.5541 9.2597,11.6513C9.2443,11.7485 9.2483,11.8478 9.2714,11.9435C9.2944,12.0392 9.3361,12.1294 9.394,12.209C9.9324,12.9224 10.6295,13.5007 11.43,13.8983C12.2304,14.2959 13.1125,14.5018 14.0062,14.4998C14.9,14.4978 15.7811,14.2878 16.5798,13.8866C17.3784,13.4853 18.0728,12.9038 18.608,12.188ZM11.25,5.75C10.8717,5.8061 10.53,6.0069 10.2968,6.31C10.0637,6.6131 9.9572,6.9949 10,7.375C9.9572,7.755 10.0637,8.1368 10.2968,8.4399C10.53,8.743 10.8717,8.9438 11.25,9C11.6283,8.9438 11.97,8.743 12.2032,8.4399C12.4363,8.1368 12.5428,7.755 12.5,7.375C12.5428,6.9949 12.4363,6.6131 12.2032,6.31C11.97,6.0069 11.6283,5.8061 11.25,5.75ZM16.75,5.75C16.3717,5.8061 16.03,6.0069 15.7968,6.31C15.5637,6.6131 15.4572,6.9949 15.5,7.375C15.4572,7.755 15.5637,8.1368 15.7968,8.4399C16.03,8.743 16.3717,8.9438 16.75,9C17.1283,8.9438 17.47,8.743 17.7032,8.4399C17.9363,8.1368 18.0428,7.755 18,7.375C18.0428,6.9949 17.9363,6.6131 17.7032,6.31C17.47,6.0069 17.1283,5.8061 16.75,5.75ZM8,8.25H4.75V5H3.25V8.25H0V9.75H3.25V13H4.75V9.75H8V8.25Z"
android:fillColor="#000000"/>
</vector>

Wyświetl plik

@ -59,7 +59,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView android:id="@+id/number"
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textDirection="ltr"

Wyświetl plik

@ -29,6 +29,17 @@
app:layout_constraintTop_toBottomOf="@id/message_request_avatar"
tools:text="Cayce Pollard" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/message_request_about"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:gravity="center"
android:textAppearance="@style/Signal.Text.MessageRequest.Subtitle"
tools:text="Hangin' on the web"
app:layout_constraintTop_toBottomOf="@id/message_request_title" />
<TextView
android:id="@+id/message_request_subtitle"
android:layout_width="wrap_content"
@ -38,7 +49,7 @@
android:textAppearance="@style/Signal.Text.MessageRequest.Subtitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/message_request_title"
app:layout_constraintTop_toBottomOf="@id/message_request_about"
tools:text="\@caycepollard" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView

Wyświetl plik

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="@drawable/ic_arrow_left_24"
app:title="@string/EditAboutFragment_about" />
<ImageView
android:id="@+id/edit_about_emoji"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:padding="15dp"
android:background="?selectableItemBackgroundBorderless"
app:srcCompat="@drawable/ic_add_emoji"
app:tint="@color/signal_text_secondary"
app:layout_constraintTop_toTopOf="@id/edit_about_body"
app:layout_constraintBottom_toBottomOf="@id/edit_about_body"
app:layout_constraintStart_toStartOf="parent"/>
<org.thoughtcrime.securesms.components.emoji.EmojiEditText
android:id="@+id/edit_about_body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
style="@style/Signal.Text.Body"
android:hint="@string/EditAboutFragment_write_a_few_words_about_yourself"
android:inputType="textCapSentences"
android:maxLines="1"
app:layout_constraintTop_toBottomOf="@id/toolbar"
app:layout_constraintStart_toEndOf="@id/edit_about_emoji"
app:layout_constraintEnd_toEndOf="parent"/>
<TextView
android:id="@+id/edit_about_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
style="@style/Signal.Text.Caption"
app:layout_constraintTop_toBottomOf="@id/edit_about_body"
app:layout_constraintEnd_toEndOf="@id/edit_about_body"
tools:text="75/100"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/edit_about_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
style="@style/Signal.Widget.Button.Medium.Primary"
android:text="@string/EditProfileNameFragment_save"
app:cornerRadius="80dp"
app:elevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Wyświetl plik

@ -39,12 +39,31 @@
android:gravity="start|center_vertical"
android:textAlignment="viewStart"
android:textAppearance="@style/Signal.Text.Body"
app:layout_constraintBottom_toBottomOf="@+id/recipient_avatar"
app:layout_constraintBottom_toTopOf="@+id/recipient_about"
app:layout_constraintEnd_toStartOf="@+id/admin"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@+id/recipient_avatar"
app:layout_constraintTop_toTopOf="@+id/recipient_avatar"
tools:text="@tools:sample/full_names" />
tools:text="Miles Morales" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/recipient_about"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:gravity="start|center_vertical"
android:textAlignment="viewStart"
android:textAppearance="@style/Signal.Text.Preview"
android:textColor="@color/signal_text_secondary"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintBottom_toBottomOf="@+id/recipient_avatar"
app:layout_constraintEnd_toStartOf="@+id/admin"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@+id/recipient_avatar"
app:layout_constraintTop_toBottomOf="@+id/recipient_name"
tools:text="Hangin' around the web" />
<TextView
android:id="@+id/admin"
@ -56,11 +75,11 @@
android:textAppearance="@style/TextAppearance.Signal.Subtitle2"
android:textColor="@color/signal_text_secondary"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/recipient_name"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/popupMenuProgressContainer"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@+id/recipient_name"
app:layout_constraintTop_toTopOf="@+id/recipient_name"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<FrameLayout

Wyświetl plik

@ -93,7 +93,7 @@
app:layout_constraintTop_toTopOf="@id/manage_profile_name"
app:layout_constraintBottom_toBottomOf="@id/manage_profile_name_subtitle"/>
<TextView
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/manage_profile_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
@ -138,7 +138,7 @@
app:layout_constraintTop_toTopOf="@id/manage_profile_username"
app:layout_constraintBottom_toBottomOf="@id/manage_profile_username_subtitle"/>
<TextView
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/manage_profile_username"
android:layout_width="0dp"
android:layout_height="wrap_content"
@ -183,7 +183,7 @@
app:layout_constraintBottom_toBottomOf="@id/manage_profile_about_subtitle"
app:layout_constraintStart_toStartOf="parent"/>
<TextView
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/manage_profile_about"
android:layout_width="0dp"
android:layout_height="wrap_content"
@ -192,7 +192,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/manage_profile_about_icon"
app:layout_constraintEnd_toEndOf="parent"
tools:text="Reporter for the Daily Bugle"/>
tools:text="Photographer for the Daily Bugle"/>
<TextView
android:id="@+id/manage_profile_about_subtitle"

Wyświetl plik

@ -41,6 +41,16 @@
app:layout_constraintTop_toBottomOf="@id/rbs_recipient_avatar"
tools:text="Gwen Stacy" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/rbs_about"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@color/signal_text_secondary"
style="@style/Signal.Text.Body"
app:layout_constraintTop_toBottomOf="@id/rbs_full_name"
tools:text="🕷🕷🕷Hangin' on the web🕷🕷"/>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/rbs_username_number"
style="@style/Signal.Text.Body"
@ -50,7 +60,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/rbs_full_name"
app:layout_constraintTop_toBottomOf="@+id/rbs_about"
tools:text="\@spidergwen +1 555-654-6657" />
<TextView

Wyświetl plik

@ -46,6 +46,17 @@
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Signal.Body1.Bold" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/about"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:textAppearance="@style/Signal.Text.Body"
android:textColor="@color/signal_text_secondary"
tools:text="Hangin' around the web" />
<TextView
android:id="@+id/username_number"
android:layout_width="wrap_content"

Wyświetl plik

@ -14,18 +14,26 @@
<action
android:id="@+id/action_manageUsername"
app:destination="@id/usernameManageFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
app:enterAnim="@anim/slide_from_end"
app:exitAnim="@anim/slide_to_start"
app:popEnterAnim="@anim/slide_from_start"
app:popExitAnim="@anim/slide_to_end" />
<action
android:id="@+id/action_manageProfileName"
app:destination="@id/profileNameManageFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
app:enterAnim="@anim/slide_from_end"
app:exitAnim="@anim/slide_to_start"
app:popEnterAnim="@anim/slide_from_start"
app:popExitAnim="@anim/slide_to_end" />
<action
android:id="@+id/action_manageAbout"
app:destination="@id/aboutManageFragment"
app:enterAnim="@anim/slide_from_end"
app:exitAnim="@anim/slide_to_start"
app:popEnterAnim="@anim/slide_from_start"
app:popExitAnim="@anim/slide_to_end" />
</fragment>
@ -41,4 +49,10 @@
android:label="fragment_manage_profile_name"
tools:layout="@layout/edit_profile_name_fragment" />
<fragment
android:id="@+id/aboutManageFragment"
android:name="org.thoughtcrime.securesms.profiles.manage.EditAboutFragment"
android:label="fragment_manage_about"
tools:layout="@layout/edit_about_fragment" />
</navigation>

Wyświetl plik

@ -1999,6 +1999,11 @@
<string name="CreateProfileActivity__create_a_username">Create a username</string>
<string name="CreateProfileActivity_custom_mms_group_names_and_photos_will_only_be_visible_to_you">Custom MMS group names and photos will only be visible to you.</string>
<!-- EditAboutFragment -->
<string name="EditAboutFragment_about">About</string>
<string name="EditAboutFragment_write_a_few_words_about_yourself">Write a few words about yourself…</string>
<string name="EditAboutFragment_count">%d/%d</string>
<!-- EditProfileFragment -->
<string name="EditProfileFragment__edit_group_name_and_photo">Edit group name and photo</string>
<string name="EditProfileFragment__group_name">Group name</string>

Wyświetl plik

@ -321,15 +321,23 @@
<style name="Theme.Signal.BottomSheetDialog.Fixed.ReactWithAny">
<item name="bottomSheetStyle">@style/Widget.Signal.BottomSheet.ReactWithAny</item>
<item name="android:backgroundDimEnabled">true</item>
</style>
<style name="Theme.Signal.BottomSheetDialog.Fixed.ReactWithAny.Shadowless">
<item name="android:backgroundDimEnabled">false</item>
</style>
<style name="Theme.Signal.Light.BottomSheetDialog.Fixed.ReactWithAny">
<item name="bottomSheetStyle">@style/Widget.Signal.BottomSheet.ReactWithAny.Light</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="shapeAppearanceOverlay">@style/ShapeAppearanceOverlay.Signal.BottomSheet.Rounded</item>
</style>
<style name="Theme.Signal.Light.BottomSheetDialog.Fixed.ReactWithAny.Shadowless">
<item name="android:backgroundDimEnabled">false</item>
</style>
<style name="Signal.DayNight.Dialog.Animated" parent="Signal.DayNight">
<item name="android:windowAnimationStyle">@style/FadeScale</item>
</style>

Wyświetl plik

@ -21,6 +21,7 @@ import org.whispersystems.libsignal.state.SignedPreKeyRecord;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException;
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
import org.whispersystems.signalservice.api.crypto.ProfileCipherInputStream;
import org.whispersystems.signalservice.api.crypto.ProfileCipherOutputStream;
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api;
@ -634,12 +635,14 @@ public class SignalServiceAccountManager {
/**
* @return The avatar URL path, if one was written.
*/
public Optional<String> setVersionedProfile(UUID uuid, ProfileKey profileKey, String name, StreamDetails avatar)
public Optional<String> setVersionedProfile(UUID uuid, ProfileKey profileKey, String name, String about, String aboutEmoji, StreamDetails avatar)
throws IOException
{
if (name == null) name = "";
byte[] ciphertextName = new ProfileCipher(profileKey).encryptName(name.getBytes(StandardCharsets.UTF_8), ProfileCipher.getTargetNameLength(name));
byte[] ciphertextAbout = new ProfileCipher(profileKey).encryptName(about.getBytes(StandardCharsets.UTF_8), ProfileCipher.getTargetAboutLength(about));
byte[] ciphertextEmoji = new ProfileCipher(profileKey).encryptName(aboutEmoji.getBytes(StandardCharsets.UTF_8), ProfileCipher.EMOJI_PADDED_LENGTH);
boolean hasAvatar = avatar != null;
ProfileAvatarData profileAvatarData = null;
@ -652,6 +655,8 @@ public class SignalServiceAccountManager {
return this.pushServiceSocket.writeProfile(new SignalServiceProfileWrite(profileKey.getProfileKeyVersion(uuid).serialize(),
ciphertextName,
ciphertextAbout,
ciphertextEmoji,
hasAvatar,
profileKey.getCommitment(uuid).serialize()),
profileAvatarData);

Wyświetl plik

@ -23,8 +23,13 @@ public class ProfileCipher {
private static final int NAME_PADDED_LENGTH_1 = 53;
private static final int NAME_PADDED_LENGTH_2 = 257;
private static final int ABOUT_PADDED_LENGTH_1 = 128;
private static final int ABOUT_PADDED_LENGTH_2 = 254;
private static final int ABOUT_PADDED_LENGTH_3 = 512;
public static final int MAX_POSSIBLE_NAME_LENGTH = NAME_PADDED_LENGTH_2;
public static final int MAX_POSSIBLE_NAME_LENGTH = NAME_PADDED_LENGTH_2;
public static final int MAX_POSSIBLE_ABOUT_LENGTH = ABOUT_PADDED_LENGTH_3;
public static final int EMOJI_PADDED_LENGTH = 32;
private final ProfileKey key;
@ -112,4 +117,16 @@ public class ProfileCipher {
return NAME_PADDED_LENGTH_2;
}
}
public static int getTargetAboutLength(String about) {
int aboutLength = about.getBytes(StandardCharsets.UTF_8).length;
if (aboutLength <= ABOUT_PADDED_LENGTH_1) {
return ABOUT_PADDED_LENGTH_1;
} else if (aboutLength < ABOUT_PADDED_LENGTH_2){
return ABOUT_PADDED_LENGTH_2;
} else {
return ABOUT_PADDED_LENGTH_3;
}
}
}

Wyświetl plik

@ -29,6 +29,12 @@ public class SignalServiceProfile {
@JsonProperty
private String name;
@JsonProperty
private String about;
@JsonProperty
private String aboutEmoji;
@JsonProperty
private String avatar;
@ -62,6 +68,14 @@ public class SignalServiceProfile {
return name;
}
public String getAbout() {
return about;
}
public String getAboutEmoji() {
return aboutEmoji;
}
public String getAvatar() {
return avatar;
}

Wyświetl plik

@ -11,6 +11,12 @@ public class SignalServiceProfileWrite {
@JsonProperty
private byte[] name;
@JsonProperty
private byte[] about;
@JsonProperty
private byte[] aboutEmoji;
@JsonProperty
private boolean avatar;
@ -21,9 +27,11 @@ public class SignalServiceProfileWrite {
public SignalServiceProfileWrite(){
}
public SignalServiceProfileWrite(String version, byte[] name, boolean avatar, byte[] commitment) {
public SignalServiceProfileWrite(String version, byte[] name, byte[] about, byte[] aboutEmoji, boolean avatar, byte[] commitment) {
this.version = version;
this.name = name;
this.about = about;
this.aboutEmoji = aboutEmoji;
this.avatar = avatar;
this.commitment = commitment;
}