diff --git a/app/src/main/java/app/fedilab/android/activities/AdminAccountActivity.java b/app/src/main/java/app/fedilab/android/activities/AdminAccountActivity.java index 8421d924a..94e63ecfc 100644 --- a/app/src/main/java/app/fedilab/android/activities/AdminAccountActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/AdminAccountActivity.java @@ -22,8 +22,6 @@ import android.content.SharedPreferences; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; import android.text.SpannableString; import android.text.Spanned; import android.text.method.LinkMovementMethod; @@ -46,6 +44,7 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -105,14 +104,7 @@ public class AdminAccountActivity extends BaseActivity { } binding.toolbar.setPopupTheme(Helper.popupStyle()); if (account != null) { - new Thread(() -> { - account = SpannableHelper.convertAccount(AdminAccountActivity.this, account); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> initializeView(account); - mainHandler.post(myRunnable); - - }).start(); - + initializeView(account); } else { Toasty.error(AdminAccountActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show(); finish(); @@ -314,7 +306,11 @@ public class AdminAccountActivity extends BaseActivity { } - binding.accountDn.setText(account.span_display_name != null ? account.span_display_name : account.display_name, TextView.BufferType.SPANNABLE); + binding.accountDn.setText( + account.getSpanDisplayName(AdminAccountActivity.this, + new WeakReference<>(binding.accountDn), + id -> binding.accountDn.invalidate()), + TextView.BufferType.SPANNABLE); binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setOnLongClickListener(v -> { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); diff --git a/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java b/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java index 701dbd07f..4feea554c 100644 --- a/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java @@ -22,8 +22,6 @@ import android.content.SharedPreferences; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; import android.text.SpannableString; import android.text.Spanned; import android.text.method.LinkMovementMethod; @@ -46,6 +44,7 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -105,14 +104,7 @@ public class AdminReportActivity extends BaseActivity { } binding.toolbar.setPopupTheme(Helper.popupStyle()); if (account != null) { - new Thread(() -> { - account = SpannableHelper.convertAccount(AdminReportActivity.this, account); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> initializeView(account); - mainHandler.post(myRunnable); - - }).start(); - + initializeView(account); } else { Toasty.error(AdminReportActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show(); finish(); @@ -331,8 +323,11 @@ public class AdminReportActivity extends BaseActivity { binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance()); } - - binding.accountDn.setText(account.span_display_name != null ? account.span_display_name : account.display_name, TextView.BufferType.SPANNABLE); + binding.accountDn.setText( + account.getSpanDisplayName(AdminReportActivity.this, + new WeakReference<>(binding.accountDn), + id -> binding.accountDn.invalidate()), + TextView.BufferType.SPANNABLE); binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setOnLongClickListener(v -> { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); diff --git a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java index ceea97968..050ce0095 100644 --- a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java @@ -84,7 +84,6 @@ import app.fedilab.android.helper.DividerDecorationSimple; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.MediaHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.interfaces.OnDownloadInterface; import app.fedilab.android.jobs.ScheduleThreadWorker; @@ -268,9 +267,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana } }); } else if (statusDraft != null) {//Restore a draft with all messages - if (statusDraft.statusReplyList != null) { - statusDraft.statusReplyList = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusDraft.statusReplyList); - } if (statusDraft.statusReplyList != null) { statusList.addAll(statusDraft.statusReplyList); binding.recyclerView.addItemDecoration(new DividerDecorationSimple(ComposeActivity.this, statusList)); @@ -285,7 +281,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana binding.recyclerView.scrollToPosition(composeAdapter.getItemCount() - 1); } else if (statusReply != null) { - statusReply = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusReply); statusList.add(statusReply); int statusCount = statusList.size(); statusDraftList.get(0).in_reply_to_id = statusReply.id; diff --git a/app/src/main/java/app/fedilab/android/activities/ContextActivity.java b/app/src/main/java/app/fedilab/android/activities/ContextActivity.java index 28aff52a9..0287e4d90 100644 --- a/app/src/main/java/app/fedilab/android/activities/ContextActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ContextActivity.java @@ -43,7 +43,6 @@ import app.fedilab.android.databinding.ActivityConversationBinding; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.ui.fragment.timeline.FragmentMastodonContext; import app.fedilab.android.viewmodel.mastodon.StatusesVM; @@ -88,7 +87,6 @@ public class ContextActivity extends BaseActivity { } MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.mastodon_account); Bundle bundle = new Bundle(); - focusedStatus = SpannableHelper.convertStatus(getApplication().getApplicationContext(), focusedStatus); bundle.putSerializable(Helper.ARG_STATUS, focusedStatus); currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, new FragmentMastodonContext(), bundle, null, null); StatusesVM timelinesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class); diff --git a/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java b/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java index a40c50ad4..7371dbbdf 100644 --- a/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java @@ -27,12 +27,7 @@ import android.content.SharedPreferences; import android.content.res.ColorStateList; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.os.Build; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.text.Html; -import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; import android.text.method.LinkMovementMethod; @@ -65,6 +60,7 @@ import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; import com.google.android.material.tabs.TabLayout; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -90,7 +86,6 @@ import app.fedilab.android.client.entities.app.WellKnownNodeinfo; import app.fedilab.android.databinding.ActivityProfileBinding; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.CrossActionHelper; -import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.SpannableHelper; @@ -165,14 +160,7 @@ public class ProfileActivity extends BaseActivity { binding.toolbar.setPopupTheme(Helper.popupStyle()); accountsVM = new ViewModelProvider(ProfileActivity.this).get(AccountsVM.class); if (account != null) { - new Thread(() -> { - account = SpannableHelper.convertAccount(ProfileActivity.this, account); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> initializeView(account); - mainHandler.post(myRunnable); - - }).start(); - + initializeView(account); } else if (account_id != null) { accountsVM.getAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account_id).observe(ProfileActivity.this, fetchedAccount -> { account = fetchedAccount; @@ -359,15 +347,12 @@ public class ProfileActivity extends BaseActivity { binding.fieldsContainer.setAdapter(fieldAdapter); binding.fieldsContainer.setLayoutManager(new LinearLayoutManager(ProfileActivity.this)); } - if (account.span_display_name == null && account.display_name == null) { - binding.accountDn.setText(account.username); - } else { - Spannable textAccount = account.span_display_name != null ? account.span_display_name : new SpannableString(account.display_name); - CustomEmoji.displayEmoji(ProfileActivity.this, account.emojis, textAccount, binding.accountDn, null, id -> { - binding.accountDn.setText(textAccount, TextView.BufferType.SPANNABLE); - }); - binding.accountDn.setText(textAccount, TextView.BufferType.SPANNABLE); - } + + binding.accountDn.setText( + account.getSpanDisplayName(ProfileActivity.this, + new WeakReference<>(binding.accountDn), + id -> binding.accountDn.invalidate()), + TextView.BufferType.SPANNABLE); binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setOnLongClickListener(v -> { @@ -381,13 +366,11 @@ public class ProfileActivity extends BaseActivity { clipboard.setPrimaryClip(clip); return false; }); - Spannable textNote; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - textNote = account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note, Html.FROM_HTML_MODE_COMPACT)); - else - textNote = account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note)); - CustomEmoji.displayEmoji(ProfileActivity.this, account.emojis, textNote, binding.accountNote, null, id -> binding.accountNote.setText(textNote, TextView.BufferType.SPANNABLE)); - binding.accountNote.setText(textNote, TextView.BufferType.SPANNABLE); + binding.accountNote.setText( + account.getSpanNote(ProfileActivity.this, + new WeakReference<>(binding.accountNote), + id -> binding.accountNote.invalidate()), + TextView.BufferType.SPANNABLE); binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance()); MastodonHelper.loadPPMastodon(binding.accountPp, account); diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java index 012314ab7..5450e7a26 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java @@ -14,15 +14,20 @@ package app.fedilab.android.client.entities.api; * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ +import android.content.Context; import android.text.Spannable; +import android.view.View; import com.google.gson.annotations.SerializedName; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; +import app.fedilab.android.helper.SpannableHelper; + public class Account implements Serializable { @SerializedName("id") @@ -74,11 +79,36 @@ public class Account implements Serializable { @SerializedName("moved") public Account moved; + public transient boolean emojiDisplayNameFetched = false; + public transient boolean emojiNoteFetched = false; //Some extra spannable element - They will be filled automatically when fetching the account - public transient Spannable span_display_name; - public transient Spannable span_note; + private transient Spannable span_display_name; + private transient Spannable span_note; + + public synchronized Spannable getSpanDisplayName(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { + if (span_display_name != null) { + return span_display_name; + } + if (display_name == null) { + display_name = username; + } + span_display_name = SpannableHelper.convert(context, display_name, null, this, true, viewWeakReference, !emojiDisplayNameFetched ? callback : null); + emojiDisplayNameFetched = true; + return span_display_name; + } + + + public synchronized Spannable getSpanNote(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { + if (span_note != null) { + return span_note; + } + span_note = SpannableHelper.convert(context, note, null, this, true, viewWeakReference, !emojiNoteFetched ? callback : null); + emojiNoteFetched = true; + return span_note; + } + public transient RelationShip relationShip; - public transient boolean emojiFetched = false; + public static class AccountParams implements Serializable { @SerializedName("discoverable") diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java b/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java index a0d80fbea..7df3e7c03 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java @@ -14,14 +14,19 @@ package app.fedilab.android.client.entities.api; * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ +import android.content.Context; import android.text.Spannable; +import android.view.View; import com.google.gson.annotations.SerializedName; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.Date; import java.util.List; +import app.fedilab.android.helper.SpannableHelper; + public class Poll implements Serializable { @SerializedName("id") @@ -55,5 +60,15 @@ public class Poll implements Serializable { //Some extra spannable element - They will be filled automatically when fetching the poll public transient Spannable span_title; + public transient boolean emojiTitleFetched = false; + + public Spannable getSpanTitle(Context context, Status status, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { + if (span_title != null) { + return span_title; + } + span_title = SpannableHelper.convert(context, title, status, null, true, viewWeakReference, !emojiTitleFetched ? callback : null); + emojiTitleFetched = true; + return span_title; + } } } diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Status.java b/app/src/main/java/app/fedilab/android/client/entities/api/Status.java index 5224dca53..159feea2d 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Status.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Status.java @@ -14,16 +14,21 @@ package app.fedilab.android.client.entities.api; * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ +import android.content.Context; import android.text.Spannable; +import android.view.View; import androidx.annotation.NonNull; import com.google.gson.annotations.SerializedName; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.Date; import java.util.List; +import app.fedilab.android.helper.SpannableHelper; + public class Status implements Serializable, Cloneable { @SerializedName("id") @@ -88,10 +93,9 @@ public class Status implements Serializable, Cloneable { public Attachment art_attachment; - //Some extra spannable element - They will be filled automatically when fetching the status - public transient Spannable span_content; - public transient Spannable span_spoiler_text; - public transient Spannable span_translate; + public transient boolean emojiContentFetched = false; + public transient boolean emojiSpoilerFetched = false; + public transient boolean emojiTranslateFetched = false; public boolean isExpended = false; public boolean isTruncated = true; public boolean isFetchMore = false; @@ -105,7 +109,46 @@ public class Status implements Serializable, Cloneable { public transient boolean setCursorToEnd = false; public transient int cursorPosition = 0; public transient boolean submitted = false; - public transient boolean emojiFetched = false; + //Some extra spannable element - They will be filled automatically when fetching the status + private transient Spannable span_content; + private transient Spannable span_spoiler_text; + private transient Spannable span_translate; + + public synchronized Spannable getSpanContent(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { + if (span_content != null) { + return span_content; + } + span_content = SpannableHelper.convert(context, content, this, null, true, viewWeakReference, !emojiContentFetched ? callback : null); + emojiContentFetched = true; + return span_content; + } + + + public Spannable getSpanContentNitter() { + if (span_content != null) { + return span_content; + } + span_content = SpannableHelper.convertNitter(content); + return span_content; + } + + public synchronized Spannable getSpanSpoiler(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { + if (span_spoiler_text != null) { + return span_spoiler_text; + } + span_spoiler_text = SpannableHelper.convert(context, spoiler_text, this, null, true, viewWeakReference, !emojiSpoilerFetched ? callback : null); + emojiSpoilerFetched = true; + return span_spoiler_text; + } + + public synchronized Spannable getSpanTranslate(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { + if (span_translate != null) { + return span_translate; + } + span_translate = SpannableHelper.convert(context, translationContent, this, null, true, viewWeakReference, !emojiTranslateFetched ? callback : null); + emojiTranslateFetched = true; + return span_translate; + } @NonNull public Object clone() throws CloneNotSupportedException { diff --git a/app/src/main/java/app/fedilab/android/client/entities/app/QuickLoad.java b/app/src/main/java/app/fedilab/android/client/entities/app/QuickLoad.java index d3f56ce41..26140deba 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/app/QuickLoad.java +++ b/app/src/main/java/app/fedilab/android/client/entities/app/QuickLoad.java @@ -29,7 +29,6 @@ import app.fedilab.android.client.entities.api.Notification; import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.sqlite.Sqlite; import app.fedilab.android.ui.fragment.timeline.FragmentMastodonNotification; @@ -576,7 +575,6 @@ public class QuickLoad { } quickLoad.position = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_POSITION)); //TimelineHelper.filterStatus(_mContext, quickLoad.statuses, TimelineHelper.FilterTimeLineType.PUBLIC); - quickLoad.statuses = SpannableHelper.convertStatus(_mContext, quickLoad.statuses); return quickLoad; } diff --git a/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java b/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java index 48cea1a0b..97b7fdc10 100644 --- a/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java @@ -278,8 +278,6 @@ public class CrossActionHelper { if (results != null) { if (results.statuses == null) { results.statuses = new ArrayList<>(); - } else { - results.statuses = SpannableHelper.convertStatus(context, results.statuses); } if (results.accounts == null) { results.accounts = new ArrayList<>(); @@ -308,15 +306,15 @@ public class CrossActionHelper { /** * Fetch and federate the remote status */ - public static void fetchRemoteAccount(@NonNull Context context, @NonNull BaseAccount ownerAccount, app.fedilab.android.client.entities.api.Account targetedAccount, Callback callback) { + public static void fetchRemoteAccount(@NonNull Context context, @NonNull BaseAccount ownerAccount, String targetedAcct, Callback callback) { MastodonSearchService mastodonSearchService = init(context, BaseMainActivity.currentInstance); String search; - if (targetedAccount.acct.contains("@")) { //Not from same instance - search = targetedAccount.acct; + if (targetedAcct.contains("@")) { //Not from same instance + search = targetedAcct; } else { - search = targetedAccount.acct + "@" + BaseMainActivity.currentInstance; + search = targetedAcct + "@" + BaseMainActivity.currentInstance; } new Thread(() -> { Call resultsCall = mastodonSearchService.search(ownerAccount.token, search, null, "accounts", false, true, false, 0, null, null, 1); @@ -329,8 +327,6 @@ public class CrossActionHelper { if (results != null) { if (results.statuses == null) { results.statuses = new ArrayList<>(); - } else { - results.statuses = SpannableHelper.convertStatus(context, results.statuses); } if (results.accounts == null) { results.accounts = new ArrayList<>(); @@ -374,8 +370,6 @@ public class CrossActionHelper { if (results != null) { if (results.statuses == null) { results.statuses = new ArrayList<>(); - } else { - results.statuses = SpannableHelper.convertStatus(context, results.statuses); } if (results.accounts == null) { results.accounts = new ArrayList<>(); diff --git a/app/src/main/java/app/fedilab/android/helper/NotificationsHelper.java b/app/src/main/java/app/fedilab/android/helper/NotificationsHelper.java index 60b3b7c1d..5fb0e16e6 100644 --- a/app/src/main/java/app/fedilab/android/helper/NotificationsHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/NotificationsHelper.java @@ -115,11 +115,6 @@ public class NotificationsHelper { if (notifications.notifications.size() > 0) { since_ids.put(slug, notifications.notifications.get(0).id); } - for (Notification notification : notifications.notifications) { - if (notification != null && notification.status != null) { - notification.status = SpannableHelper.convertStatus(context.getApplicationContext(), notification.status); - } - } } notifications.pagination = MastodonHelper.getPagination(notificationsResponse.headers()); } diff --git a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java index 19feda165..39fec6469 100644 --- a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java @@ -24,6 +24,9 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -35,16 +38,25 @@ import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextPaint; import android.text.style.ClickableSpan; +import android.text.style.ImageSpan; import android.text.style.URLSpan; +import android.util.Log; import android.util.Patterns; import android.view.LayoutInflater; import android.view.View; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceManager; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.target.CustomTarget; +import com.bumptech.glide.request.transition.Transition; import java.io.IOException; +import java.lang.ref.WeakReference; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; @@ -62,11 +74,9 @@ import app.fedilab.android.activities.ContextActivity; import app.fedilab.android.activities.HashTagActivity; import app.fedilab.android.activities.ProfileActivity; import app.fedilab.android.client.entities.api.Account; -import app.fedilab.android.client.entities.api.Announcement; import app.fedilab.android.client.entities.api.Attachment; -import app.fedilab.android.client.entities.api.Field; +import app.fedilab.android.client.entities.api.Emoji; import app.fedilab.android.client.entities.api.Mention; -import app.fedilab.android.client.entities.api.Poll; import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.databinding.PopupLinksBinding; import es.dmoral.toasty.Toasty; @@ -75,150 +85,143 @@ public class SpannableHelper { public static final String CLICKABLE_SPAN = "CLICKABLE_SPAN"; + public static Spannable convert(Context context, String text, + Status status, Account account, + boolean convertHtml, + WeakReference viewWeakReference, + EmojiCallback listener) { - private static Spannable convert(@NonNull Context context, @NonNull Status status, String text) { - return convert(context, status, text, true); - } - /** - * Convert HTML content to text. Also, it handles click on link - * This needs to be run asynchronously - * - * @param context {@link Context} - * @param status {@link Status} - Status concerned by the spannable transformation - * @param text String - text to convert, it can be content, spoiler, poll items, etc. - * @param convertHtml boolean - text need to be converted in html first - * @return Spannable string - */ - private static Spannable convert(@NonNull Context context, @NonNull Status status, String text, boolean convertHtml) { SpannableString initialContent; if (text == null) { return null; } - Matcher matcherALink = Helper.aLink.matcher(text); - //We stock details - HashMap urlDetails = new HashMap<>(); - while (matcherALink.find()) { - String urlText = matcherALink.group(3); - String url = matcherALink.group(2); - if (urlText != null) { - urlText = urlText.substring(1); - } - if (url != null && urlText != null && !url.equals(urlText) && !urlText.contains(" mentionList = null; + List emojiList = null; + if (status != null) { + mentionList = status.mentions; + emojiList = status.emojis; + id = status.id; + } else if (account != null) { + emojiList = account.emojis; + id = account.id; } - if(convertHtml) { + HashMap urlDetails = new HashMap<>(); + if (convertHtml) { + Matcher matcherALink = Helper.aLink.matcher(text); + //We stock details + while (matcherALink.find()) { + String urlText = matcherALink.group(3); + String url = matcherALink.group(2); + if (urlText != null) { + urlText = urlText.substring(1); + } + if (url != null && urlText != null && !url.equals(urlText) && !urlText.contains("= Build.VERSION_CODES.N) initialContent = new SpannableString(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)); else initialContent = new SpannableString(Html.fromHtml(text)); + + content = new SpannableStringBuilder(initialContent); + URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class); + for (URLSpan span : urls) { + content.removeSpan(span); + } + //Make tags, mentions, groups + interaction(context, content, mentionList); + //Make all links + linkify(context, content, urlDetails); } else { - initialContent = new SpannableString(text); + content = new SpannableStringBuilder(text); } - - SpannableStringBuilder content = new SpannableStringBuilder(initialContent); - URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class); - for (URLSpan span : urls) { - content.removeSpan(span); - } - - // --- For all patterns defined in Helper class --- - for (Map.Entry entry : Helper.patternHashMap.entrySet()) { - Helper.PatternType patternType = entry.getKey(); - Pattern pattern = entry.getValue(); - Matcher matcher = pattern.matcher(content); - while (matcher.find()) { - int matchStart = matcher.start(); - int matchEnd = matcher.end(); - String word = content.toString().substring(matchStart, matchEnd); - if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) { - URLSpan[] span = content.getSpans(matchStart, matchEnd, URLSpan.class); - content.removeSpan(span); - content.setSpan(new ClickableSpan() { - @Override - public void onClick(@NonNull View textView) { - textView.setTag(CLICKABLE_SPAN); - switch (patternType) { - case TAG: - Intent intent = new Intent(context, HashTagActivity.class); - Bundle b = new Bundle(); - b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim()); - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case GROUP: - break; - case MENTION: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - Mention targetedMention = null; - HashMap countUsername = new HashMap<>(); - - for (Mention mention : status.mentions) { - Integer count = countUsername.get(mention.username); - if (count == null) { - count = 0; - } - if (countUsername.containsKey(mention.username)) { - countUsername.put(mention.username, count + 1); - } else { - countUsername.put(mention.username, 1); + if (emojiList != null && emojiList.size() > 0) { + SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); + boolean animate = !sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); + int count = 1; + for (Emoji emoji : emojiList) { + int finalCount = count; + List finalEmojiList = emojiList; + String finalId = id; + List finalEmojiList1 = emojiList; + Glide.with(context) + .asDrawable() + .load(animate ? emoji.url : emoji.static_url) + .into( + new CustomTarget() { + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); + if (finalCount == finalEmojiList.size() && listener != null) { + listener.transformationDone(finalId); } } - for (Mention mention : status.mentions) { - Integer count = countUsername.get(mention.username); - if (count == null) { - count = 0; + + @Override + public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { + Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) + .matcher(content); + while (matcher.find()) { + ImageSpan imageSpan; + resource.setBounds(0, 0, (int) Helper.convertDpToPixel(20, context), (int) Helper.convertDpToPixel(20, context)); + resource.setVisible(true, true); + imageSpan = new ImageSpan(resource); + content.setSpan( + imageSpan, matcher.start(), + matcher.end(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); } - if (word.trim().compareToIgnoreCase("@" + mention.username) == 0 && count == 1) { - targetedMention = mention; - break; + if (animate && resource instanceof Animatable) { + Drawable.Callback callback = resource.getCallback(); + resource.setCallback(new Drawable.Callback() { + @Override + public void invalidateDrawable(@NonNull Drawable drawable) { + if (callback != null) { + callback.invalidateDrawable(drawable); + } + view.invalidate(); + } + + @Override + public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) { + if (callback != null) { + callback.scheduleDrawable(drawable, runnable, l); + } + } + + @Override + public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) { + if (callback != null) { + callback.unscheduleDrawable(drawable, runnable); + } + } + }); + ((Animatable) resource).start(); + + } + Log.v(Helper.TAG, ">: " + emoji.shortcode + " --> " + listener + " - " + finalCount + " <> " + finalEmojiList1.size()); + if (finalCount == finalEmojiList.size() && listener != null) { + Log.v(Helper.TAG, "OK FOR: " + emoji.shortcode + " --> " + finalId); + listener.transformationDone(finalId); } - } - if (targetedMention != null) { - b.putString(Helper.ARG_USER_ID, targetedMention.id); - } else { - b.putString(Helper.ARG_MENTION, word.trim()); } - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case MENTION_LONG: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - targetedMention = null; - for (Mention mention : status.mentions) { - if (word.trim().substring(1).compareToIgnoreCase("@" + mention.acct) == 0) { - targetedMention = mention; - break; - } - } - if (targetedMention != null) { - b.putString(Helper.ARG_USER_ID, targetedMention.id); - } else { - b.putString(Helper.ARG_MENTION, word.trim()); - } - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - } - } + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - ds.setColor(linkColor); - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } + } + } + ); + count++; } } + return trimSpannable(new SpannableStringBuilder(content)); + } + + private static void linkify(Context context, SpannableStringBuilder content, HashMap urlDetails) { //--- URLs ---- Matcher matcherLink = Patterns.WEB_URL.matcher(content); int offSetTruncate = 0; @@ -418,7 +421,7 @@ public class SpannableHelper { } }); } else {//It's an account - CrossActionHelper.fetchRemoteAccount(context, currentAccount, status.account, new CrossActionHelper.Callback() { + CrossActionHelper.fetchRemoteAccount(context, currentAccount, matcherLink.group(2) + "@" + matcherLink.group(1), new CrossActionHelper.Callback() { @Override public void federatedStatus(Status status) { } @@ -449,8 +452,126 @@ public class SpannableHelper { }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } } + } + private static void interaction(Context context, Spannable content, List mentions) { + // --- For all patterns defined in Helper class --- + for (Map.Entry entry : Helper.patternHashMap.entrySet()) { + Helper.PatternType patternType = entry.getKey(); + Pattern pattern = entry.getValue(); + Matcher matcher = pattern.matcher(content); + if (pattern == Helper.mentionPattern && mentions == null) { + continue; + } else if (pattern == Helper.mentionLongPattern && mentions == null) { + continue; + } + while (matcher.find()) { + int matchStart = matcher.start(); + int matchEnd = matcher.end(); + String word = content.toString().substring(matchStart, matchEnd); + if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) { + URLSpan[] span = content.getSpans(matchStart, matchEnd, URLSpan.class); + content.removeSpan(span); + content.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View textView) { + textView.setTag(CLICKABLE_SPAN); + switch (patternType) { + case TAG: + Intent intent = new Intent(context, HashTagActivity.class); + Bundle b = new Bundle(); + b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim()); + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + break; + case GROUP: + break; + case MENTION: + intent = new Intent(context, ProfileActivity.class); + b = new Bundle(); + Mention targetedMention = null; + HashMap countUsername = new HashMap<>(); + + if (mentions != null) { + for (Mention mention : mentions) { + Integer count = countUsername.get(mention.username); + if (count == null) { + count = 0; + } + if (countUsername.containsKey(mention.username)) { + countUsername.put(mention.username, count + 1); + } else { + countUsername.put(mention.username, 1); + } + } + for (Mention mention : mentions) { + Integer count = countUsername.get(mention.username); + if (count == null) { + count = 0; + } + if (word.trim().compareToIgnoreCase("@" + mention.username) == 0 && count == 1) { + targetedMention = mention; + break; + } + } + } + if (targetedMention != null) { + b.putString(Helper.ARG_USER_ID, targetedMention.id); + } else { + b.putString(Helper.ARG_MENTION, word.trim()); + } + + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + break; + case MENTION_LONG: + intent = new Intent(context, ProfileActivity.class); + b = new Bundle(); + targetedMention = null; + if (mentions != null) { + for (Mention mention : mentions) { + if (word.trim().substring(1).compareToIgnoreCase("@" + mention.acct) == 0) { + targetedMention = mention; + break; + } + } + } + if (targetedMention != null) { + b.putString(Helper.ARG_USER_ID, targetedMention.id); + } else { + b.putString(Helper.ARG_MENTION, word.trim()); + } + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + break; + } + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + ds.setColor(linkColor); + } + }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + } + } + } + + /** + * Convert HTML content to text. Also, it handles click on link + * This needs to be run asynchronously + * + * @param status {@link Status} - Status concerned by the spannable transformation + * @param content String - text to convert, it can be content, spoiler, poll items, etc. + * @return Spannable string + */ + private static void convertOuich(@NonNull Status status, SpannableStringBuilder content) { Matcher matcher = Helper.ouichesPattern.matcher(content); while (matcher.find()) { @@ -477,361 +598,28 @@ public class SpannableHelper { status.media_attachments.add(attachment); } } - return trimSpannable(new SpannableStringBuilder(content)); } - /** * Convert HTML content to text. Also, it handles click on link * This needs to be run asynchronously * - * @param context {@link Context} - * @param announcement {@link Announcement} - Announcement concerned by the spannable transformation - * @param text String - text to convert, it can be content, spoiler, poll items, etc. + * @param text String - text to convert, it can be content, spoiler, poll items, etc. * @return Spannable string */ - private static Spannable convert(@NonNull Context context, @NonNull Announcement announcement, String text) { + public static Spannable convertNitter(String text) { SpannableString initialContent; if (text == null) { return null; } - Matcher matcherALink = Helper.aLink.matcher(text); - //We stock details - HashMap urlDetails = new HashMap<>(); - while (matcherALink.find()) { - String urlText = matcherALink.group(3); - String url = matcherALink.group(2); - if (urlText != null) { - urlText = urlText.substring(1); - } - if (url != null && urlText != null && !url.equals(urlText) && !urlText.contains("= Build.VERSION_CODES.N) initialContent = new SpannableString(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)); else initialContent = new SpannableString(Html.fromHtml(text)); - - SpannableStringBuilder content = new SpannableStringBuilder(initialContent); - URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class); - for (URLSpan span : urls) { - content.removeSpan(span); - } - - //--- URLs ---- - Matcher matcherLink = Patterns.WEB_URL.matcher(content); - int offSetTruncate = 0; - while (matcherLink.find()) { - int matchStart = matcherLink.start() - offSetTruncate; - int matchEnd = matchStart + matcherLink.group().length(); - if (matchEnd > content.toString().length()) { - matchEnd = content.toString().length(); - } - - if (content.toString().length() < matchEnd || matchStart < 0 || matchStart > matchEnd) { - continue; - } - final String url = content.toString().substring(matchStart, matchEnd); - String newURL = Helper.transformURL(context, url); - //If URL has been transformed - if (newURL.compareTo(url) != 0) { - content.replace(matchStart, matchEnd, newURL); - offSetTruncate += (newURL.length() - url.length()); - matchEnd = matchStart + newURL.length(); - //The transformed URL was in the list of URLs having a different names - if (urlDetails.containsKey(url)) { - urlDetails.put(newURL, urlDetails.get(url)); - } - } - //Truncate URL if needed - //TODO: add an option to disable truncated URLs - String urlText = newURL; - if (newURL.length() > 30 && !urlDetails.containsKey(urlText)) { - urlText = urlText.substring(0, 30); - urlText += "…"; - content.replace(matchStart, matchEnd, urlText); - matchEnd = matchStart + 31; - offSetTruncate += (newURL.length() - urlText.length()); - } else if (urlDetails.containsKey(urlText) && urlDetails.get(urlText) != null) { - urlText = urlDetails.get(urlText); - if (urlText != null) { - content.replace(matchStart, matchEnd, urlText); - matchEnd = matchStart + urlText.length(); - offSetTruncate += (newURL.length() - urlText.length()); - } - } - - if (matchEnd <= content.length() && matchEnd >= matchStart) { - content.setSpan(new LongClickableSpan() { - @Override - public void onLongClick(View view) { - Context mContext = view.getContext(); - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext, Helper.dialogStyle()); - PopupLinksBinding popupLinksBinding = PopupLinksBinding.inflate(LayoutInflater.from(context)); - dialogBuilder.setView(popupLinksBinding.getRoot()); - AlertDialog alertDialog = dialogBuilder.create(); - alertDialog.show(); - - popupLinksBinding.displayFullLink.setOnClickListener(v -> { - AlertDialog.Builder builder = new AlertDialog.Builder(mContext, Helper.dialogStyle()); - builder.setMessage(url); - builder.setTitle(context.getString(R.string.display_full_link)); - builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss()) - .show(); - alertDialog.dismiss(); - }); - popupLinksBinding.shareLink.setOnClickListener(v -> { - Intent sendIntent = new Intent(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via)); - sendIntent.putExtra(Intent.EXTRA_TEXT, url); - sendIntent.setType("text/plain"); - sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - Intent intentChooser = Intent.createChooser(sendIntent, context.getString(R.string.share_with)); - intentChooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intentChooser); - alertDialog.dismiss(); - }); - - popupLinksBinding.openOtherApp.setOnClickListener(v -> { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(url)); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - context.startActivity(intent); - } catch (Exception e) { - Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show(); - } - alertDialog.dismiss(); - }); - - popupLinksBinding.copyLink.setOnClickListener(v -> { - ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, url); - if (clipboard != null) { - clipboard.setPrimaryClip(clip); - Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show(); - } - alertDialog.dismiss(); - }); - - popupLinksBinding.checkRedirect.setOnClickListener(v -> { - try { - - URL finalUrlCheck = new URL(url); - new Thread(() -> { - try { - String redirect = null; - HttpsURLConnection httpsURLConnection = (HttpsURLConnection) finalUrlCheck.openConnection(); - httpsURLConnection.setConnectTimeout(10 * 1000); - httpsURLConnection.setRequestProperty("http.keepAlive", "false"); - httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT); - httpsURLConnection.setRequestMethod("HEAD"); - if (httpsURLConnection.getResponseCode() == 301 || httpsURLConnection.getResponseCode() == 302) { - Map> map = httpsURLConnection.getHeaderFields(); - for (Map.Entry> entry : map.entrySet()) { - if (entry.toString().toLowerCase().startsWith("location")) { - Matcher matcher = urlPattern.matcher(entry.toString()); - if (matcher.find()) { - redirect = matcher.group(1); - } - } - } - } - httpsURLConnection.getInputStream().close(); - if (redirect != null && redirect.compareTo(url) != 0) { - URL redirectURL = new URL(redirect); - String host = redirectURL.getHost(); - String protocol = redirectURL.getProtocol(); - if (protocol == null || host == null) { - redirect = null; - } - } - Handler mainHandler = new Handler(context.getMainLooper()); - String finalRedirect = redirect; - Runnable myRunnable = () -> { - AlertDialog.Builder builder1 = new AlertDialog.Builder(view.getContext(), Helper.dialogStyle()); - if (finalRedirect != null) { - builder1.setMessage(context.getString(R.string.redirect_detected, url, finalRedirect)); - builder1.setNegativeButton(R.string.copy_link, (dialog, which) -> { - ClipboardManager clipboard1 = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip1 = ClipData.newPlainText(Helper.CLIP_BOARD, finalRedirect); - if (clipboard1 != null) { - clipboard1.setPrimaryClip(clip1); - Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show(); - } - dialog.dismiss(); - }); - builder1.setNeutralButton(R.string.share_link, (dialog, which) -> { - Intent sendIntent1 = new Intent(Intent.ACTION_SEND); - sendIntent1.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via)); - sendIntent1.putExtra(Intent.EXTRA_TEXT, url); - sendIntent1.setType("text/plain"); - context.startActivity(Intent.createChooser(sendIntent1, context.getString(R.string.share_with))); - dialog.dismiss(); - }); - } else { - builder1.setMessage(R.string.no_redirect); - } - builder1.setTitle(context.getString(R.string.check_redirect)); - builder1.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss()) - .show(); - - }; - mainHandler.post(myRunnable); - } catch (IOException e) { - e.printStackTrace(); - } - - }).start(); - } catch (MalformedURLException e) { - e.printStackTrace(); - } - - alertDialog.dismiss(); - }); - - } - - @Override - public void onClick(@NonNull View textView) { - textView.setTag(CLICKABLE_SPAN); - Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$"); - Matcher matcherLink = link.matcher(url); - if (matcherLink.find() && !url.contains("medium.com")) { - if (matcherLink.group(3) != null && Objects.requireNonNull(matcherLink.group(3)).length() > 0) { //It's a toot - CrossActionHelper.fetchRemoteStatus(context, currentAccount, url, new CrossActionHelper.Callback() { - @Override - public void federatedStatus(Status status) { - Intent intent = new Intent(context, ContextActivity.class); - intent.putExtra(Helper.ARG_STATUS, status); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } - - @Override - public void federatedAccount(Account account) { - - } - }); - } - } else { - Helper.openBrowser(context, newURL); - } - - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - ds.setColor(linkColor); - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - } - - // --- For all patterns defined in Helper class --- - for (Map.Entry entry : Helper.patternHashMap.entrySet()) { - Helper.PatternType patternType = entry.getKey(); - Pattern pattern = entry.getValue(); - Matcher matcher = pattern.matcher(content); - while (matcher.find()) { - - int matchStart = matcher.start(); - int matchEnd = matcher.end(); - String word = content.toString().substring(matchStart, matchEnd); - if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) { - URLSpan[] span = content.getSpans(matchStart, matchEnd, URLSpan.class); - content.removeSpan(span); - - content.setSpan(new ClickableSpan() { - @Override - public void onClick(@NonNull View textView) { - textView.setTag(CLICKABLE_SPAN); - switch (patternType) { - case TAG: - Intent intent = new Intent(context, HashTagActivity.class); - Bundle b = new Bundle(); - b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim()); - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case GROUP: - break; - case MENTION: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - Mention targetedMention = null; - HashMap countUsername = new HashMap<>(); - for (Mention mention : announcement.mentions) { - Integer count = countUsername.get(mention.username); - if (count == null) { - count = 0; - } - if (countUsername.containsKey(mention.username)) { - countUsername.put(mention.username, count + 1); - } else { - countUsername.put(mention.username, 1); - } - } - for (Mention mention : announcement.mentions) { - Integer count = countUsername.get(mention.username); - if (count == null) { - count = 0; - } - if (word.trim().compareToIgnoreCase("@" + mention.username) == 0 && count == 1) { - targetedMention = mention; - break; - } - } - if (targetedMention != null) { - b.putString(Helper.ARG_USER_ID, targetedMention.id); - } else { - b.putString(Helper.ARG_MENTION, word.trim()); - } - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case MENTION_LONG: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - targetedMention = null; - for (Mention mention : announcement.mentions) { - if (word.trim().substring(1).compareToIgnoreCase("@" + mention.acct) == 0) { - targetedMention = mention; - break; - } - } - - if (targetedMention != null) { - b.putString(Helper.ARG_USER_ID, targetedMention.id); - } else { - b.putString(Helper.ARG_MENTION, word.trim()); - } - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - } - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - ds.setColor(linkColor); - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - } - } - return trimSpannable(new SpannableStringBuilder(content)); + return initialContent; } + /** * Remove extra carriage returns at the bottom due to

tags in toots * @@ -856,245 +644,6 @@ public class SpannableHelper { return spannable.delete(0, trimStart).delete(spannable.length() - trimEnd, spannable.length()); } - public static List convertStatus(Context context, List statuses) { - if (statuses != null) { - for (Status status : statuses) { - convertStatus(context, status); - } - } - return statuses; - } - - public static List convertNitterStatus(List statuses) { - if (statuses != null) { - for (Status status : statuses) { - convertNitterStatus(status); - } - } - return statuses; - } - - public static void convertNitterStatus(Status status) { - if (status != null) { - status.span_content = SpannableHelper.convertNitter(status.content); - } - } - - /** - * Convert HTML content to text. Also, it handles click on link - * This needs to be run asynchronously - * - * @param text String - text to convert, it can be content, spoiler, poll items, etc. - * @return Spannable string - */ - private static Spannable convertNitter(String text) { - SpannableString initialContent; - if (text == null) { - return null; - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - initialContent = new SpannableString(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)); - else - initialContent = new SpannableString(Html.fromHtml(text)); - return initialContent; - } - - public static List convertAnnouncement(Context context, List announcements) { - if (announcements != null) { - for (Announcement announcement : announcements) { - convertAnnouncement(context, announcement); - } - } - return announcements; - } - - - public static Announcement convertAnnouncement(Context context, Announcement announcement) { - if (announcement != null) { - announcement.span_content = SpannableHelper.convert(context, announcement, announcement.content); - } - return announcement; - } - - public static Status convertStatus(Context context, Status status) { - if (status != null) { - status.span_content = SpannableHelper.convert(context, status, status.content); - status.span_spoiler_text = SpannableHelper.convert(context, status, status.spoiler_text); - if (status.translationContent != null) { - status.span_translate = SpannableHelper.convert(context, status, status.translationContent); - } - if (status.account == null) { - return status; - } - status.account.span_display_name = SpannableHelper.convertA(context, status.account.display_name, true); - if (status.poll != null) { - for (Poll.PollItem pollItem : status.poll.options) { - pollItem.span_title = SpannableHelper.convert(context, status, pollItem.title, false); - } - } - if (status.reblog != null) { - status.reblog.span_content = SpannableHelper.convert(context, status, status.reblog.content); - if (status.reblog.translationContent != null) { - status.reblog.span_translate = SpannableHelper.convert(context, status, status.reblog.translationContent); - } - status.reblog.span_spoiler_text = SpannableHelper.convert(context, status, status.reblog.spoiler_text); - status.reblog.account.span_display_name = SpannableHelper.convertA(context, status.reblog.account.display_name, true); - if (status.reblog.poll != null) { - for (Poll.PollItem pollItem : status.reblog.poll.options) { - pollItem.span_title = SpannableHelper.convert(context, status, pollItem.title, false); - } - } - } - } - return status; - } - - - public static List convertAccounts(Context context, List accounts) { - if (accounts != null) { - for (Account account : accounts) { - convertAccount(context, account); - } - } - return accounts; - } - - public static Account convertAccount(Context context, Account account) { - if (account != null) { - account.span_display_name = SpannableHelper.convertA(context, account.display_name, true); - account.span_note = SpannableHelper.convertA(context, account.note, false); - if (account.fields != null && account.fields.size() > 0) { - List fields = new ArrayList<>(); - for (Field field : account.fields) { - field.value_span = SpannableHelper.convertA(context, field.value, false); - fields.add(field); - } - account.fields = fields; - } - } - return account; - } - - - /** - * Convert HTML content to text. Also, it handles click on link - * This needs to be run asynchronously - * - * @param context {@link Context} - * @param text String - text to convert, it can be display name or bio - * @return Spannable string - */ - private static Spannable convertA(@NonNull Context context, String text, boolean limitedToDisplayName) { - SpannableString initialContent; - if (text == null) { - return null; - } - if (!limitedToDisplayName) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - initialContent = new SpannableString(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)); - else - initialContent = new SpannableString(Html.fromHtml(text)); - } else { - initialContent = new SpannableString(text); - } - SpannableStringBuilder content = new SpannableStringBuilder(initialContent); - URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class); - for (URLSpan span : urls) - content.removeSpan(span); - //--- URLs ---- - Matcher matcherALink = Patterns.WEB_URL.matcher(content); - - int offSetTruncate = 0; - while (matcherALink.find()) { - int matchStart = matcherALink.start() - offSetTruncate; - int matchEnd = matchStart + matcherALink.group().length(); - //Get real URL - if (matcherALink.start(1) > matcherALink.end(1) || matcherALink.end() > content.length()) { - continue; - } - final String url = content.toString().substring(matchStart, matchEnd); - //Truncate URL if needed - //TODO: add an option to disable truncated URLs - String urlText = url; - if (url.length() > 30 && matchStart < matchEnd) { - urlText = urlText.substring(0, 30); - urlText += "…"; - content.replace(matchStart, matchEnd, urlText); - matchEnd = matcherALink.end() - (url.length() - urlText.length()); - offSetTruncate += (url.length() - urlText.length()); - } - if (!urlText.startsWith("http")) { - continue; - } - if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) - content.setSpan(new ClickableSpan() { - @Override - public void onClick(@NonNull View textView) { - textView.setTag(CLICKABLE_SPAN); - Helper.openBrowser(context, url); - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - ds.setColor(linkColor); - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - - // --- For all patterns defined in Helper class --- - for (Map.Entry entry : Helper.patternHashMap.entrySet()) { - Helper.PatternType patternType = entry.getKey(); - Pattern pattern = entry.getValue(); - Matcher matcher = pattern.matcher(content); - while (matcher.find()) { - int matchStart = matcher.start(); - int matchEnd = matcher.end(); - if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) { - URLSpan[] span = content.getSpans(matchStart, matchEnd, URLSpan.class); - content.removeSpan(span); - String word = content.toString().substring(matchStart, matchEnd); - content.setSpan(new ClickableSpan() { - @Override - public void onClick(@NonNull View textView) { - textView.setTag(CLICKABLE_SPAN); - switch (patternType) { - case TAG: - Intent intent = new Intent(context, HashTagActivity.class); - Bundle b = new Bundle(); - b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim()); - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case GROUP: - break; - case MENTION_LONG: - case MENTION: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - b.putString(Helper.ARG_MENTION, word.trim()); - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - } - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - ds.setColor(linkColor); - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - } - } - return trimSpannable(new SpannableStringBuilder(content)); - } - /** * Makes the move to account clickable * @@ -1128,4 +677,9 @@ public class SpannableHelper { } return spannableString; } + + + public interface EmojiCallback { + void transformationDone(String id); + } } diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java index 05e1b2fa9..53b7303d9 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java @@ -37,6 +37,7 @@ import androidx.lifecycle.ViewModelStoreOwner; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.RecyclerView; +import java.lang.ref.WeakReference; import java.util.Date; import java.util.List; @@ -45,7 +46,6 @@ import app.fedilab.android.R; import app.fedilab.android.activities.ProfileActivity; import app.fedilab.android.client.entities.api.Account; import app.fedilab.android.databinding.DrawerAccountBinding; -import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.viewmodel.mastodon.AccountsVM; @@ -225,21 +225,17 @@ public class AccountAdapter extends RecyclerView.Adapter { - if (!account.emojiFetched) { - account.emojiFetched = true; - accountViewHolder.binding.displayName.post(() -> adapter.notifyItemChanged(position)); - } - }); - accountViewHolder.binding.displayName.setText(account.span_display_name, TextView.BufferType.SPANNABLE); + accountViewHolder.binding.displayName.setText( + account.getSpanDisplayName(context, + new WeakReference<>(accountViewHolder.binding.displayName), + id -> adapter.notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); accountViewHolder.binding.username.setText(String.format("@%s", account.acct)); - CustomEmoji.displayEmoji(context, account.emojis, account.span_note, accountViewHolder.binding.bio, account.id, id -> { - if (!account.emojiFetched) { - account.emojiFetched = true; - accountViewHolder.binding.bio.post(() -> adapter.notifyItemChanged(position)); - } - }); - accountViewHolder.binding.bio.setText(account.span_note, TextView.BufferType.SPANNABLE); + accountViewHolder.binding.bio.setText( + account.getSpanNote(context, + new WeakReference<>(accountViewHolder.binding.bio), + id -> adapter.notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); } public int getCount() { diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/AccountListAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/AccountListAdapter.java index 77accd35b..cbcaef6e4 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/AccountListAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/AccountListAdapter.java @@ -27,6 +27,7 @@ import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelStoreOwner; import androidx.recyclerview.widget.RecyclerView; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -78,7 +79,11 @@ public class AccountListAdapter extends RecyclerView.Adapter(holder.binding.displayName), + id -> notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); holder.binding.username.setText(String.format("@%s", account.acct)); if (searchList != null) { diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java index 34509b108..d1e667221 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java @@ -73,6 +73,7 @@ import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; import java.io.File; +import java.lang.ref.WeakReference; import java.text.Normalizer; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -103,7 +104,6 @@ import app.fedilab.android.databinding.DrawerStatusComposeBinding; import app.fedilab.android.databinding.DrawerStatusSimpleBinding; import app.fedilab.android.databinding.PopupMediaDescriptionBinding; import app.fedilab.android.exception.DBException; -import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.ThemeHelper; @@ -1041,25 +1041,25 @@ public class ComposeAdapter extends RecyclerView.Adapter { - if (!status.emojiFetched) { - status.emojiFetched = true; - holder.binding.statusContent.post(() -> notifyItemChanged(position)); - } - }); - holder.binding.statusContent.setText(status.span_content, TextView.BufferType.SPANNABLE); + holder.binding.statusContent.setText( + status.getSpanContent(context, + new WeakReference<>(holder.binding.statusContent), + id -> notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); MastodonHelper.loadPPMastodon(holder.binding.avatar, status.account); - CustomEmoji.displayEmoji(context, status.account.emojis, status.account.span_display_name, holder.binding.displayName, status.id, id -> { - if (!status.account.emojiFetched) { - status.account.emojiFetched = true; - holder.binding.statusContent.post(() -> notifyItemChanged(position)); - } - }); - holder.binding.displayName.setText(status.account.span_display_name, TextView.BufferType.SPANNABLE); + holder.binding.displayName.setText( + status.account.getSpanDisplayName(context, + new WeakReference<>(holder.binding.displayName), + id -> notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); holder.binding.username.setText(String.format("@%s", status.account.acct)); if (status.spoiler_text != null && !status.spoiler_text.trim().isEmpty()) { holder.binding.spoiler.setVisibility(View.VISIBLE); - holder.binding.spoiler.setText(status.span_spoiler_text, TextView.BufferType.SPANNABLE); + holder.binding.spoiler.setText( + status.getSpanSpoiler(context, + new WeakReference<>(holder.binding.spoiler), + id -> notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); } else { holder.binding.spoiler.setVisibility(View.GONE); holder.binding.spoiler.setText(null); diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/ConversationAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/ConversationAdapter.java index 70f2484d7..561e34b2c 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/ConversationAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/ConversationAdapter.java @@ -36,6 +36,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.Timer; @@ -49,7 +50,6 @@ import app.fedilab.android.client.entities.api.Conversation; import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.databinding.DrawerConversationBinding; import app.fedilab.android.databinding.ThumbnailBinding; -import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; @@ -138,26 +138,22 @@ public class ConversationAdapter extends RecyclerView.Adapter { - if (!conversation.last_status.emojiFetched) { - conversation.last_status.emojiFetched = true; - holder.binding.spoiler.post(() -> notifyItemChanged(position)); - } - }); - holder.binding.spoiler.setText(conversation.last_status.span_spoiler_text, TextView.BufferType.SPANNABLE); + holder.binding.spoiler.setText( + conversation.last_status.getSpanSpoiler(context, + new WeakReference<>(holder.binding.spoiler), + id -> notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); } else { holder.binding.spoiler.setVisibility(View.GONE); holder.binding.spoilerExpand.setVisibility(View.GONE); holder.binding.spoiler.setText(null); } //--- MAIN CONTENT --- - CustomEmoji.displayEmoji(context, conversation.last_status.emojis, conversation.last_status.span_content, holder.binding.statusContent, conversation.last_status.id, id -> { - if (!conversation.last_status.emojiFetched) { - conversation.last_status.emojiFetched = true; - holder.binding.statusContent.post(() -> notifyItemChanged(position)); - } - }); - holder.binding.statusContent.setText(conversation.last_status.span_content, TextView.BufferType.SPANNABLE); + holder.binding.statusContent.setText( + conversation.last_status.getSpanContent(context, + new WeakReference<>(holder.binding.statusContent), + id -> notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); //--- DATE --- holder.binding.lastMessageDate.setText(Helper.dateDiff(context, conversation.last_status.created_at)); diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/NotificationAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/NotificationAdapter.java index 27150f845..d383571e1 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/NotificationAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/NotificationAdapter.java @@ -21,8 +21,6 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.text.Spannable; -import android.text.SpannableString; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -34,6 +32,7 @@ import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelStoreOwner; import androidx.recyclerview.widget.RecyclerView; +import java.lang.ref.WeakReference; import java.util.List; import java.util.Locale; @@ -45,7 +44,6 @@ import app.fedilab.android.databinding.DrawerFetchMoreBinding; import app.fedilab.android.databinding.DrawerFollowBinding; import app.fedilab.android.databinding.DrawerStatusNotificationBinding; import app.fedilab.android.databinding.NotificationsRelatedAccountsBinding; -import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.viewmodel.mastodon.SearchVM; @@ -118,24 +116,37 @@ public class NotificationAdapter extends RecyclerView.Adapter - Not null when calling from notification adapter + * @param id String - Current status + * @return int - position in real time + */ + public static int getPositionAsync(List notificationList, String id) { + int position = 0; + if (notificationList != null) { + for (Notification notification : notificationList) { + if (notification.status != null && notification.status.id.compareTo(id) == 0) { + break; + } + position++; + } + } + return position; + } + @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { Notification notification = notificationList.get(position); if (getItemViewType(position) == TYPE_FOLLOW || getItemViewType(position) == TYPE_FOLLOW_REQUEST) { ViewHolderFollow holderFollow = (ViewHolderFollow) viewHolder; MastodonHelper.loadPPMastodon(holderFollow.binding.avatar, notification.account); - if (notification.account.span_display_name == null && notification.account.display_name != null) { - notification.account.span_display_name = new SpannableString(notification.account.display_name); - } else { - notification.account.span_display_name = new SpannableString(notification.account.username); - } - CustomEmoji.displayEmoji(context, notification.account.emojis, notification.account.span_display_name, holderFollow.binding.displayName, notification.id, id -> { - if (!notification.account.emojiFetched) { - notification.account.emojiFetched = true; - holderFollow.binding.displayName.post(() -> notifyItemChanged(position)); - } - }); - holderFollow.binding.displayName.setText(notification.account.span_display_name, TextView.BufferType.SPANNABLE); + holderFollow.binding.displayName.setText( + notification.account.getSpanDisplayName(context, + new WeakReference<>(holderFollow.binding.displayName), + id -> notifyItemChanged(getPositionAsync(notificationList, id))), + TextView.BufferType.SPANNABLE); holderFollow.binding.username.setText(String.format("@%s", notification.account.acct)); if (getItemViewType(position) == TYPE_FOLLOW_REQUEST) { holderFollow.binding.rejectButton.setVisibility(View.VISIBLE); @@ -197,19 +208,18 @@ public class NotificationAdapter extends RecyclerView.Adapter { - if (!notification.account.emojiFetched) { - notification.account.emojiFetched = true; - holderStatus.binding.displayName.post(() -> notifyItemChanged(position)); - } - }); - holderStatus.bindingNotification.status.displayName.setText(title, TextView.BufferType.SPANNABLE); + notification.account.display_name = title; + holderStatus.bindingNotification.status.displayName.setText( + notification.account.getSpanDisplayName(context, + new WeakReference<>(holderStatus.bindingNotification.status.displayName), + id -> holderStatus.bindingNotification.status.displayName.post(() -> notifyItemChanged(getPositionAsync(notificationList, id)))), + TextView.BufferType.SPANNABLE); holderStatus.bindingNotification.status.username.setText(String.format("@%s", notification.account.acct)); holderStatus.bindingNotification.containerTransparent.setAlpha(.1f); if (notification.status != null && notification.status.visibility.equalsIgnoreCase("direct")) { @@ -220,14 +230,14 @@ public class NotificationAdapter extends RecyclerView.Adapter 0) { if (notification.type.equals("favourite")) { @@ -276,12 +286,12 @@ public class NotificationAdapter extends RecyclerView.Adapter { - if (!notification.account.emojiFetched) { - notification.account.emojiFetched = true; - holderStatus.binding.displayName.post(() -> notifyItemChanged(position)); - } - }); + notification.account.display_name = title; + holderStatus.bindingNotification.status.displayName.setText( + notification.account.getSpanDisplayName(context, + new WeakReference<>(holderStatus.bindingNotification.status.displayName), + id -> holderStatus.bindingNotification.status.displayName.post(() -> notifyItemChanged(getPositionAsync(notificationList, id)))), + TextView.BufferType.SPANNABLE); holderStatus.bindingNotification.status.displayName.setText(title, TextView.BufferType.SPANNABLE); holderStatus.bindingNotification.status.username.setText(String.format("@%s", notification.account.acct)); holderStatus.bindingNotification.status.actionButtons.setVisibility(View.GONE); @@ -289,6 +299,7 @@ public class NotificationAdapter extends RecyclerView.Adapter //--- ACCOUNT INFO --- MastodonHelper.loadPPMastodon(holder.binding.avatar, statusToDeal.account); - Spannable span_display_name = statusToDeal.account.span_display_name; - if (span_display_name == null || span_display_name.toString().trim().length() == 0) { - span_display_name = new SpannableString(statusToDeal.account.username); - } - CustomEmoji.displayEmoji(context, statusToDeal.account.emojis, span_display_name, holder.binding.displayName, status.id, id -> { - if (!statusToDeal.account.emojiFetched) { - statusToDeal.account.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - holder.binding.displayName.setText(span_display_name, TextView.BufferType.SPANNABLE); + holder.binding.displayName.setText( + statusToDeal.account.getSpanDisplayName(context, + new WeakReference<>(holder.binding.displayName), + id -> holder.binding.displayName.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); if (theme_text_header_1_line != -1) { holder.binding.displayName.setTextColor(theme_text_header_1_line); } @@ -823,16 +814,11 @@ public class StatusAdapter extends RecyclerView.Adapter if (expand_cw || expand) { holder.binding.spoilerExpand.setVisibility(View.VISIBLE); holder.binding.spoiler.setVisibility(View.VISIBLE); - CustomEmoji.displayEmoji(context, statusToDeal.emojis, statusToDeal.span_spoiler_text, holder.binding.spoiler, status.id, id -> { - if (!statusToDeal.emojiFetched) { - statusToDeal.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - holder.binding.spoiler.setText(statusToDeal.span_spoiler_text, TextView.BufferType.SPANNABLE); + holder.binding.spoiler.setText( + statusToDeal.getSpanSpoiler(context, + new WeakReference<>(holder.binding.spoiler), + id -> holder.binding.spoiler.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); statusToDeal.isExpended = true; statusToDeal.isMediaDisplayed = true; } else { @@ -843,16 +829,12 @@ public class StatusAdapter extends RecyclerView.Adapter }); holder.binding.spoilerExpand.setVisibility(View.VISIBLE); holder.binding.spoiler.setVisibility(View.VISIBLE); - CustomEmoji.displayEmoji(context, statusToDeal.emojis, statusToDeal.span_spoiler_text, holder.binding.spoiler, status.id, id -> { - if (!statusToDeal.emojiFetched) { - statusToDeal.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - holder.binding.spoiler.setText(statusToDeal.span_spoiler_text, TextView.BufferType.SPANNABLE); + + holder.binding.spoiler.setText( + statusToDeal.getSpanSpoiler(context, + new WeakReference<>(holder.binding.spoiler), + id -> holder.binding.spoiler.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); } if (statusToDeal.isExpended) { holder.binding.spoilerExpand.setText(context.getString(R.string.hide_content)); @@ -868,20 +850,13 @@ public class StatusAdapter extends RecyclerView.Adapter //--- BOOSTER INFO --- if (status.reblog != null) { MastodonHelper.loadPPMastodon(holder.binding.statusBoosterAvatar, status.account); - Spannable span_display_name_boost = status.account.span_display_name; - if (span_display_name_boost == null || span_display_name_boost.toString().trim().length() == 0) { - span_display_name_boost = new SpannableString(status.account.username); - } - CustomEmoji.displayEmoji(context, statusToDeal.account.emojis, span_display_name_boost, holder.binding.statusBoosterDisplayName, status.id, id -> { - if (!statusToDeal.account.emojiFetched) { - statusToDeal.account.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - holder.binding.statusBoosterDisplayName.setText(span_display_name_boost, TextView.BufferType.SPANNABLE); + + holder.binding.statusBoosterDisplayName.setText( + status.account.getSpanDisplayName(context, + new WeakReference<>(holder.binding.statusBoosterDisplayName), + id -> holder.binding.statusBoosterDisplayName.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); + holder.binding.statusBoosterInfo.setVisibility(View.VISIBLE); holder.binding.boosterDivider.setVisibility(View.VISIBLE); if (theme_text_header_1_line != -1) { @@ -913,18 +888,15 @@ public class StatusAdapter extends RecyclerView.Adapter break; } //--- MAIN CONTENT --- - - CustomEmoji.displayEmoji(context, statusToDeal.emojis, statusToDeal.span_content, holder.binding.statusContent, status.id, id -> { - if (!status.emojiFetched) { - status.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - - holder.binding.statusContent.setText(statusToDeal.span_content, TextView.BufferType.SPANNABLE); + holder.binding.statusContent.setText( + statusToDeal.getSpanContent(context, + new WeakReference<>(holder.binding.statusContent), + id -> holder.binding.statusContent.post(() -> { + Log.v(Helper.TAG, "notifiy: " + id); + Log.v(Helper.TAG, "position: " + getPositionAsync(notificationList, statusList, id)); + adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)); + })), + TextView.BufferType.SPANNABLE); if (truncate_toots_size > 0) { holder.binding.statusContent.setMaxLines(truncate_toots_size); holder.binding.statusContent.setEllipsize(TextUtils.TruncateAt.END); @@ -955,16 +927,11 @@ public class StatusAdapter extends RecyclerView.Adapter } if (statusToDeal.translationContent != null) { holder.binding.containerTrans.setVisibility(View.VISIBLE); - CustomEmoji.displayEmoji(context, statusToDeal.emojis, statusToDeal.span_translate, holder.binding.statusContentTranslated, status.id, id -> { - if (!statusToDeal.emojiFetched) { - statusToDeal.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - holder.binding.statusContentTranslated.setText(statusToDeal.span_translate, TextView.BufferType.SPANNABLE); + holder.binding.statusContentTranslated.setText( + statusToDeal.getSpanTranslate(context, + new WeakReference<>(holder.binding.statusContentTranslated), + id -> holder.binding.statusContentTranslated.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); } else { holder.binding.containerTrans.setVisibility(View.GONE); } @@ -1226,16 +1193,11 @@ public class StatusAdapter extends RecyclerView.Adapter pollItemBinding.pollItemPercent.setTextColor(theme_text_color); pollItemBinding.pollItemText.setTextColor(theme_text_color); } - CustomEmoji.displayEmoji(context, statusToDeal.emojis, pollItem.span_title, pollItemBinding.pollItemText, status.id, id -> { - if (!statusToDeal.emojiFetched) { - statusToDeal.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - pollItemBinding.pollItemText.setText(pollItem.span_title, TextView.BufferType.SPANNABLE); + pollItemBinding.pollItemText.setText( + pollItem.getSpanTitle(context, statusToDeal, + new WeakReference<>(pollItemBinding.pollItemText), + id -> pollItemBinding.pollItemText.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); pollItemBinding.pollItemValue.setProgress((int) value); if (pollItem.votes_count == greaterValue) { pollItemBinding.pollItemPercent.setTypeface(null, Typeface.BOLD); @@ -1263,16 +1225,11 @@ public class StatusAdapter extends RecyclerView.Adapter for (Poll.PollItem pollOption : statusToDeal.poll.options) { CheckBox cb = new CheckBox(context); cb.setButtonTintList(ThemeHelper.getButtonColorStateList(context)); - CustomEmoji.displayEmoji(context, statusToDeal.emojis, pollOption.span_title, cb, status.id, id -> { - if (!statusToDeal.emojiFetched) { - statusToDeal.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - cb.setText(pollOption.span_title, TextView.BufferType.SPANNABLE); + cb.setText( + pollOption.getSpanTitle(context, statusToDeal, + new WeakReference<>(cb), + id -> cb.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); holder.binding.poll.multipleChoice.addView(cb); } holder.binding.poll.multipleChoice.setVisibility(View.VISIBLE); @@ -1283,25 +1240,12 @@ public class StatusAdapter extends RecyclerView.Adapter for (Poll.PollItem pollOption : statusToDeal.poll.options) { RadioButton rb = new RadioButton(context); rb.setButtonTintList(ThemeHelper.getButtonColorStateList(context)); - CustomEmoji.displayEmoji(context, statusToDeal.account.emojis, pollOption.span_title, rb, status.id, id -> { - if (!statusToDeal.account.emojiFetched) { - statusToDeal.account.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - CustomEmoji.displayEmoji(context, statusToDeal.emojis, pollOption.span_title, rb, status.id, id -> { - if (!statusToDeal.emojiFetched) { - statusToDeal.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - rb.setText(pollOption.span_title, TextView.BufferType.SPANNABLE); + rb.setText( + pollOption.getSpanTitle(context, statusToDeal, + new WeakReference<>(rb), + id -> rb.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); + holder.binding.poll.singleChoiceRadioGroup.addView(rb); } holder.binding.poll.singleChoiceRadioGroup.setVisibility(View.VISIBLE); @@ -1626,7 +1570,6 @@ public class StatusAdapter extends RecyclerView.Adapter if (translate.getTranslatedContent() != null) { statusToDeal.translationShown = true; statusToDeal.translationContent = translate.getTranslatedContent(); - SpannableHelper.convertStatus(context.getApplicationContext(), statusToDeal); adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal)); } else { Toasty.error(context, context.getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show(); @@ -1901,16 +1844,11 @@ public class StatusAdapter extends RecyclerView.Adapter .load(status.art_attachment.preview_url) .apply(new RequestOptions().transform(new RoundedCorners((int) Helper.convertDpToPixel(3, context)))) .into(holder.bindingArt.artMedia); - CustomEmoji.displayEmoji(context, status.emojis, status.account.span_display_name, holder.bindingArt.artAcct, status.id, id -> { - if (!status.emojiFetched) { - status.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - notifyItemChanged(getPositionAsync(null, statusList, id)); - } - }); - holder.bindingArt.artAcct.setText(status.account.span_display_name, TextView.BufferType.SPANNABLE); + holder.bindingArt.artAcct.setText( + status.account.getSpanDisplayName(context, + new WeakReference<>(holder.bindingArt.artAcct), + id -> holder.bindingArt.artAcct.post(() -> notifyItemChanged(getPositionAsync(null, statusList, id)))), + TextView.BufferType.SPANNABLE); holder.bindingArt.artUsername.setText(String.format(Locale.getDefault(), "@%s", status.account.acct)); holder.bindingArt.artPp.setOnClickListener(v -> { Intent intent = new Intent(context, ProfileActivity.class); diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java index e4be47a13..1ce3b38d4 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java @@ -43,7 +43,6 @@ import app.fedilab.android.client.entities.app.Timeline; import app.fedilab.android.databinding.FragmentPaginationBinding; import app.fedilab.android.helper.DividerDecoration; import app.fedilab.android.helper.Helper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.ui.drawer.StatusAdapter; import app.fedilab.android.viewmodel.mastodon.StatusesVM; @@ -96,11 +95,10 @@ public class FragmentMastodonContext extends Fragment { } } else if (statusPosted != null && statusAdapter != null) { if (requireActivity() instanceof ContextActivity) { - Status convertStatus = SpannableHelper.convertStatus(context, statusPosted); int i = 0; for (Status status : statuses) { - if (status.id.equals(convertStatus.in_reply_to_id)) { - statuses.add((i + 1), convertStatus); + if (status.id.equals(statusPosted.in_reply_to_id)) { + statuses.add((i + 1), statusPosted); statusAdapter.notifyItemInserted((i + 1)); if (requireActivity() instanceof ContextActivity) { //Redraw decorations diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java index d62864015..23c3bf3cf 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java @@ -56,7 +56,6 @@ import app.fedilab.android.client.entities.app.Timeline; import app.fedilab.android.databinding.FragmentPaginationBinding; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.ui.drawer.StatusAdapter; import app.fedilab.android.viewmodel.mastodon.AccountsVM; @@ -118,8 +117,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. statusAdapter.notifyItemRemoved(position); } } else if (statusPosted != null && statusAdapter != null && timelineType == Timeline.TimeLineEnum.HOME) { - Status convertStatus = SpannableHelper.convertStatus(context, statusPosted); - statuses.add(0, convertStatus); + statuses.add(0, statusPosted); statusAdapter.notifyItemInserted(0); } } diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AccountsVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AccountsVM.java index 5b8cf38ec..0c9868ba4 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AccountsVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AccountsVM.java @@ -52,7 +52,6 @@ import app.fedilab.android.client.entities.api.Tag; import app.fedilab.android.client.entities.api.Token; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import retrofit2.Call; @@ -306,9 +305,6 @@ public class AccountsVM extends AndroidViewModel { } } Account finalAccount = account; - if (finalAccount != null) { - SpannableHelper.convertAccount(getApplication().getApplicationContext(), finalAccount); - } Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> accountMutableLiveData.setValue(finalAccount); mainHandler.post(myRunnable); @@ -342,7 +338,7 @@ public class AccountsVM extends AndroidViewModel { try { Response> accountStatusesResponse = accountStatusesCall.execute(); if (accountStatusesResponse.isSuccessful()) { - statusList = SpannableHelper.convertStatus(getApplication().getApplicationContext(), accountStatusesResponse.body()); + statusList = accountStatusesResponse.body(); pagination = MastodonHelper.getPagination(accountStatusesResponse.headers()); } @@ -378,7 +374,7 @@ public class AccountsVM extends AndroidViewModel { try { Response> followersResponse = followersCall.execute(); if (followersResponse.isSuccessful()) { - accountList = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), followersResponse.body()); + accountList = followersResponse.body(); pagination = MastodonHelper.getPagination(followersResponse.headers()); } } catch (Exception e) { @@ -414,7 +410,7 @@ public class AccountsVM extends AndroidViewModel { try { Response> followingResponse = followingCall.execute(); if (followingResponse.isSuccessful()) { - accountList = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), followingResponse.body()); + accountList = followingResponse.body(); pagination = MastodonHelper.getPagination(followingResponse.headers()); } } catch (Exception e) { @@ -883,11 +879,6 @@ public class AccountsVM extends AndroidViewModel { } } List finalAccountList = accountList; - if (finalAccountList != null) { - for (Account account : finalAccountList) { - SpannableHelper.convertAccount(getApplication().getApplicationContext(), account); - } - } Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> accountListMutableLiveData.setValue(finalAccountList); mainHandler.post(myRunnable); @@ -912,7 +903,7 @@ public class AccountsVM extends AndroidViewModel { Response> bookmarksResponse = bookmarksCall.execute(); if (bookmarksResponse.isSuccessful()) { statusList = bookmarksResponse.body(); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusList); + statuses.statuses = statusList; statuses.pagination = MastodonHelper.getPagination(bookmarksResponse.headers()); } } catch (Exception e) { @@ -943,7 +934,7 @@ public class AccountsVM extends AndroidViewModel { Response> favouritesResponse = favouritesCall.execute(); if (favouritesResponse.isSuccessful()) { statusList = favouritesResponse.body(); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusList); + statuses.statuses = statusList; statuses.pagination = MastodonHelper.getPagination(favouritesResponse.headers()); } } catch (Exception e) { @@ -975,7 +966,7 @@ public class AccountsVM extends AndroidViewModel { Response> mutesResponse = mutesCall.execute(); if (mutesResponse.isSuccessful()) { accountList = mutesResponse.body(); - accounts.accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountList); + accounts.accounts = accountList; accounts.pagination = MastodonHelper.getPagination(mutesResponse.headers()); } } catch (Exception e) { @@ -1007,7 +998,7 @@ public class AccountsVM extends AndroidViewModel { Response> blocksResponse = blocksCall.execute(); if (blocksResponse.isSuccessful()) { accountList = blocksResponse.body(); - accounts.accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountList); + accounts.accounts = accountList; accounts.pagination = MastodonHelper.getPagination(blocksResponse.headers()); } } catch (Exception e) { @@ -1303,7 +1294,7 @@ public class AccountsVM extends AndroidViewModel { Response> followRequestsResponse = followRequestsCall.execute(); if (followRequestsResponse.isSuccessful()) { accountList = followRequestsResponse.body(); - accounts.accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountList); + accounts.accounts = accountList; accounts.pagination = MastodonHelper.getPagination(followRequestsResponse.headers()); } } catch (Exception e) { diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AnnouncementsVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AnnouncementsVM.java index afca9d266..743cd32a8 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AnnouncementsVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AnnouncementsVM.java @@ -32,7 +32,6 @@ import java.util.concurrent.TimeUnit; import app.fedilab.android.client.endpoints.MastodonAnnouncementsService; import app.fedilab.android.client.entities.api.Announcement; import app.fedilab.android.helper.Helper; -import app.fedilab.android.helper.SpannableHelper; import okhttp3.OkHttpClient; import retrofit2.Call; import retrofit2.Response; @@ -83,7 +82,6 @@ public class AnnouncementsVM extends AndroidViewModel { Response> getAnnouncementsResponse = getAnnouncementsCall.execute(); if (getAnnouncementsResponse.isSuccessful()) { announcementList = getAnnouncementsResponse.body(); - SpannableHelper.convertAnnouncement(getApplication(), announcementList); } } catch (Exception e) { e.printStackTrace(); diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/NotificationsVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/NotificationsVM.java index eccded41a..30faf9f66 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/NotificationsVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/NotificationsVM.java @@ -35,7 +35,6 @@ import app.fedilab.android.client.entities.api.Notifications; import app.fedilab.android.client.entities.api.PushSubscription; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.TimelineHelper; import okhttp3.OkHttpClient; import retrofit2.Call; @@ -103,15 +102,6 @@ public class NotificationsVM extends AndroidViewModel { if (notificationsResponse.isSuccessful()) { List notFilteredNotifications = notificationsResponse.body(); notifications.notifications = TimelineHelper.filterNotification(getApplication().getApplicationContext(), notFilteredNotifications); - if (notifications.notifications != null) { - for (Notification notification : notifications.notifications) { - if (notification != null) { - if (notification.status != null) { - notification.status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), notification.status); - } - } - } - } notifications.pagination = MastodonHelper.getPagination(notificationsResponse.headers()); } } catch (Exception e) { @@ -147,9 +137,6 @@ public class NotificationsVM extends AndroidViewModel { Response notificationResponse = notificationCall.execute(); if (notificationResponse.isSuccessful()) { notification = notificationResponse.body(); - if (notification != null) { - notification.status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), notification.status); - } } } catch (Exception e) { e.printStackTrace(); diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/SearchVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/SearchVM.java index b4d5f0e07..bbc928aa9 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/SearchVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/SearchVM.java @@ -36,7 +36,6 @@ import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.app.StatusCache; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.Helper; -import app.fedilab.android.helper.SpannableHelper; import okhttp3.OkHttpClient; import retrofit2.Call; import retrofit2.Response; @@ -112,13 +111,9 @@ public class SearchVM extends AndroidViewModel { if (results != null) { if (results.statuses == null) { results.statuses = new ArrayList<>(); - } else { - results.statuses = SpannableHelper.convertStatus(getApplication(), results.statuses); } if (results.accounts == null) { results.accounts = new ArrayList<>(); - } else { - results.accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), results.accounts); } if (results.hashtags == null) { results.hashtags = new ArrayList<>(); @@ -144,7 +139,6 @@ public class SearchVM extends AndroidViewModel { try { results.statuses = new ArrayList<>(); List statuses = new StatusCache(getApplication()).searchStatus(StatusCache.CacheEnum.HOME, instance, userId, q); - statuses = SpannableHelper.convertStatus(getApplication(), statuses); results.statuses.addAll(statuses); } catch (DBException e) { e.printStackTrace(); diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/StatusesVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/StatusesVM.java index c97c2daf0..6f4bf1594 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/StatusesVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/StatusesVM.java @@ -47,7 +47,6 @@ import app.fedilab.android.client.entities.app.StatusCache; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.TimelineHelper; import okhttp3.Headers; import okhttp3.MultipartBody; @@ -279,7 +278,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } } catch (Exception e) { e.printStackTrace(); @@ -356,13 +355,7 @@ public class StatusesVM extends AndroidViewModel { context = contextResponse.body(); if (context != null) { TimelineHelper.filterStatus(getApplication().getApplicationContext(), context.descendants, TimelineHelper.FilterTimeLineType.CONTEXT); - for (Status status : context.descendants) { - SpannableHelper.convertStatus(getApplication().getApplicationContext(), status); - } TimelineHelper.filterStatus(getApplication().getApplicationContext(), context.ancestors, TimelineHelper.FilterTimeLineType.CONTEXT); - for (Status status : context.ancestors) { - SpannableHelper.convertStatus(getApplication().getApplicationContext(), status); - } } } @@ -406,7 +399,7 @@ public class StatusesVM extends AndroidViewModel { try { Response> accountsResponse = accountsCall.execute(); if (accountsResponse.isSuccessful()) { - accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountsResponse.body()); + accounts = accountsResponse.body(); } headers = accountsResponse.headers(); } catch (Exception e) { @@ -451,7 +444,7 @@ public class StatusesVM extends AndroidViewModel { try { Response> accountsResponse = accountsCall.execute(); if (accountsResponse.isSuccessful()) { - accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountsResponse.body()); + accounts = accountsResponse.body(); } headers = accountsResponse.headers(); } catch (Exception e) { @@ -489,7 +482,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -533,7 +526,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -579,7 +572,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -623,7 +616,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -667,7 +660,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -711,7 +704,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -755,7 +748,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -799,7 +792,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -843,7 +836,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -887,7 +880,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java index 47defbdf5..bfd0ace12 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java @@ -48,7 +48,6 @@ import app.fedilab.android.client.entities.peertube.PeertubeVideo; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.TimelineHelper; import okhttp3.OkHttpClient; import retrofit2.Call; @@ -121,7 +120,7 @@ public class TimelinesVM extends AndroidViewModel { Response> publicTlResponse = publicTlCall.execute(); if (publicTlResponse.isSuccessful()) { statusList = publicTlResponse.body(); - statusList = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusList); + statusList = statusList; } } catch (Exception e) { e.printStackTrace(); @@ -190,8 +189,7 @@ public class TimelinesVM extends AndroidViewModel { Response> publicTlResponse = publicTlCall.execute(); if (publicTlResponse.isSuccessful()) { List notFilteredStatuses = publicTlResponse.body(); - List filteredStatuses = TimelineHelper.filterStatus(getApplication(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses); + statuses.statuses = TimelineHelper.filterStatus(getApplication(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC); statuses.pagination = MastodonHelper.getPagination(publicTlResponse.headers()); } } catch (Exception e) { @@ -232,7 +230,7 @@ public class TimelinesVM extends AndroidViewModel { statusList.add(status); } } - statuses.statuses = SpannableHelper.convertNitterStatus(statusList); + statuses.statuses = statusList; String max_id = publicTlResponse.headers().get("min-id"); statuses.pagination = new Pagination(); statuses.pagination.max_id = max_id; @@ -278,8 +276,7 @@ public class TimelinesVM extends AndroidViewModel { statusList.add(status); } } - List filteredStatuses = TimelineHelper.filterStatus(getApplication(), statusList, TimelineHelper.FilterTimeLineType.PUBLIC); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses); + statuses.statuses = TimelineHelper.filterStatus(getApplication(), statusList, TimelineHelper.FilterTimeLineType.PUBLIC); statuses.pagination = new Pagination(); if (statusList.size() > 0) { statuses.pagination.min_id = statusList.get(0).id; @@ -325,8 +322,7 @@ public class TimelinesVM extends AndroidViewModel { statusList.add(status); } } - List filteredStatuses = TimelineHelper.filterStatus(getApplication(), statusList, TimelineHelper.FilterTimeLineType.PUBLIC); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses); + statuses.statuses = TimelineHelper.filterStatus(getApplication(), statusList, TimelineHelper.FilterTimeLineType.PUBLIC); statuses.pagination = new Pagination(); if (statusList.size() > 0) { //These values are not used. @@ -407,8 +403,7 @@ public class TimelinesVM extends AndroidViewModel { Response> hashTagTlResponse = hashTagTlCall.execute(); if (hashTagTlResponse.isSuccessful()) { List notFilteredStatuses = hashTagTlResponse.body(); - List filteredStatuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses); + statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC); statuses.pagination = MastodonHelper.getPagination(hashTagTlResponse.headers()); } } catch (Exception e) { @@ -450,8 +445,7 @@ public class TimelinesVM extends AndroidViewModel { Response> homeTlResponse = homeTlCall.execute(); if (homeTlResponse.isSuccessful()) { List notFilteredStatuses = homeTlResponse.body(); - List filteredStatuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.HOME); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses); + statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.HOME); statuses.pagination = MastodonHelper.getPagination(homeTlResponse.headers()); if (!fetchingMissing) { for (Status status : statuses.statuses) { @@ -503,8 +497,7 @@ public class TimelinesVM extends AndroidViewModel { statuses = statusCacheDAO.geStatuses(StatusCache.CacheEnum.HOME, instance, user_id, maxId, minId, sinceId); if (statuses != null) { - List filteredStatuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), statuses.statuses, TimelineHelper.FilterTimeLineType.HOME); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses); + statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), statuses.statuses, TimelineHelper.FilterTimeLineType.HOME); if (statuses.statuses != null && statuses.statuses.size() > 0) { statuses.pagination = new Pagination(); statuses.pagination.min_id = statuses.statuses.get(0).id; @@ -571,7 +564,7 @@ public class TimelinesVM extends AndroidViewModel { try { Response> listTlResponse = listTlCall.execute(); if (listTlResponse.isSuccessful()) { - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), listTlResponse.body()); + statuses.statuses = listTlResponse.body(); statuses.pagination = MastodonHelper.getPagination(listTlResponse.headers()); } } catch (Exception e) { @@ -607,11 +600,6 @@ public class TimelinesVM extends AndroidViewModel { Response> conversationsResponse = conversationsCall.execute(); if (conversationsResponse.isSuccessful()) { conversations.conversations = conversationsResponse.body(); - if (conversations.conversations != null) { - for (Conversation conversation : conversations.conversations) { - conversation.last_status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), conversation.last_status); - } - } conversations.pagination = MastodonHelper.getPagination(conversationsResponse.headers()); } } catch (Exception e) {