Loading app/src/main/java/app/fedilab/android/client/entities/api/Status.java +22 −6 Original line number Diff line number Diff line Loading @@ -115,6 +115,10 @@ public class Status implements Serializable, Cloneable { public transient boolean submitted = false; public transient boolean spoilerChecked = false; public Filter filteredByApp; public transient Spannable contentSpan; public transient Spannable contentSpoilerSpan; public transient Spannable contentTranslateSpan; @Override public boolean equals(@Nullable Object obj) { boolean same = false; Loading @@ -124,17 +128,29 @@ public class Status implements Serializable, Cloneable { return same; } public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference) { return SpannableHelper.convert(context, content, this, null, null, true, viewWeakReference); public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference, Callback callback) { if (contentSpan == null) { contentSpan = SpannableHelper.convert(context, content, this, null, null, true, viewWeakReference, callback); } return contentSpan; } public synchronized Spannable getSpanSpoiler(Context context, WeakReference<View> viewWeakReference, Callback callback) { if (contentSpoilerSpan == null) { contentSpoilerSpan = SpannableHelper.convert(context, spoiler_text, this, null, null, true, viewWeakReference, callback); } return contentSpoilerSpan; } public synchronized Spannable getSpanSpoiler(Context context, WeakReference<View> viewWeakReference) { return SpannableHelper.convert(context, spoiler_text, this, null, null, true, viewWeakReference); public synchronized Spannable getSpanTranslate(Context context, WeakReference<View> viewWeakReference, Callback callback) { if (contentTranslateSpan == null) { contentTranslateSpan = SpannableHelper.convert(context, translationContent, this, null, null, true, viewWeakReference, callback); } return contentTranslateSpan; } public synchronized Spannable getSpanTranslate(Context context, WeakReference<View> viewWeakReference) { return SpannableHelper.convert(context, translationContent, this, null, null, true, viewWeakReference); public interface Callback { void emojiFetched(); } @NonNull Loading app/src/main/java/app/fedilab/android/helper/CustomEmoji.java +34 −2 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.text.SpannableStringBuilder; import android.text.style.ReplacementSpan; import android.view.View; Loading @@ -13,26 +14,53 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.target.Target; import com.bumptech.glide.request.transition.Transition; import java.lang.ref.WeakReference; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import app.fedilab.android.R; import app.fedilab.android.client.entities.api.Emoji; import app.fedilab.android.client.entities.api.Status; public class CustomEmoji extends ReplacementSpan { private final float scale; private final WeakReference<View> viewWeakReference; private Drawable imageDrawable; private boolean callbackCalled; CustomEmoji(WeakReference<View> viewWeakReference) { Context mContext = viewWeakReference.get().getContext(); this.viewWeakReference = viewWeakReference; SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(mContext); scale = sharedpreferences.getFloat(mContext.getString(R.string.SET_FONT_SCALE), 1.1f); callbackCalled = false; } public SpannableStringBuilder makeEmoji(SpannableStringBuilder content, List<Emoji> emojiList, boolean animate, Status.Callback callback) { if (emojiList != null && emojiList.size() > 0) { int count = 1; for (Emoji emoji : emojiList) { Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) .matcher(content); while (matcher.find()) { CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(viewWeakReference.get())); content.setSpan(customEmoji, matcher.start(), matcher.end(), 0); Glide.with(viewWeakReference.get()) .asDrawable() .load(animate ? emoji.url : emoji.static_url) .into(customEmoji.getTarget(animate, count == emojiList.size() && !callbackCalled ? callback : null)); } count++; } } return content; } @Override Loading Loading @@ -61,7 +89,7 @@ public class CustomEmoji extends ReplacementSpan { } } public Target<Drawable> getTarget(boolean animate) { public Target<Drawable> getTarget(boolean animate, Status.Callback callback) { return new CustomTarget<Drawable>() { @Override public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) { Loading Loading @@ -97,6 +125,10 @@ public class CustomEmoji extends ReplacementSpan { if (view != null) { view.invalidate(); } if (callback != null) { callbackCalled = true; callback.emojiFetched(); } } @Override Loading app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +11 −18 Original line number Diff line number Diff line Loading @@ -89,10 +89,16 @@ public class SpannableHelper { public static final String CLICKABLE_SPAN = "CLICKABLE_SPAN"; public static Spannable convert(Context context, String text, Status status, Account account, Announcement announcement, boolean convertHtml, WeakReference<View> viewWeakReference) { return convert(context, text, status, account, announcement, convertHtml, viewWeakReference, null); } public static Spannable convert(Context context, String text, Status status, Account account, Announcement announcement, boolean convertHtml, WeakReference<View> viewWeakReference) { WeakReference<View> viewWeakReference, Status.Callback callback) { SpannableString initialContent; Loading Loading @@ -161,20 +167,8 @@ public class SpannableHelper { } SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); boolean animate = !sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); if (emojiList != null && emojiList.size() > 0) { for (Emoji emoji : emojiList) { Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) .matcher(content); while (matcher.find()) { CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view)); content.setSpan(customEmoji, matcher.start(), matcher.end(), 0); Glide.with(view) .asDrawable() .load(animate ? emoji.url : emoji.static_url) .into(customEmoji.getTarget(animate)); } } } content = customEmoji.makeEmoji(content, emojiList, animate, callback); if (imagesToReplace.size() > 0) { for (Map.Entry<String, String> entry : imagesToReplace.entrySet()) { Loading @@ -183,12 +177,11 @@ public class SpannableHelper { Matcher matcher = Pattern.compile(key, Pattern.LITERAL) .matcher(content); while (matcher.find()) { CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view)); content.setSpan(customEmoji, matcher.start(), matcher.end(), 0); Glide.with(view) .asDrawable() .load(url) .into(customEmoji.getTarget(animate)); .into(customEmoji.getTarget(animate, null)); } } Loading Loading @@ -1066,7 +1059,7 @@ public class SpannableHelper { Glide.with(viewWeakReference.get()) .asDrawable() .load(animate ? emoji.url : emoji.static_url) .into(customEmoji.getTarget(animate)); .into(customEmoji.getTarget(animate, null)); } } } Loading app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java +2 −2 Original line number Diff line number Diff line Loading @@ -1107,7 +1107,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder StatusSimpleViewHolder holder = (StatusSimpleViewHolder) viewHolder; holder.binding.statusContent.setText( status.getSpanContent(context, new WeakReference<>(holder.binding.statusContent)), new WeakReference<>(holder.binding.statusContent), null), TextView.BufferType.SPANNABLE); MastodonHelper.loadPPMastodon(holder.binding.avatar, status.account); if (status.account != null) { Loading @@ -1122,7 +1122,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder holder.binding.spoiler.setVisibility(View.VISIBLE); holder.binding.spoiler.setText( status.getSpanSpoiler(context, new WeakReference<>(holder.binding.spoiler)), new WeakReference<>(holder.binding.spoiler), null), TextView.BufferType.SPANNABLE); } else { holder.binding.spoiler.setVisibility(View.GONE); Loading app/src/main/java/app/fedilab/android/ui/drawer/ConversationAdapter.java +2 −2 Original line number Diff line number Diff line Loading @@ -151,7 +151,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH holder.binding.spoiler.setVisibility(View.VISIBLE); holder.binding.spoiler.setText( conversation.last_status.getSpanSpoiler(context, new WeakReference<>(holder.binding.spoiler)), new WeakReference<>(holder.binding.spoiler), () -> notifyItemChanged(holder.getBindingAdapterPosition())), TextView.BufferType.SPANNABLE); } else { holder.binding.spoiler.setVisibility(View.GONE); Loading @@ -161,7 +161,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH //--- MAIN CONTENT --- holder.binding.statusContent.setText( conversation.last_status.getSpanContent(context, new WeakReference<>(holder.binding.statusContent)), new WeakReference<>(holder.binding.statusContent), () -> notifyItemChanged(holder.getBindingAdapterPosition())), TextView.BufferType.SPANNABLE); //--- DATE --- holder.binding.lastMessageDate.setText(Helper.dateDiff(context, conversation.last_status.created_at)); Loading Loading
app/src/main/java/app/fedilab/android/client/entities/api/Status.java +22 −6 Original line number Diff line number Diff line Loading @@ -115,6 +115,10 @@ public class Status implements Serializable, Cloneable { public transient boolean submitted = false; public transient boolean spoilerChecked = false; public Filter filteredByApp; public transient Spannable contentSpan; public transient Spannable contentSpoilerSpan; public transient Spannable contentTranslateSpan; @Override public boolean equals(@Nullable Object obj) { boolean same = false; Loading @@ -124,17 +128,29 @@ public class Status implements Serializable, Cloneable { return same; } public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference) { return SpannableHelper.convert(context, content, this, null, null, true, viewWeakReference); public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference, Callback callback) { if (contentSpan == null) { contentSpan = SpannableHelper.convert(context, content, this, null, null, true, viewWeakReference, callback); } return contentSpan; } public synchronized Spannable getSpanSpoiler(Context context, WeakReference<View> viewWeakReference, Callback callback) { if (contentSpoilerSpan == null) { contentSpoilerSpan = SpannableHelper.convert(context, spoiler_text, this, null, null, true, viewWeakReference, callback); } return contentSpoilerSpan; } public synchronized Spannable getSpanSpoiler(Context context, WeakReference<View> viewWeakReference) { return SpannableHelper.convert(context, spoiler_text, this, null, null, true, viewWeakReference); public synchronized Spannable getSpanTranslate(Context context, WeakReference<View> viewWeakReference, Callback callback) { if (contentTranslateSpan == null) { contentTranslateSpan = SpannableHelper.convert(context, translationContent, this, null, null, true, viewWeakReference, callback); } return contentTranslateSpan; } public synchronized Spannable getSpanTranslate(Context context, WeakReference<View> viewWeakReference) { return SpannableHelper.convert(context, translationContent, this, null, null, true, viewWeakReference); public interface Callback { void emojiFetched(); } @NonNull Loading
app/src/main/java/app/fedilab/android/helper/CustomEmoji.java +34 −2 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.text.SpannableStringBuilder; import android.text.style.ReplacementSpan; import android.view.View; Loading @@ -13,26 +14,53 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.target.Target; import com.bumptech.glide.request.transition.Transition; import java.lang.ref.WeakReference; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import app.fedilab.android.R; import app.fedilab.android.client.entities.api.Emoji; import app.fedilab.android.client.entities.api.Status; public class CustomEmoji extends ReplacementSpan { private final float scale; private final WeakReference<View> viewWeakReference; private Drawable imageDrawable; private boolean callbackCalled; CustomEmoji(WeakReference<View> viewWeakReference) { Context mContext = viewWeakReference.get().getContext(); this.viewWeakReference = viewWeakReference; SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(mContext); scale = sharedpreferences.getFloat(mContext.getString(R.string.SET_FONT_SCALE), 1.1f); callbackCalled = false; } public SpannableStringBuilder makeEmoji(SpannableStringBuilder content, List<Emoji> emojiList, boolean animate, Status.Callback callback) { if (emojiList != null && emojiList.size() > 0) { int count = 1; for (Emoji emoji : emojiList) { Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) .matcher(content); while (matcher.find()) { CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(viewWeakReference.get())); content.setSpan(customEmoji, matcher.start(), matcher.end(), 0); Glide.with(viewWeakReference.get()) .asDrawable() .load(animate ? emoji.url : emoji.static_url) .into(customEmoji.getTarget(animate, count == emojiList.size() && !callbackCalled ? callback : null)); } count++; } } return content; } @Override Loading Loading @@ -61,7 +89,7 @@ public class CustomEmoji extends ReplacementSpan { } } public Target<Drawable> getTarget(boolean animate) { public Target<Drawable> getTarget(boolean animate, Status.Callback callback) { return new CustomTarget<Drawable>() { @Override public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) { Loading Loading @@ -97,6 +125,10 @@ public class CustomEmoji extends ReplacementSpan { if (view != null) { view.invalidate(); } if (callback != null) { callbackCalled = true; callback.emojiFetched(); } } @Override Loading
app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +11 −18 Original line number Diff line number Diff line Loading @@ -89,10 +89,16 @@ public class SpannableHelper { public static final String CLICKABLE_SPAN = "CLICKABLE_SPAN"; public static Spannable convert(Context context, String text, Status status, Account account, Announcement announcement, boolean convertHtml, WeakReference<View> viewWeakReference) { return convert(context, text, status, account, announcement, convertHtml, viewWeakReference, null); } public static Spannable convert(Context context, String text, Status status, Account account, Announcement announcement, boolean convertHtml, WeakReference<View> viewWeakReference) { WeakReference<View> viewWeakReference, Status.Callback callback) { SpannableString initialContent; Loading Loading @@ -161,20 +167,8 @@ public class SpannableHelper { } SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); boolean animate = !sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); if (emojiList != null && emojiList.size() > 0) { for (Emoji emoji : emojiList) { Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) .matcher(content); while (matcher.find()) { CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view)); content.setSpan(customEmoji, matcher.start(), matcher.end(), 0); Glide.with(view) .asDrawable() .load(animate ? emoji.url : emoji.static_url) .into(customEmoji.getTarget(animate)); } } } content = customEmoji.makeEmoji(content, emojiList, animate, callback); if (imagesToReplace.size() > 0) { for (Map.Entry<String, String> entry : imagesToReplace.entrySet()) { Loading @@ -183,12 +177,11 @@ public class SpannableHelper { Matcher matcher = Pattern.compile(key, Pattern.LITERAL) .matcher(content); while (matcher.find()) { CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view)); content.setSpan(customEmoji, matcher.start(), matcher.end(), 0); Glide.with(view) .asDrawable() .load(url) .into(customEmoji.getTarget(animate)); .into(customEmoji.getTarget(animate, null)); } } Loading Loading @@ -1066,7 +1059,7 @@ public class SpannableHelper { Glide.with(viewWeakReference.get()) .asDrawable() .load(animate ? emoji.url : emoji.static_url) .into(customEmoji.getTarget(animate)); .into(customEmoji.getTarget(animate, null)); } } } Loading
app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java +2 −2 Original line number Diff line number Diff line Loading @@ -1107,7 +1107,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder StatusSimpleViewHolder holder = (StatusSimpleViewHolder) viewHolder; holder.binding.statusContent.setText( status.getSpanContent(context, new WeakReference<>(holder.binding.statusContent)), new WeakReference<>(holder.binding.statusContent), null), TextView.BufferType.SPANNABLE); MastodonHelper.loadPPMastodon(holder.binding.avatar, status.account); if (status.account != null) { Loading @@ -1122,7 +1122,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder holder.binding.spoiler.setVisibility(View.VISIBLE); holder.binding.spoiler.setText( status.getSpanSpoiler(context, new WeakReference<>(holder.binding.spoiler)), new WeakReference<>(holder.binding.spoiler), null), TextView.BufferType.SPANNABLE); } else { holder.binding.spoiler.setVisibility(View.GONE); Loading
app/src/main/java/app/fedilab/android/ui/drawer/ConversationAdapter.java +2 −2 Original line number Diff line number Diff line Loading @@ -151,7 +151,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH holder.binding.spoiler.setVisibility(View.VISIBLE); holder.binding.spoiler.setText( conversation.last_status.getSpanSpoiler(context, new WeakReference<>(holder.binding.spoiler)), new WeakReference<>(holder.binding.spoiler), () -> notifyItemChanged(holder.getBindingAdapterPosition())), TextView.BufferType.SPANNABLE); } else { holder.binding.spoiler.setVisibility(View.GONE); Loading @@ -161,7 +161,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH //--- MAIN CONTENT --- holder.binding.statusContent.setText( conversation.last_status.getSpanContent(context, new WeakReference<>(holder.binding.statusContent)), new WeakReference<>(holder.binding.statusContent), () -> notifyItemChanged(holder.getBindingAdapterPosition())), TextView.BufferType.SPANNABLE); //--- DATE --- holder.binding.lastMessageDate.setText(Helper.dateDiff(context, conversation.last_status.created_at)); Loading