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 94a7d08f..1ed93997 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,6 +16,7 @@ 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; @@ -28,6 +29,7 @@ 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; @@ -139,6 +141,8 @@ public class Status implements Serializable, Cloneable { public boolean spoilerChecked = false; public Filter filteredByApp; public transient Spannable contentSpan; + + public transient String[] bottomTags; public transient Spannable contentSpoilerSpan; public transient Spannable contentTranslateSpan; public transient MathJaxView mathJaxView; @@ -162,6 +166,13 @@ public class Status implements Serializable, Cloneable { return contentSpan; } + public synchronized String[] getBottomTags() { + if(bottomTags == null) { + bottomTags = SpannableHelper.hasBottomTags(content); + } + return bottomTags; + } + public synchronized Spannable getSpanSpoiler(Context context, WeakReference viewWeakReference, Callback callback) { if (contentSpoilerSpan == null) { contentSpoilerSpan = SpannableHelper.convert(context, spoiler_text, this, null, null, viewWeakReference, callback, true, false); diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/SpannableHelper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/SpannableHelper.java index 0a3f34e3..246c16c4 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/SpannableHelper.java @@ -37,6 +37,7 @@ import android.text.TextPaint; import android.text.style.ClickableSpan; import android.text.style.QuoteSpan; import android.text.style.URLSpan; +import android.util.Log; import android.util.Patterns; import android.view.LayoutInflater; import android.view.View; @@ -107,6 +108,28 @@ public class SpannableHelper { private static int linkColor; private static boolean underlineLinks; + private static final String patternBottomTags = "\\n{2,}((#[\\w_À-ú-]+)\\s?)+$"; + + public static String[] hasBottomTags(String text) { + if (text == null) { + return new String[]{}; + } + SpannableString initialContent; + 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)); + } + final Pattern bottomTagsPattern = Pattern.compile(patternBottomTags, Pattern.CASE_INSENSITIVE); + Matcher matcherBottomTags = bottomTagsPattern.matcher(initialContent); + String[] tags = new String[]{}; + while (matcherBottomTags.find()) { + String stringTags = Objects.requireNonNull(matcherBottomTags.group()).trim(); + tags = stringTags.split("\\s"); + } + return tags; + } + public static Spannable convert(Context context, String text, Status status, Account account, Announcement announcement, WeakReference viewWeakReference, Status.Callback callback, boolean convertHtml, boolean convertMarkdown) { @@ -162,6 +185,7 @@ public class SpannableHelper { } else { initialContent = new SpannableString(text); } + boolean markdownSupport = sharedpreferences.getBoolean(context.getString(R.string.SET_MARKDOWN_SUPPORT), false); //Get all links SpannableStringBuilder content; @@ -393,6 +417,20 @@ public class SpannableHelper { } } + } + + boolean underlineBottomHashTags = sharedpreferences.getBoolean(context.getString(R.string.SET_UNDERLINE_BOTTOM_HASHTAGS), true); + if(underlineBottomHashTags) { + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); + final Pattern bottomTagsPattern = Pattern.compile(patternBottomTags, Pattern.CASE_INSENSITIVE); + Matcher matcherBottomTags = bottomTagsPattern.matcher(content); + int length = 0; + while (matcherBottomTags.find()) { + length = Objects.requireNonNull(matcherBottomTags.group()).length(); + } + spannableStringBuilder.append(content,0, content.length()-length); + return trimSpannable(spannableStringBuilder); + } return trimSpannable(new SpannableStringBuilder(content)); } 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 eab59d9b..bb2bbd31 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 @@ -108,6 +108,7 @@ import com.bumptech.glide.ListPreloader; import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.request.RequestOptions; import com.github.stom79.mytransl.MyTransL; +import com.google.android.material.chip.Chip; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.smarteist.autoimageslider.SliderAnimations; import com.smarteist.autoimageslider.SliderView; @@ -143,6 +144,7 @@ import app.fedilab.android.databinding.LayoutPollItemBinding; import app.fedilab.android.mastodon.activities.ComposeActivity; import app.fedilab.android.mastodon.activities.ContextActivity; import app.fedilab.android.mastodon.activities.CustomSharingActivity; +import app.fedilab.android.mastodon.activities.HashTagActivity; import app.fedilab.android.mastodon.activities.MediaActivity; import app.fedilab.android.mastodon.activities.ProfileActivity; import app.fedilab.android.mastodon.activities.ReportActivity; @@ -508,6 +510,9 @@ public class StatusAdapter extends RecyclerView.Adapter LinearLayoutCompat.MarginLayoutParams psc = (LinearLayoutCompat.MarginLayoutParams) holder.binding.statusContent.getLayoutParams(); psc.setMarginStart((int) Helper.convertDpToPixel(6, context)); holder.binding.statusContent.setLayoutParams(psc); + LinearLayoutCompat.MarginLayoutParams pst = (LinearLayoutCompat.MarginLayoutParams) holder.binding.statusHashtags.getLayoutParams(); + pst.setMarginStart((int) Helper.convertDpToPixel(6, context)); + holder.binding.statusHashtags.setLayoutParams(pst); LinearLayoutCompat.MarginLayoutParams psq = (LinearLayoutCompat.MarginLayoutParams) holder.binding.quotedMessage.cardviewContainer.getLayoutParams(); psq.setMarginStart((int) Helper.convertDpToPixel(6, context)); holder.binding.quotedMessage.cardviewContainer.setLayoutParams(psq); @@ -1510,6 +1515,37 @@ public class StatusAdapter extends RecyclerView.Adapter recyclerView.post(() -> adapter.notifyItemChanged(holder.getBindingAdapterPosition())); }), TextView.BufferType.SPANNABLE); + boolean underlineBottomHashTags = sharedpreferences.getBoolean(context.getString(R.string.SET_UNDERLINE_BOTTOM_HASHTAGS), true); + if(underlineBottomHashTags) { + if (statusToDeal.getBottomTags().length > 0) { + holder.binding.statusHashtags.setVisibility(View.VISIBLE); + holder.binding.statusHashtags.removeAllViews(); + for (String tag : statusToDeal.getBottomTags()) { + Chip chip = new Chip(context); + chip.setClickable(true); + chip.setEnsureMinTouchTargetSize(false); + chip.setText(tag); + chip.setTextColor(ThemeHelper.getAttColor(context, R.attr.colorPrimary)); + chip.setOnClickListener(v -> { + Intent intentTag = new Intent(context, HashTagActivity.class); + Bundle args = new Bundle(); + args.putString(Helper.ARG_SEARCH_KEYWORD, tag.replace("#", "")); + new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> { + Bundle bundle = new Bundle(); + bundle.putLong(Helper.ARG_INTENT_ID, bundleId); + intentTag.putExtras(bundle); + intentTag.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intentTag); + }); + }); + holder.binding.statusHashtags.addView(chip); + } + } else { + holder.binding.statusHashtags.setVisibility(View.GONE); + } + } else { + holder.binding.statusHashtags.setVisibility(View.GONE); + } if (truncate_toots_size > 0) { holder.binding.statusContent.setMaxLines(truncate_toots_size); holder.binding.statusContent.setEllipsize(TextUtils.TruncateAt.END); diff --git a/app/src/main/res/layouts/mastodon/layout/drawer_status.xml b/app/src/main/res/layouts/mastodon/layout/drawer_status.xml index 17b87568..cec3fca7 100644 --- a/app/src/main/res/layouts/mastodon/layout/drawer_status.xml +++ b/app/src/main/res/layouts/mastodon/layout/drawer_status.xml @@ -281,6 +281,18 @@ tools:maxLines="10" tools:text="@tools:sample/lorem/random" /> + + SET_TIMELINE_SCROLLBAR SET_MARKDOWN_SUPPORT SET_TRUNCATE_LINKS + SET_UNDERLINE_BOTTOM_HASHTAGS SET_UNDERLINE_CLICKABLE SET_PRONOUNS_SUPPORT SET_TRUNCATE_LINKS_MAX @@ -2081,6 +2082,7 @@ There are missing media descriptions Truncate links + Highlight bottom hashtags Underline clickable elements Max chars in links diff --git a/app/src/main/res/xml/pref_timelines.xml b/app/src/main/res/xml/pref_timelines.xml index 9816e10a..bf5e87b1 100644 --- a/app/src/main/res/xml/pref_timelines.xml +++ b/app/src/main/res/xml/pref_timelines.xml @@ -72,6 +72,14 @@ app:singleLineTitle="false" app:title="@string/truncate_links" /> + + +