finalized Emoji support (Mastodon), bug fix

This commit is contained in:
nuclearfog 2023-03-25 18:18:55 +01:00
parent af381a8feb
commit fc675a4e6f
No known key found for this signature in database
GPG Key ID: 03488A185C476379
13 changed files with 166 additions and 103 deletions

View File

@ -74,11 +74,12 @@ public class ImageCache {
if (imageFolder != null && !files.containsKey(key)) { if (imageFolder != null && !files.containsKey(key)) {
try { try {
File file = new File(imageFolder, key); File file = new File(imageFolder, key);
file.createNewFile(); if (file.createNewFile()) {
FileOutputStream output = new FileOutputStream(file); FileOutputStream output = new FileOutputStream(file);
image.compress(Bitmap.CompressFormat.PNG, 1, output); image.compress(Bitmap.CompressFormat.PNG, 1, output);
files.put(key, file); files.put(key, file);
} catch (IOException e) { }
} catch (IOException|SecurityException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }

View File

@ -0,0 +1,53 @@
package org.nuclearfog.twidda.backend.utils;
import android.graphics.Bitmap;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ImageSpan;
import android.widget.TextView;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class provides method to replace tags (e.g. :tagname:) from a TextView with bitmap drawables
*
* @author nuclearfog
*/
public class TextWithEmoji {
private static final Pattern EMOJI_PATTERN = Pattern.compile(":\\w+:");
private TextWithEmoji() {}
/**
* replace tags with emojis
*
* @param textView textview containing text with emoji tags (e.g. :tagname:)
* @param emojis a map of emoji tags & bitmap. every emoji bitmap has its own tag
*/
public static void addEmojis(TextView textView, Map<String, Bitmap> emojis) {
if (textView.length() > 0) {
SpannableStringBuilder builder = new SpannableStringBuilder(textView.getText());
Matcher matcher = EMOJI_PATTERN.matcher(textView.getText());
Stack<Integer> indexes = new Stack<>();
while (matcher.find()) {
indexes.push(matcher.start());
indexes.push(matcher.end());
}
while (!indexes.isEmpty()) {
int end = indexes.pop();
int start = indexes.pop();
String tag = builder.subSequence(start + 1, end - 1).toString();
Bitmap emoji = emojis.get(tag);
if (emoji != null) {
ImageSpan imgSpan = new ImageSpan(textView.getContext(), emoji);
builder.setSpan(imgSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
textView.setText(builder);
}
}
}

View File

@ -1453,7 +1453,16 @@ public class AppDatabase {
} else { } else {
flags &= ~MASK_USER_DEFAULT_IMAGE; flags &= ~MASK_USER_DEFAULT_IMAGE;
} }
ContentValues column = new ContentValues(13); ContentValues column = new ContentValues(14);
if (user.getEmojis().length > 0) {
StringBuilder buf = new StringBuilder();
saveEmojis(user.getEmojis(), db);
for (Emoji emoji : user.getEmojis()) {
buf.append(emoji.getCode()).append(';');
}
String emojiKeys = buf.deleteCharAt(buf.length() - 1).toString();
column.put(UserTable.EMOJI, emojiKeys);
}
column.put(UserTable.ID, user.getId()); column.put(UserTable.ID, user.getId());
column.put(UserTable.USERNAME, user.getUsername()); column.put(UserTable.USERNAME, user.getUsername());
column.put(UserTable.SCREENNAME, user.getScreenname()); column.put(UserTable.SCREENNAME, user.getScreenname());

View File

@ -16,7 +16,7 @@ public class DatabasePoll implements Poll {
private static final long serialVersionUID = 3534663789678017084L; private static final long serialVersionUID = 3534663789678017084L;
private static final Pattern SPLIT = Pattern.compile(";"); private static final Pattern SEPARATOR = Pattern.compile(";");
public static final String[] PROJECTION = { public static final String[] PROJECTION = {
PollTable.ID, PollTable.ID,
@ -39,7 +39,7 @@ public class DatabasePoll implements Poll {
expired = cursor.getLong(2); expired = cursor.getLong(2);
String optionStr = cursor.getString(3); String optionStr = cursor.getString(3);
if (optionStr != null && !optionStr.isEmpty()) { if (optionStr != null && !optionStr.isEmpty()) {
String[] optArray = SPLIT.split(optionStr); String[] optArray = SEPARATOR.split(optionStr);
options = new DatabasePollOption[optArray.length]; options = new DatabasePollOption[optArray.length];
for (int i = 0; i < optArray.length; i++) { for (int i = 0; i < optArray.length; i++) {
options[i] = new DatabasePollOption(optArray[i]); options[i] = new DatabasePollOption(optArray[i]);

View File

@ -38,38 +38,24 @@ public class DatabaseStatus implements Status {
private static final Pattern KEY_SEPARATOR = Pattern.compile(";"); private static final Pattern KEY_SEPARATOR = Pattern.compile(";");
private long id; private long id, time, embeddedId, replyID, replyUserId, myRepostId, locationId, pollId;
private long time; private int repostCount, favoriteCount, replyCount;
private long embeddedId; private boolean reposted, favorited, bookmarked, sensitive, spoiler, isHidden;
private long replyID;
private long replyUserId;
private long myRepostId;
private long locationId;
private long pollId;
private Status embedded; private Status embedded;
private Poll poll;
private User author;
private Location location;
private String[] mediaKeys = {}; private String[] mediaKeys = {};
private String[] emojiKeys = {}; private String[] emojiKeys = {};
private Media[] medias = {}; private Media[] medias = {};
private Emoji[] emojis = {}; private Emoji[] emojis = {};
private Card[] cards = {}; private Card[] cards = {};
private Poll poll;
private User author;
private Location location;
private int repostCount;
private int favoriteCount;
private int replyCount;
private String replyName = ""; private String replyName = "";
private String text = ""; private String text = "";
private String source = ""; private String source = "";
private String userMentions = ""; private String userMentions = "";
private String statusUrl = ""; private String statusUrl = "";
private String language = ""; private String language = "";
private boolean reposted;
private boolean favorited;
private boolean bookmarked;
private boolean sensitive;
private boolean spoiler;
private boolean isHidden;
/** /**
* @param cursor database cursor * @param cursor database cursor

View File

@ -29,17 +29,9 @@ public class DatabaseUser implements User {
private static final Pattern KEY_SEPARATOR = Pattern.compile(";"); private static final Pattern KEY_SEPARATOR = Pattern.compile(";");
private long id; private long id, createdAt;
private long createdAt; private int following, follower, statusCount, favorCount;
private int following; private boolean isCurrentUser, isVerified, isLocked, followReqSent, defaultImage;
private int follower;
private int statusCount;
private int favorCount;
private boolean isCurrentUser;
private boolean isVerified;
private boolean isLocked;
private boolean followReqSent;
private boolean defaultImage;
private String username = ""; private String username = "";
private String screen_name = ""; private String screen_name = "";
private String bio = ""; private String bio = "";
@ -94,8 +86,6 @@ public class DatabaseUser implements User {
this.profileBannerOrig = profileBannerOrig; this.profileBannerOrig = profileBannerOrig;
if (emojiKeys != null && !emojiKeys.isEmpty()) if (emojiKeys != null && !emojiKeys.isEmpty())
this.emojiKeys = KEY_SEPARATOR.split(emojiKeys); this.emojiKeys = KEY_SEPARATOR.split(emojiKeys);
if (emojiKeys != null && !emojiKeys.isEmpty())
this.emojiKeys = KEY_SEPARATOR.split(emojiKeys);
switch (account.getConfiguration()) { switch (account.getConfiguration()) {
case TWITTER1: case TWITTER1:

View File

@ -21,14 +21,10 @@ import static org.nuclearfog.twidda.ui.activities.UsersActivity.USERS_REQUESTS;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color; import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ImageSpan;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@ -78,6 +74,7 @@ import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.ErrorHandler; import org.nuclearfog.twidda.backend.utils.ErrorHandler;
import org.nuclearfog.twidda.backend.utils.PicassoBuilder; import org.nuclearfog.twidda.backend.utils.PicassoBuilder;
import org.nuclearfog.twidda.backend.utils.StringTools; import org.nuclearfog.twidda.backend.utils.StringTools;
import org.nuclearfog.twidda.backend.utils.TextWithEmoji;
import org.nuclearfog.twidda.config.Configuration; import org.nuclearfog.twidda.config.Configuration;
import org.nuclearfog.twidda.config.GlobalSettings; import org.nuclearfog.twidda.config.GlobalSettings;
import org.nuclearfog.twidda.database.impl.DatabaseUser; import org.nuclearfog.twidda.database.impl.DatabaseUser;
@ -89,7 +86,6 @@ import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.List; import java.util.List;
import java.util.Map;
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation; import jp.wasabeef.picasso.transformations.RoundedCornersTransformation;
@ -157,7 +153,7 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
private ConstraintLayout header; private ConstraintLayout header;
private ViewGroup body; private ViewGroup body;
private TextView[] tabIndicator; private TextView[] tabIndicator;
private TextView user_location, user_createdAt, user_website, user_bio, follow_back, username, screenName; private TextView user_location, user_createdAt, user_website, description, follow_back, username, screenName;
private ImageView profileImage, bannerImage, toolbarBackground; private ImageView profileImage, bannerImage, toolbarBackground;
private Button following, follower; private Button following, follower;
private ViewPager tabPages; private ViewPager tabPages;
@ -185,7 +181,7 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
body = findViewById(R.id.page_profile_body); body = findViewById(R.id.page_profile_body);
root = findViewById(R.id.user_view); root = findViewById(R.id.user_view);
toolbar = findViewById(R.id.profile_toolbar); toolbar = findViewById(R.id.profile_toolbar);
user_bio = findViewById(R.id.bio); description = findViewById(R.id.bio);
following = findViewById(R.id.following); following = findViewById(R.id.following);
follower = findViewById(R.id.follower); follower = findViewById(R.id.follower);
user_website = findViewById(R.id.links); user_website = findViewById(R.id.links);
@ -220,8 +216,8 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
toolbar.setBackgroundColor(settings.getBackgroundColor() & TOOLBAR_TRANSPARENCY); toolbar.setBackgroundColor(settings.getBackgroundColor() & TOOLBAR_TRANSPARENCY);
username.setBackgroundColor(settings.getBackgroundColor() & TEXT_TRANSPARENCY); username.setBackgroundColor(settings.getBackgroundColor() & TEXT_TRANSPARENCY);
follow_back.setBackgroundColor(settings.getBackgroundColor() & TEXT_TRANSPARENCY); follow_back.setBackgroundColor(settings.getBackgroundColor() & TEXT_TRANSPARENCY);
user_bio.setMovementMethod(LinkAndScrollMovement.getInstance()); description.setMovementMethod(LinkAndScrollMovement.getInstance());
user_bio.setLinkTextColor(settings.getHighlightColor()); description.setLinkTextColor(settings.getHighlightColor());
AppStyles.setTheme(root); AppStyles.setTheme(root);
user_website.setTextColor(settings.getHighlightColor()); user_website.setTextColor(settings.getHighlightColor());
tabLayout.setBackgroundColor(Color.TRANSPARENT); tabLayout.setBackgroundColor(Color.TRANSPARENT);
@ -775,10 +771,10 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
user_location.setVisibility(GONE); user_location.setVisibility(GONE);
} }
if (!user.getDescription().isEmpty()) { if (!user.getDescription().isEmpty()) {
user_bio.setVisibility(VISIBLE); description.setVisibility(VISIBLE);
user_bio.setText(bio); description.setText(bio);
} else { } else {
user_bio.setVisibility(GONE); description.setVisibility(GONE);
} }
if (!user.getProfileUrl().isEmpty()) { if (!user.getProfileUrl().isEmpty()) {
String link = user.getProfileUrl(); String link = user.getProfileUrl();
@ -819,20 +815,8 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
*/ */
private void onEmojiResult(EmojiLoader.EmojiResult result) { private void onEmojiResult(EmojiLoader.EmojiResult result) {
if (settings.getLogin().getConfiguration() == Configuration.MASTODON && result.images != null) { if (settings.getLogin().getConfiguration() == Configuration.MASTODON && result.images != null) {
TextView[] textViews = {username, user_bio}; TextWithEmoji.addEmojis(username, result.images);
for (TextView textView: textViews) { TextWithEmoji.addEmojis(description, result.images);
if (textView.length() > 0) {
SpannableStringBuilder builder = new SpannableStringBuilder(textView.getText());
for (Map.Entry<String, Bitmap> item : result.images.entrySet()) {
int idx = TextUtils.indexOf(builder, ':' + item.getKey() + ':');
if (idx >= 0) {
ImageSpan imgSpan = new ImageSpan(getApplicationContext(), item.getValue());
builder.setSpan(imgSpan, idx, idx + item.getKey().length() + 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
textView.setText(builder);
}
}
} }
} }
} }

View File

@ -17,15 +17,10 @@ import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter; import android.graphics.BlurMaskFilter;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ImageSpan;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.SubMenu; import android.view.SubMenu;
@ -74,6 +69,7 @@ import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.ErrorHandler; import org.nuclearfog.twidda.backend.utils.ErrorHandler;
import org.nuclearfog.twidda.backend.utils.PicassoBuilder; import org.nuclearfog.twidda.backend.utils.PicassoBuilder;
import org.nuclearfog.twidda.backend.utils.StringTools; import org.nuclearfog.twidda.backend.utils.StringTools;
import org.nuclearfog.twidda.backend.utils.TextWithEmoji;
import org.nuclearfog.twidda.config.Configuration; import org.nuclearfog.twidda.config.Configuration;
import org.nuclearfog.twidda.config.GlobalSettings; import org.nuclearfog.twidda.config.GlobalSettings;
import org.nuclearfog.twidda.database.impl.DatabaseNotification; import org.nuclearfog.twidda.database.impl.DatabaseNotification;
@ -97,7 +93,6 @@ import org.nuclearfog.twidda.ui.fragments.StatusFragment;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation; import jp.wasabeef.picasso.transformations.RoundedCornersTransformation;
@ -218,7 +213,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
private NestedScrollView container; private NestedScrollView container;
private ViewGroup root, body; private ViewGroup root, body;
private TextView statusApi, createdAt, statusText, screenName, userName, locationName, sensitive, spoiler, spoilerHint, translateText; private TextView statusApi, createdAt, statusText, screenName, username, locationName, sensitive, spoiler, spoilerHint, translateText;
private Button replyButton, repostButton, likeButton, replyName, repostNameButton; private Button replyButton, repostButton, likeButton, replyName, repostNameButton;
private ImageView profileImage; private ImageView profileImage;
private RecyclerView cardList; private RecyclerView cardList;
@ -250,7 +245,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
replyButton = findViewById(R.id.page_status_reply); replyButton = findViewById(R.id.page_status_reply);
repostButton = findViewById(R.id.page_status_repost); repostButton = findViewById(R.id.page_status_repost);
likeButton = findViewById(R.id.page_status_favorite); likeButton = findViewById(R.id.page_status_favorite);
userName = findViewById(R.id.page_status_username); username = findViewById(R.id.page_status_username);
screenName = findViewById(R.id.page_status_screenname); screenName = findViewById(R.id.page_status_screenname);
profileImage = findViewById(R.id.page_status_profile); profileImage = findViewById(R.id.page_status_profile);
replyName = findViewById(R.id.page_status_reply_reference); replyName = findViewById(R.id.page_status_reply_reference);
@ -831,10 +826,10 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
AppStyles.setDrawableColor(likeButton, settings.getIconColor()); AppStyles.setDrawableColor(likeButton, settings.getIconColor());
} }
if (author.isVerified()) { if (author.isVerified()) {
userName.setCompoundDrawablesWithIntrinsicBounds(R.drawable.verify, 0, 0, 0); username.setCompoundDrawablesWithIntrinsicBounds(R.drawable.verify, 0, 0, 0);
AppStyles.setDrawableColor(userName, settings.getIconColor()); AppStyles.setDrawableColor(username, settings.getIconColor());
} else { } else {
userName.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); username.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
} }
if (author.isProtected()) { if (author.isProtected()) {
screenName.setCompoundDrawablesWithIntrinsicBounds(R.drawable.lock, 0, 0, 0); screenName.setCompoundDrawablesWithIntrinsicBounds(R.drawable.lock, 0, 0, 0);
@ -846,7 +841,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
translateText.setVisibility(View.VISIBLE); translateText.setVisibility(View.VISIBLE);
translateText.setTextColor(settings.getHighlightColor()); translateText.setTextColor(settings.getHighlightColor());
} }
userName.setText(author.getUsername()); username.setText(author.getUsername());
screenName.setText(author.getScreenname()); screenName.setText(author.getScreenname());
createdAt.setText(SimpleDateFormat.getDateTimeInstance().format(status.getTimestamp())); createdAt.setText(SimpleDateFormat.getDateTimeInstance().format(status.getTimestamp()));
replyButton.setText(StringTools.NUMBER_FORMAT.format(status.getReplyCount())); replyButton.setText(StringTools.NUMBER_FORMAT.format(status.getReplyCount()));
@ -1152,20 +1147,8 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
*/ */
private void onEmojiResult(EmojiResult result) { private void onEmojiResult(EmojiResult result) {
if (settings.getLogin().getConfiguration() == Configuration.MASTODON && result.images != null) { if (settings.getLogin().getConfiguration() == Configuration.MASTODON && result.images != null) {
TextView[] textViews = {statusText, userName}; TextWithEmoji.addEmojis(statusText, result.images);
for (TextView textView : textViews) { TextWithEmoji.addEmojis(username, result.images);
if (textView.length() > 0) {
SpannableStringBuilder builder = new SpannableStringBuilder(textView.getText());
for (Map.Entry<String, Bitmap> item : result.images.entrySet()) {
int idx = TextUtils.indexOf(builder, ':' + item.getKey() + ':');
if (idx >= 0) {
ImageSpan imgSpan = new ImageSpan(getApplicationContext(), item.getValue());
builder.setSpan(imgSpan, idx, idx + item.getKey().length() + 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
textView.setText(builder);
}
}
} }
} }
} }

View File

@ -10,6 +10,7 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
import org.nuclearfog.twidda.backend.async.EmojiLoader;
import org.nuclearfog.twidda.backend.utils.PicassoBuilder; import org.nuclearfog.twidda.backend.utils.PicassoBuilder;
import org.nuclearfog.twidda.config.GlobalSettings; import org.nuclearfog.twidda.config.GlobalSettings;
import org.nuclearfog.twidda.model.Notification; import org.nuclearfog.twidda.model.Notification;
@ -54,6 +55,7 @@ public class NotificationAdapter extends Adapter<ViewHolder> implements OnHolder
private Picasso picasso; private Picasso picasso;
private GlobalSettings settings; private GlobalSettings settings;
private OnNotificationClickListener listener; private OnNotificationClickListener listener;
private EmojiLoader emojiLoader;
private List<Notification> items; private List<Notification> items;
private int loadingIndex; private int loadingIndex;
@ -62,6 +64,7 @@ public class NotificationAdapter extends Adapter<ViewHolder> implements OnHolder
public NotificationAdapter(Context context, OnNotificationClickListener listener) { public NotificationAdapter(Context context, OnNotificationClickListener listener) {
settings = GlobalSettings.getInstance(context); settings = GlobalSettings.getInstance(context);
picasso = PicassoBuilder.get(context); picasso = PicassoBuilder.get(context);
emojiLoader = new EmojiLoader(context);
items = new LinkedList<>(); items = new LinkedList<>();
loadingIndex = NO_LOADING; loadingIndex = NO_LOADING;
this.listener = listener; this.listener = listener;
@ -72,9 +75,9 @@ public class NotificationAdapter extends Adapter<ViewHolder> implements OnHolder
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == TYPE_STATUS) { if (viewType == TYPE_STATUS) {
return new StatusHolder(parent, settings, picasso, this); return new StatusHolder(parent, settings, picasso, emojiLoader, this);
} else if (viewType == TYPE_USER) { } else if (viewType == TYPE_USER) {
return new UserHolder(parent, settings, picasso, this, false); return new UserHolder(parent, settings, picasso, emojiLoader, this, false);
} else { } else {
return new PlaceHolder(parent, settings, false, this); return new PlaceHolder(parent, settings, false, this);
} }

View File

@ -11,6 +11,7 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
import org.nuclearfog.twidda.backend.async.EmojiLoader;
import org.nuclearfog.twidda.backend.utils.PicassoBuilder; import org.nuclearfog.twidda.backend.utils.PicassoBuilder;
import org.nuclearfog.twidda.config.GlobalSettings; import org.nuclearfog.twidda.config.GlobalSettings;
import org.nuclearfog.twidda.model.Status; import org.nuclearfog.twidda.model.Status;
@ -50,6 +51,7 @@ public class StatusAdapter extends Adapter<ViewHolder> implements OnHolderClickL
*/ */
private static final int MIN_COUNT = 2; private static final int MIN_COUNT = 2;
private EmojiLoader emojiLoader;
private StatusSelectListener listener; private StatusSelectListener listener;
private GlobalSettings settings; private GlobalSettings settings;
private Picasso picasso; private Picasso picasso;
@ -63,6 +65,7 @@ public class StatusAdapter extends Adapter<ViewHolder> implements OnHolderClickL
public StatusAdapter(Context context, StatusSelectListener itemClickListener) { public StatusAdapter(Context context, StatusSelectListener itemClickListener) {
settings = GlobalSettings.getInstance(context); settings = GlobalSettings.getInstance(context);
picasso = PicassoBuilder.get(context); picasso = PicassoBuilder.get(context);
emojiLoader = new EmojiLoader(context);
loadingIndex = NO_LOADING; loadingIndex = NO_LOADING;
items = new LinkedList<>(); items = new LinkedList<>();
this.listener = itemClickListener; this.listener = itemClickListener;
@ -96,7 +99,7 @@ public class StatusAdapter extends Adapter<ViewHolder> implements OnHolderClickL
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == VIEW_STATUS) { if (viewType == VIEW_STATUS) {
return new StatusHolder(parent, settings, picasso, this); return new StatusHolder(parent, settings, picasso, emojiLoader, this);
} else { } else {
return new PlaceHolder(parent, settings, false, this); return new PlaceHolder(parent, settings, false, this);
} }
@ -108,7 +111,8 @@ public class StatusAdapter extends Adapter<ViewHolder> implements OnHolderClickL
if (holder instanceof StatusHolder) { if (holder instanceof StatusHolder) {
Status status = items.get(index); Status status = items.get(index);
if (status != null) { if (status != null) {
((StatusHolder) holder).setContent(status); StatusHolder statusHolder = ((StatusHolder) holder);
statusHolder.setContent(status);
} }
} else if (holder instanceof PlaceHolder) { } else if (holder instanceof PlaceHolder) {
PlaceHolder placeHolder = (PlaceHolder) holder; PlaceHolder placeHolder = (PlaceHolder) holder;

View File

@ -11,6 +11,7 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
import org.nuclearfog.twidda.backend.async.EmojiLoader;
import org.nuclearfog.twidda.backend.helper.Users; import org.nuclearfog.twidda.backend.helper.Users;
import org.nuclearfog.twidda.backend.utils.PicassoBuilder; import org.nuclearfog.twidda.backend.utils.PicassoBuilder;
import org.nuclearfog.twidda.config.GlobalSettings; import org.nuclearfog.twidda.config.GlobalSettings;
@ -44,6 +45,7 @@ public class UserAdapter extends Adapter<ViewHolder> implements OnHolderClickLis
private GlobalSettings settings; private GlobalSettings settings;
private Picasso picasso; private Picasso picasso;
private EmojiLoader emojiLoader;
private UserClickListener listener; private UserClickListener listener;
private boolean enableDelete; private boolean enableDelete;
@ -58,6 +60,7 @@ public class UserAdapter extends Adapter<ViewHolder> implements OnHolderClickLis
public UserAdapter(Context context, UserClickListener listener, boolean enableDelete) { public UserAdapter(Context context, UserClickListener listener, boolean enableDelete) {
settings = GlobalSettings.getInstance(context); settings = GlobalSettings.getInstance(context);
picasso = PicassoBuilder.get(context); picasso = PicassoBuilder.get(context);
emojiLoader = new EmojiLoader(context);
users = new Users(0L, 0L); users = new Users(0L, 0L);
loadingIndex = NO_LOADING; loadingIndex = NO_LOADING;
this.enableDelete = enableDelete; this.enableDelete = enableDelete;
@ -92,7 +95,7 @@ public class UserAdapter extends Adapter<ViewHolder> implements OnHolderClickLis
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == ITEM_USER) { if (viewType == ITEM_USER) {
return new UserHolder(parent, settings, picasso, this, enableDelete); return new UserHolder(parent, settings, picasso, emojiLoader, this, enableDelete);
} else { } else {
return new PlaceHolder(parent, settings, false, this); return new PlaceHolder(parent, settings, false, this);
} }

View File

@ -24,9 +24,16 @@ import com.squareup.picasso.Transformation;
import org.nuclearfog.tag.Tagger; import org.nuclearfog.tag.Tagger;
import org.nuclearfog.twidda.R; import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback;
import org.nuclearfog.twidda.backend.async.EmojiLoader;
import org.nuclearfog.twidda.backend.async.EmojiLoader.EmojiParam;
import org.nuclearfog.twidda.backend.async.EmojiLoader.EmojiResult;
import org.nuclearfog.twidda.backend.utils.AppStyles; import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.StringTools; import org.nuclearfog.twidda.backend.utils.StringTools;
import org.nuclearfog.twidda.backend.utils.TextWithEmoji;
import org.nuclearfog.twidda.config.Configuration;
import org.nuclearfog.twidda.config.GlobalSettings; import org.nuclearfog.twidda.config.GlobalSettings;
import org.nuclearfog.twidda.model.Emoji;
import org.nuclearfog.twidda.model.Notification; import org.nuclearfog.twidda.model.Notification;
import org.nuclearfog.twidda.model.Status; import org.nuclearfog.twidda.model.Status;
import org.nuclearfog.twidda.model.User; import org.nuclearfog.twidda.model.User;
@ -41,7 +48,7 @@ import jp.wasabeef.picasso.transformations.RoundedCornersTransformation;
* @author nuclearfog * @author nuclearfog
* @see StatusAdapter * @see StatusAdapter
*/ */
public class StatusHolder extends ViewHolder implements OnClickListener { public class StatusHolder extends ViewHolder implements OnClickListener, AsyncCallback<EmojiResult> {
private ImageView profile, repostUserIcon, verifiedIcon, lockedIcon, repostIcon, favoriteIcon, replyStatus; private ImageView profile, repostUserIcon, verifiedIcon, lockedIcon, repostIcon, favoriteIcon, replyStatus;
private TextView username, screenname, text, repost, favorite, reply, reposter, created, replyname, label; private TextView username, screenname, text, repost, favorite, reply, reposter, created, replyname, label;
@ -50,15 +57,17 @@ public class StatusHolder extends ViewHolder implements OnClickListener {
private GlobalSettings settings; private GlobalSettings settings;
private Picasso picasso; private Picasso picasso;
private EmojiLoader emojiLoader;
private IconAdapter adapter; private IconAdapter adapter;
private OnHolderClickListener listener; private OnHolderClickListener listener;
public StatusHolder(ViewGroup parent, GlobalSettings settings, Picasso picasso, OnHolderClickListener listener) { public StatusHolder(ViewGroup parent, GlobalSettings settings, Picasso picasso, EmojiLoader emojiLoader, OnHolderClickListener listener) {
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_status, parent, false)); super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_status, parent, false));
this.settings = settings; this.settings = settings;
this.picasso = picasso; this.picasso = picasso;
this.listener = listener; this.listener = listener;
this.emojiLoader = emojiLoader;
CardView cardLayout = (CardView) itemView; CardView cardLayout = (CardView) itemView;
ViewGroup container = itemView.findViewById(R.id.item_status_container); ViewGroup container = itemView.findViewById(R.id.item_status_container);
@ -114,6 +123,15 @@ public class StatusHolder extends ViewHolder implements OnClickListener {
} }
} }
@Override
public void onResult(EmojiResult result) {
if (settings.getLogin().getConfiguration() == Configuration.MASTODON && result.images != null) {
TextWithEmoji.addEmojis(text, result.images);
TextWithEmoji.addEmojis(username, result.images);
}
}
/** /**
* set view content * set view content
* *
@ -202,6 +220,16 @@ public class StatusHolder extends ViewHolder implements OnClickListener {
} else { } else {
iconList.setVisibility(View.GONE); iconList.setVisibility(View.GONE);
} }
if (status.getEmojis().length > 0 || status.getAuthor().getEmojis().length > 0) {
int index = 0;
Emoji[] emojis = new Emoji[status.getEmojis().length + status.getAuthor().getEmojis().length];
for (Emoji emoji : status.getEmojis())
emojis[index++] = emoji;
for (Emoji emoji : status.getAuthor().getEmojis())
emojis[index++] = emoji;
EmojiParam param = new EmojiParam(emojis, text.getLineHeight());
emojiLoader.execute(param, this);
}
} }
/** /**

View File

@ -21,8 +21,14 @@ import com.squareup.picasso.Picasso;
import com.squareup.picasso.Transformation; import com.squareup.picasso.Transformation;
import org.nuclearfog.twidda.R; import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback;
import org.nuclearfog.twidda.backend.async.EmojiLoader;
import org.nuclearfog.twidda.backend.async.EmojiLoader.EmojiParam;
import org.nuclearfog.twidda.backend.async.EmojiLoader.EmojiResult;
import org.nuclearfog.twidda.backend.utils.AppStyles; import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.StringTools; import org.nuclearfog.twidda.backend.utils.StringTools;
import org.nuclearfog.twidda.backend.utils.TextWithEmoji;
import org.nuclearfog.twidda.config.Configuration;
import org.nuclearfog.twidda.config.GlobalSettings; import org.nuclearfog.twidda.config.GlobalSettings;
import org.nuclearfog.twidda.model.Notification; import org.nuclearfog.twidda.model.Notification;
import org.nuclearfog.twidda.model.User; import org.nuclearfog.twidda.model.User;
@ -35,7 +41,7 @@ import jp.wasabeef.picasso.transformations.RoundedCornersTransformation;
* @author nuclearfog * @author nuclearfog
* @see org.nuclearfog.twidda.ui.adapter.UserAdapter * @see org.nuclearfog.twidda.ui.adapter.UserAdapter
*/ */
public class UserHolder extends ViewHolder implements OnClickListener { public class UserHolder extends ViewHolder implements OnClickListener, AsyncCallback<EmojiResult> {
private TextView username, screenname, followingCount, followerCount, label; private TextView username, screenname, followingCount, followerCount, label;
private ImageView profileImg, verifyIcon, lockedIcon; private ImageView profileImg, verifyIcon, lockedIcon;
@ -43,16 +49,18 @@ public class UserHolder extends ViewHolder implements OnClickListener {
private View notificationDismiss; private View notificationDismiss;
private GlobalSettings settings; private GlobalSettings settings;
private EmojiLoader emojiLoader;
private Picasso picasso; private Picasso picasso;
private OnHolderClickListener listener; private OnHolderClickListener listener;
public UserHolder(ViewGroup parent, GlobalSettings settings, Picasso picasso, OnHolderClickListener listener, boolean enableDelete) { public UserHolder(ViewGroup parent, GlobalSettings settings, Picasso picasso, EmojiLoader emojiLoader, OnHolderClickListener listener, boolean enableDelete) {
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_user, parent, false)); super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_user, parent, false));
this.settings = settings; this.settings = settings;
this.picasso = picasso; this.picasso = picasso;
this.listener = listener; this.listener = listener;
this.emojiLoader = emojiLoader;
CardView background = (CardView) itemView; CardView background = (CardView) itemView;
ViewGroup container = itemView.findViewById(R.id.item_user_container); ViewGroup container = itemView.findViewById(R.id.item_user_container);
@ -74,7 +82,6 @@ public class UserHolder extends ViewHolder implements OnClickListener {
} else { } else {
delete.setVisibility(GONE); delete.setVisibility(GONE);
} }
itemView.setOnClickListener(this); itemView.setOnClickListener(this);
notificationDismiss.setOnClickListener(this); notificationDismiss.setOnClickListener(this);
delete.setOnClickListener(this); delete.setOnClickListener(this);
@ -95,6 +102,14 @@ public class UserHolder extends ViewHolder implements OnClickListener {
} }
} }
@Override
public void onResult(EmojiResult result) {
if (settings.getLogin().getConfiguration() == Configuration.MASTODON && result.images != null) {
TextWithEmoji.addEmojis(username, result.images);
}
}
/** /**
* set user information * set user information
* *
@ -122,6 +137,10 @@ public class UserHolder extends ViewHolder implements OnClickListener {
} else { } else {
profileImg.setImageResource(0); profileImg.setImageResource(0);
} }
if (user.getEmojis().length > 0) {
EmojiParam param = new EmojiParam(user.getEmojis(), username.getLineHeight());
emojiLoader.execute(param, this);
}
} }
/** /**