NotificationsAdapter: implement custom emoji reacts
If the emoji_url field is specified in the notification data, it will be used to represent the emoji in the notification text. As such, the Notification entity data class has been updated accordingly, to match the current implementation at AkkomaDev/Akkoma, which may later be integrated by more Pleroma-based backends. The SpannableBuilder formatting is a bit hacky, but it works. Ideally, something like Phrase should be used for localization and Spannable formatting. However, this would be a different undertaking of its own, and would require more work on refactoring multiple parts of the application code. Signed-off-by: Hélène <pleroma-dev@helene.moe> Signed-off-by: Adolfo Santiago <epoch@nixnetmail.com>
This commit is contained in:
parent
e6b402b377
commit
b85335e8d4
5 changed files with 50 additions and 18 deletions
|
@ -540,8 +540,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
Notification.Type type = notificationViewData.getType();
|
||||
|
||||
Context context = message.getContext();
|
||||
String wholeMessage;
|
||||
Drawable icon;
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
switch (type) {
|
||||
default:
|
||||
case FAVOURITE: {
|
||||
|
@ -552,7 +552,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
}
|
||||
|
||||
String format = context.getString(R.string.notification_favourite_format);
|
||||
wholeMessage = String.format(format, displayName);
|
||||
builder.append(String.format(format, displayName));
|
||||
break;
|
||||
}
|
||||
case REBLOG: {
|
||||
|
@ -563,7 +563,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
}
|
||||
|
||||
String format = context.getString(R.string.notification_reblog_format);
|
||||
wholeMessage = String.format(format, displayName);
|
||||
builder.append(String.format(format, displayName));
|
||||
break;
|
||||
}
|
||||
case STATUS: {
|
||||
|
@ -574,7 +574,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
}
|
||||
|
||||
String format = context.getString(R.string.notification_subscription_format);
|
||||
wholeMessage = String.format(format, displayName);
|
||||
builder.append(String.format(format, displayName));
|
||||
break;
|
||||
}
|
||||
case EMOJI_REACTION: {
|
||||
|
@ -586,15 +586,26 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
|
||||
String format = context.getString(R.string.notification_emoji_format);
|
||||
String emojiCode = notificationViewData.getEmoji();
|
||||
wholeMessage = String.format(format, displayName, emojiCode);
|
||||
builder.append(String.format(format, displayName, emojiCode));
|
||||
|
||||
final String emojiUrl = notificationViewData.getEmojiUrl();
|
||||
if(emojiUrl != null) {
|
||||
// terrible hack... ideally, there should be a CharSequence formatter
|
||||
final int emojiPosition = format.indexOf("%s", 1)
|
||||
- "%s".length() + displayName.length();
|
||||
|
||||
var span = CustomEmojiHelper.createEmojiSpan(emojiUrl, message, true);
|
||||
builder.setSpan(span, emojiPosition, emojiPosition + emojiCode.length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
message.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
|
||||
final SpannableString str = new SpannableString(wholeMessage);
|
||||
str.setSpan(new StyleSpan(Typeface.BOLD), 0, displayName.length(),
|
||||
builder.setSpan(new StyleSpan(Typeface.BOLD), 0, displayName.length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
CharSequence emojifiedText = CustomEmojiHelper.emojify(str, notificationViewData.getAccount().getEmojis(), message, true);
|
||||
CharSequence emojifiedText = CustomEmojiHelper.emojify(builder, notificationViewData.getAccount().getEmojis(), message, true);
|
||||
message.setText(emojifiedText);
|
||||
|
||||
if (statusViewData != null) {
|
||||
|
|
|
@ -31,6 +31,7 @@ data class Notification(
|
|||
val status: Status?,
|
||||
val pleroma: PleromaNotification? = null,
|
||||
val emoji: String? = null,
|
||||
@SerializedName("emoji_url") val emojiUrl: String? = null,
|
||||
@SerializedName("chat_message") val chatMessage: ChatMessage? = null,
|
||||
@SerializedName("created_at") val createdAt: Date? = null,
|
||||
val target: Account? = null) {
|
||||
|
|
|
@ -505,7 +505,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
|
||||
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
|
||||
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget());
|
||||
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(),
|
||||
viewdata.getEmojiUrl(), viewdata.getTarget());
|
||||
notifications.setPairedItem(position, newViewData);
|
||||
updateAdapter();
|
||||
}
|
||||
|
@ -539,7 +540,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
|
||||
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
|
||||
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget());
|
||||
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(),
|
||||
viewdata.getEmojiUrl(), viewdata.getTarget());
|
||||
|
||||
notifications.setPairedItem(position, newViewData);
|
||||
updateAdapter();
|
||||
|
@ -574,7 +576,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
|
||||
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
|
||||
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget());
|
||||
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(),
|
||||
viewdata.getEmojiUrl(), viewdata.getTarget());
|
||||
|
||||
notifications.setPairedItem(position, newViewData);
|
||||
updateAdapter();
|
||||
|
@ -603,7 +606,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
|
||||
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
|
||||
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget());
|
||||
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(),
|
||||
viewdata.getEmojiUrl(), viewdata.getTarget());
|
||||
|
||||
notifications.setPairedItem(position, newViewData);
|
||||
updateAdapter();
|
||||
|
@ -650,7 +654,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
.setIsExpanded(expanded)
|
||||
.createStatusViewData();
|
||||
NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(),
|
||||
old.getId(), old.getAccount(), statusViewData, old.getEmoji(), old.getTarget());
|
||||
old.getId(), old.getAccount(), statusViewData, old.getEmoji(),
|
||||
old.getEmojiUrl(), old.getTarget());
|
||||
notifications.setPairedItem(position, notificationViewData);
|
||||
updateAdapter();
|
||||
}
|
||||
|
@ -664,7 +669,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
.setIsShowingSensitiveContent(isShowing)
|
||||
.createStatusViewData();
|
||||
NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(),
|
||||
old.getId(), old.getAccount(), statusViewData, old.getEmoji(), old.getTarget());
|
||||
old.getId(), old.getAccount(), statusViewData, old.getEmoji(),
|
||||
old.getEmojiUrl(), old.getTarget());
|
||||
notifications.setPairedItem(position, notificationViewData);
|
||||
updateAdapter();
|
||||
}
|
||||
|
@ -678,7 +684,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
.setMuted(isMuted)
|
||||
.createStatusViewData();
|
||||
NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(),
|
||||
old.getId(), old.getAccount(), statusViewData, old.getEmoji(), old.getTarget());
|
||||
old.getId(), old.getAccount(), statusViewData, old.getEmoji(),
|
||||
old.getEmojiUrl(), old.getTarget());
|
||||
notifications.setPairedItem(position, notificationViewData);
|
||||
updateAdapter();
|
||||
}
|
||||
|
@ -694,7 +701,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
|
||||
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
|
||||
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget());
|
||||
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(),
|
||||
viewdata.getEmojiUrl(), viewdata.getTarget());
|
||||
|
||||
notifications.setPairedItem(position, newViewData);
|
||||
}
|
||||
|
@ -751,6 +759,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
concreteNotification.getAccount(),
|
||||
updatedStatus,
|
||||
concreteNotification.getEmoji(),
|
||||
concreteNotification.getEmojiUrl(),
|
||||
concreteNotification.getTarget()
|
||||
);
|
||||
notifications.setPairedItem(position, updatedNotification);
|
||||
|
@ -1445,7 +1454,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
|
||||
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
|
||||
ViewDataUtils.statusToViewData(newStatus, false, false),
|
||||
viewdata.getEmoji(), viewdata.getTarget());
|
||||
viewdata.getEmoji(), viewdata.getEmojiUrl(), viewdata.getTarget());
|
||||
|
||||
notifications.setPairedItem(position, newViewData);
|
||||
updateAdapter();
|
||||
|
|
|
@ -94,6 +94,7 @@ public final class ViewDataUtils {
|
|||
alwaysOpenSpoiler
|
||||
),
|
||||
notification.getEmoji(),
|
||||
notification.getEmojiUrl(),
|
||||
notification.getTarget()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -50,16 +50,20 @@ public abstract class NotificationViewData {
|
|||
@Nullable
|
||||
private final String emoji;
|
||||
@Nullable
|
||||
private final String emojiUrl;
|
||||
@Nullable
|
||||
private final Account target; // move notification
|
||||
|
||||
public Concrete(Notification.Type type, String id, Account account,
|
||||
@Nullable StatusViewData.Concrete statusViewData,
|
||||
@Nullable String emoji, @Nullable Account target) {
|
||||
@Nullable String emoji, @Nullable String emojiUrl,
|
||||
@Nullable Account target) {
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.account = account;
|
||||
this.statusViewData = statusViewData;
|
||||
this.emoji = emoji;
|
||||
this.emojiUrl = emojiUrl;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
|
@ -85,6 +89,11 @@ public abstract class NotificationViewData {
|
|||
return emoji;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getEmojiUrl() {
|
||||
return emojiUrl;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Account getTarget() {
|
||||
return target;
|
||||
|
@ -104,6 +113,7 @@ public abstract class NotificationViewData {
|
|||
Objects.equals(id, concrete.id) &&
|
||||
account.getId().equals(concrete.account.getId()) &&
|
||||
(emoji != null && concrete.emoji != null && emoji.equals(concrete.emoji)) &&
|
||||
(emojiUrl != null && concrete.emojiUrl != null && emojiUrl.equals(concrete.emojiUrl)) &&
|
||||
(target != null && concrete.target != null && target.getId().equals(concrete.target.getId())) &&
|
||||
(statusViewData == concrete.statusViewData ||
|
||||
statusViewData != null &&
|
||||
|
|
Loading…
Add table
Reference in a new issue