From ca9c47edeac5624ddd9100a5308d0de411a5d349 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 24 Jul 2025 15:52:24 +0200 Subject: [PATCH] Add Mastodon Quote support in TL --- .../mastodon/client/entities/api/Status.java | 44 +++++++++++++++++-- .../mastodon/ui/drawer/StatusAdapter.java | 20 ++++----- .../viewmodel/mastodon/TimelinesVM.java | 2 +- .../metadata/android/en/changelogs/535.txt | 9 ++++ 4 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 src/fdroid/fastlane/metadata/android/en/changelogs/535.txt diff --git a/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Status.java b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Status.java index 1ff67480..78c221a1 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Status.java +++ b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Status.java @@ -16,12 +16,15 @@ package app.fedilab.android.mastodon.client.entities.api; import android.content.Context; import android.text.Spannable; -import android.util.Log; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; import java.io.Serializable; @@ -29,7 +32,6 @@ import java.lang.ref.WeakReference; import java.util.Date; import java.util.List; -import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.SpannableHelper; import de.timfreiheit.mathjax.android.MathJaxView; @@ -84,7 +86,7 @@ public class Status implements Serializable, Cloneable { @SerializedName("reblog") public Status reblog; @SerializedName("quote") - public Status quote; + private Object quote; @SerializedName("application") public App application; @SerializedName("account") @@ -114,6 +116,35 @@ public class Status implements Serializable, Cloneable { @SerializedName("reactions") public List reactions; + public Status getQuote() { + Status quote = null; + ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); + String json = String.valueOf(this.quote); + try { + json = ow.writeValueAsString(this.quote); + } catch (JsonProcessingException ignored) { + } + Gson gson = new Gson(); + try{ + quote = gson.fromJson(json, Status.class); + if(quote.account == null) { + MastodonQuote mastodonQuote = gson.fromJson(json, MastodonQuote.class); + if(mastodonQuote.quoted_status != null && (mastodonQuote.state != null && mastodonQuote.state.equals("accepted"))) { + quote = mastodonQuote.quoted_status; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + if(quote !=null && quote.account != null) { + return quote; + } + return null; + } + + public void setQuote(Status quote) { + this.quote =quote; + } public String attachedNotification = null; public int gifPosition = 0; @@ -202,4 +233,11 @@ public class Status implements Serializable, Cloneable { void emojiFetched(); } + + private static class MastodonQuote implements Serializable { + @SerializedName("state") + String state; + @SerializedName("quoted_status") + Status quoted_status; + } } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java index 3bcd2120..18550028 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java @@ -571,7 +571,7 @@ public class StatusAdapter extends RecyclerView.Adapter } else { holder.binding.pronouns.setVisibility(View.GONE); } - if (statusToDeal.quote != null && (statusToDeal.spoiler_text == null || statusToDeal.spoiler_text.trim().isEmpty() || statusToDeal.isExpended)) { + if (statusToDeal.getQuote() != null && (statusToDeal.spoiler_text == null || statusToDeal.spoiler_text.trim().isEmpty() || statusToDeal.isExpended)) { holder.binding.quotedMessage.cardviewContainer.setCardElevation((int) Helper.convertDpToPixel(5, context)); holder.binding.quotedMessage.dividerCard.setVisibility(View.GONE); holder.binding.quotedMessage.cardviewContainer.setStrokeWidth((int) Helper.convertDpToPixel(1, context)); @@ -595,7 +595,7 @@ public class StatusAdapter extends RecyclerView.Adapter if (!remote) { Intent intent = new Intent(context, ContextActivity.class); Bundle args = new Bundle(); - args.putSerializable(Helper.ARG_STATUS, statusToDeal.quote); + args.putSerializable(Helper.ARG_STATUS, statusToDeal.getQuote()); new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> { Bundle bundle = new Bundle(); bundle.putLong(Helper.ARG_INTENT_ID, bundleId); @@ -603,27 +603,27 @@ public class StatusAdapter extends RecyclerView.Adapter context.startActivity(intent); }); } else { - Helper.openBrowser(context,statusToDeal.quote.url); + Helper.openBrowser(context,statusToDeal.getQuote().url); } }); holder.binding.quotedMessage.cardviewContainer.setStrokeColor(ThemeHelper.getAttColor(context, R.attr.colorPrimary)); holder.binding.quotedMessage.statusContent.setText( - statusToDeal.quote.getSpanContent(context, remote, + statusToDeal.getQuote().getSpanContent(context, remote, new WeakReference<>(holder.binding.quotedMessage.statusContent), null), TextView.BufferType.SPANNABLE); - MastodonHelper.loadPPMastodon(holder.binding.quotedMessage.avatar, statusToDeal.quote.account); - if (statusToDeal.quote.account != null) { + MastodonHelper.loadPPMastodon(holder.binding.quotedMessage.avatar, statusToDeal.getQuote().account); + if (statusToDeal.getQuote().account != null) { holder.binding.quotedMessage.displayName.setText( - statusToDeal.quote.account.getSpanDisplayName(context, + statusToDeal.getQuote().account.getSpanDisplayName(context, new WeakReference<>(holder.binding.quotedMessage.displayName)), TextView.BufferType.SPANNABLE); - holder.binding.quotedMessage.username.setText(String.format("@%s", statusToDeal.quote.account.acct)); + holder.binding.quotedMessage.username.setText(String.format("@%s", statusToDeal.getQuote().account.acct)); } - if (statusToDeal.quote.spoiler_text != null && !statusToDeal.quote.spoiler_text.trim().isEmpty()) { + if (statusToDeal.getQuote().spoiler_text != null && !statusToDeal.getQuote().spoiler_text.trim().isEmpty()) { holder.binding.quotedMessage.spoiler.setVisibility(View.VISIBLE); holder.binding.quotedMessage.spoiler.setText( - statusToDeal.quote.getSpanSpoiler(context, + statusToDeal.getQuote().getSpanSpoiler(context, new WeakReference<>(holder.binding.quotedMessage.spoiler), null), TextView.BufferType.SPANNABLE); } else { diff --git a/app/src/main/java/app/fedilab/android/mastodon/viewmodel/mastodon/TimelinesVM.java b/app/src/main/java/app/fedilab/android/mastodon/viewmodel/mastodon/TimelinesVM.java index ac4a446e..92c5a351 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/viewmodel/mastodon/TimelinesVM.java +++ b/app/src/main/java/app/fedilab/android/mastodon/viewmodel/mastodon/TimelinesVM.java @@ -381,7 +381,7 @@ public class TimelinesVM extends AndroidViewModel { //Quoted message if(!timelineItem.select(".quote").html().isEmpty()) { - status.quote = Nitter.nitterHTMLParser(context, timelineItem.select(".quote").first(), nitterInstance); + status.setQuote(Nitter.nitterHTMLParser(context, timelineItem.select(".quote").first(), nitterInstance)); } Status finalStatus; diff --git a/src/fdroid/fastlane/metadata/android/en/changelogs/535.txt b/src/fdroid/fastlane/metadata/android/en/changelogs/535.txt new file mode 100644 index 00000000..ec0e1e83 --- /dev/null +++ b/src/fdroid/fastlane/metadata/android/en/changelogs/535.txt @@ -0,0 +1,9 @@ +Added: + + +Changed: + + +Fixed: +- Fix a crash with threads +- Fix empty Hashtags \ No newline at end of file