diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/TestActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/TestActivity.java index 1ac4c05c..28ba7c77 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/TestActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/TestActivity.java @@ -2,8 +2,7 @@ package gr.thmmy.mthmmy.activities; import androidx.appcompat.app.AppCompatActivity; import gr.thmmy.mthmmy.R; -import gr.thmmy.mthmmy.utils.parsing.BBParser; -import timber.log.Timber; +import gr.thmmy.mthmmy.utils.parsing.ThmmyParser; import android.os.Bundle; import android.text.SpannableStringBuilder; @@ -17,7 +16,7 @@ public class TestActivity extends AppCompatActivity { setContentView(R.layout.activity_test); String bb = "[b]An [i]elep[u]hant[/i][/b] swi[/u]ms in [s]the[/s] tree"; - SpannableStringBuilder result = BBParser.bb2span(bb); + SpannableStringBuilder result = ThmmyParser.bb2span(bb); TextView bbRaw = findViewById(R.id.bb_raw); TextView bb2Text = findViewById(R.id.bb2text); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java index 1c32ce2d..44658316 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java @@ -66,6 +66,7 @@ import gr.thmmy.mthmmy.model.ThmmyFile; import gr.thmmy.mthmmy.model.ThmmyPage; import gr.thmmy.mthmmy.model.TopicItem; import gr.thmmy.mthmmy.utils.CircleTransform; +import gr.thmmy.mthmmy.utils.parsing.ThmmyParser; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import gr.thmmy.mthmmy.viewmodel.TopicViewModel; import timber.log.Timber; @@ -166,9 +167,34 @@ class TopicAdapter extends RecyclerView.Adapter { Poll poll = (Poll) topicItems.get(position); Poll.Entry[] entries = poll.getEntries(); PollViewHolder holder = (PollViewHolder) currentHolder; + + boolean pollSupported = true; + for (Poll.Entry entry : entries) { + if (ThmmyParser.containsHtml(entry.getEntryName())) pollSupported = false; + break; + } + if (ThmmyParser.containsHtml(poll.getQuestion())) pollSupported = false; + if (!pollSupported) { + holder.optionsLayout.setVisibility(View.GONE); + holder.voteChart.setVisibility(View.GONE); + holder.removeVotesButton.setVisibility(View.GONE); + holder.showPollResultsButton.setVisibility(View.GONE); + holder.hidePollResultsButton.setVisibility(View.GONE); + // use the submit vote button to open poll on browser + holder.submitButton.setText("Open in browser"); + holder.submitButton.setOnClickListener(v -> { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(viewModel.getTopicUrl())); + context.startActivity(browserIntent); + }); + holder.submitButton.setVisibility(View.VISIBLE); + // put a warning instead of a question + holder.question.setText("This topic contains a poll that is not supported in mthmmy"); + return; + } + holder.question.setText(poll.getQuestion()); holder.optionsLayout.removeAllViews(); - holder.errorTooManySelected.setVisibility(View.GONE); + holder.errorTextview.setVisibility(View.GONE); if (poll.getAvailableVoteCount() > 1) { for (Poll.Entry entry : entries) { LinearLayout container = new LinearLayout(context); @@ -183,6 +209,7 @@ class TopicAdapter extends RecyclerView.Adapter { //noinspection deprecation label.setText(Html.fromHtml(entry.getEntryName())); } + label.setText(ThmmyParser.html2span(context, entry.getEntryName())); checkBox.setTextColor(context.getResources().getColor(R.color.primary_text)); container.addView(checkBox); container.addView(label); @@ -202,6 +229,7 @@ class TopicAdapter extends RecyclerView.Adapter { //noinspection deprecation radioButton.setText(Html.fromHtml(entries[i].getEntryName())); } + radioButton.setText(ThmmyParser.html2span(context, entries[i].getEntryName())); radioButton.setTextColor(context.getResources().getColor(R.color.primary_text)); radioGroup.addView(radioButton); } @@ -261,10 +289,10 @@ class TopicAdapter extends RecyclerView.Adapter { if (poll.getPollFormUrl() != null) { holder.submitButton.setOnClickListener(v -> { if (!viewModel.submitVote(holder.optionsLayout)) { - holder.errorTooManySelected.setText(context.getResources() + holder.errorTextview.setText(context.getResources() .getQuantityString(R.plurals.error_too_many_checked, poll.getAvailableVoteCount(), poll.getAvailableVoteCount())); - holder.errorTooManySelected.setVisibility(View.VISIBLE); + holder.errorTextview.setVisibility(View.VISIBLE); } }); holder.submitButton.setVisibility(View.VISIBLE); @@ -754,7 +782,7 @@ class TopicAdapter extends RecyclerView.Adapter { } static class PollViewHolder extends RecyclerView.ViewHolder { - final TextView question, errorTooManySelected; + final TextView question, errorTextview; final LinearLayout optionsLayout; final AppCompatButton submitButton; final AppCompatButton removeVotesButton, showPollResultsButton, hidePollResultsButton; @@ -769,7 +797,7 @@ class TopicAdapter extends RecyclerView.Adapter { removeVotesButton = itemView.findViewById(R.id.remove_vote_button); showPollResultsButton = itemView.findViewById(R.id.show_poll_results_button); hidePollResultsButton = itemView.findViewById(R.id.show_poll_options_button); - errorTooManySelected = itemView.findViewById(R.id.error_too_many_checked); + errorTextview = itemView.findViewById(R.id.error_too_many_checked); voteChart = itemView.findViewById(R.id.vote_chart); } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/HTMLUtils.java b/app/src/main/java/gr/thmmy/mthmmy/utils/HTMLUtils.java index 2713a2a8..8ca4ede6 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/HTMLUtils.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/HTMLUtils.java @@ -1,6 +1,7 @@ package gr.thmmy.mthmmy.utils; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; @@ -12,6 +13,7 @@ import android.text.style.URLSpan; import android.view.View; import gr.thmmy.mthmmy.activities.board.BoardActivity; +import gr.thmmy.mthmmy.activities.main.MainActivity; import gr.thmmy.mthmmy.activities.profile.ProfileActivity; import gr.thmmy.mthmmy.model.ThmmyPage; @@ -41,7 +43,7 @@ public class HTMLUtils { return strBuilder; } - private static void makeLinkClickable(Activity activity, SpannableStringBuilder strBuilder, final URLSpan span) { + public static void makeLinkClickable(Context context, SpannableStringBuilder strBuilder, final URLSpan span) { int start = strBuilder.getSpanStart(span); int end = strBuilder.getSpanEnd(span); int flags = strBuilder.getSpanFlags(span); @@ -50,24 +52,27 @@ public class HTMLUtils { public void onClick(View view) { ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(span.getURL())); if (target.is(ThmmyPage.PageCategory.BOARD)) { - Intent intent = new Intent(activity.getApplicationContext(), BoardActivity.class); + Intent intent = new Intent(context, BoardActivity.class); Bundle extras = new Bundle(); extras.putString(BUNDLE_BOARD_URL, span.getURL()); extras.putString(BUNDLE_BOARD_TITLE, ""); intent.putExtras(extras); intent.setFlags(FLAG_ACTIVITY_NEW_TASK); - activity.getApplicationContext().startActivity(intent); + context.startActivity(intent); } else if (target.is(ThmmyPage.PageCategory.PROFILE)) { - Intent intent = new Intent(activity.getApplicationContext(), ProfileActivity.class); + Intent intent = new Intent(context, ProfileActivity.class); Bundle extras = new Bundle(); extras.putString(BUNDLE_PROFILE_URL, span.getURL()); extras.putString(BUNDLE_PROFILE_THUMBNAIL_URL, ""); extras.putString(BUNDLE_PROFILE_USERNAME, ""); intent.putExtras(extras); intent.setFlags(FLAG_ACTIVITY_NEW_TASK); - activity.getApplicationContext().startActivity(intent); - } else if (target.is(ThmmyPage.PageCategory.INDEX)) - activity.finish(); + context.startActivity(intent); + } else if (target.is(ThmmyPage.PageCategory.INDEX)) { + Intent intent = new Intent(context, MainActivity.class); + intent.setFlags(FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } } }; strBuilder.setSpan(clickable, start, end, flags); diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/BBParser.java b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ThmmyParser.java similarity index 70% rename from app/src/main/java/gr/thmmy/mthmmy/utils/parsing/BBParser.java rename to app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ThmmyParser.java index 618366ff..f900290f 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/BBParser.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ThmmyParser.java @@ -1,11 +1,13 @@ package gr.thmmy.mthmmy.utils.parsing; +import android.content.Context; import android.graphics.Typeface; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.style.StrikethroughSpan; import android.text.style.StyleSpan; +import android.text.style.URLSpan; import android.text.style.UnderlineSpan; import java.nio.charset.UnsupportedCharsetException; @@ -15,12 +17,14 @@ import java.util.regex.Pattern; import gr.thmmy.mthmmy.model.BBTag; import gr.thmmy.mthmmy.model.HtmlTag; +import gr.thmmy.mthmmy.utils.HTMLUtils; -public class BBParser { - private static final String[] ALL_TAGS = {"b", "i", "u", "s", "glow", "shadow", "move", "pre", "lefter", +public class ThmmyParser { + private static final String[] ALL_BB_TAGS = {"b", "i", "u", "s", "glow", "shadow", "move", "pre", "lefter", "center", "right", "hr", "size", "font", "color", "youtube", "flash", "img", "url" , "email", "ftp", "table", "tr", "td", "sup", "sub", "tt", "code", "quote", "tex", "list", "li"}; - private static final String[] SUPPORTED_TAGS = {"b", "i", "u", "s"}; + private static final String[] ALL_HTML_TAGS = {"b", "br", "span", "i", "div", "del", "marquee", "pre", + "hr", "embed", "noembed", "a", "img", "table", "tr", "td", "sup", "sub", "tt", "pre", "ul", "li"}; public static SpannableStringBuilder bb2span(String bb) { SpannableStringBuilder builder = new SpannableStringBuilder(bb); @@ -30,7 +34,7 @@ public class BBParser { stringIndices.add(i); } - BBTag[] tags = getTags(bb); + BBTag[] tags = getBBTags(bb); for (BBTag tag : tags) { int start = stringIndices.indexOf(tag.getStart()); int end = stringIndices.indexOf(tag.getEnd()); @@ -65,7 +69,7 @@ public class BBParser { return builder; } - public static SpannableStringBuilder html2span(String html) { + public static SpannableStringBuilder html2span(Context context, String html) { SpannableStringBuilder builder = new SpannableStringBuilder(html); // store the original indices of the string LinkedList stringIndices = new LinkedList<>(); @@ -78,10 +82,35 @@ public class BBParser { int start = stringIndices.indexOf(tag.getStart()); int end = stringIndices.indexOf(tag.getEnd()); int startTagLength = tag.getName().length() + 2; + if (tag.getAttributeKey() != null) { + startTagLength += tag.getAttributeKey().length() + tag.getAttributeValue().length() + 4; + } int endTagLength = tag.getName().length() + 3; - switch (tag.getName()) { - + if (isHtmlTagSupported(tag.getName(), tag.getAttributeKey(), tag.getAttributeValue())) { + switch (tag.getName()) { + case "b": + builder.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case "i": + builder.setSpan(new StyleSpan(Typeface.ITALIC), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case "span": + if (tag.getAttributeKey().equals("style") && tag.getAttributeValue().equals("text-decoration: underline;")) { + builder.setSpan(new UnderlineSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + break; + case "del": + builder.setSpan(new StrikethroughSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + break; + case "a": + URLSpan urlSpan = new URLSpan(tag.getAttributeValue()); + builder.setSpan(urlSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + HTMLUtils.makeLinkClickable(context, builder, urlSpan); + break; + default: + throw new UnsupportedCharsetException("Tag not supported"); + } } //remove starting and ending tag and and do the same changes in the list @@ -97,7 +126,7 @@ public class BBParser { return builder; } - public static BBTag[] getTags(String bb) { + public static BBTag[] getBBTags(String bb) { Pattern bbtagPattern = Pattern.compile("\\[(.+?)\\]"); LinkedList tags = new LinkedList<>(); @@ -123,7 +152,7 @@ public class BBParser { } continue; } - if (isSupported(name)) + if (isBBTagSupported(name)) tags.add(new BBTag(bbMatcher.start(), name, attribute)); } // remove parsed tags with no end tag @@ -145,8 +174,8 @@ public class BBParser { if (separatorIndex > 0) { String fullAttribute = startTag.substring(separatorIndex); int equalsIndex = fullAttribute.indexOf('='); - attribute = fullAttribute.substring(0, equalsIndex); - attributeValue = fullAttribute.substring(equalsIndex); + attribute = fullAttribute.substring(1, equalsIndex); + attributeValue = fullAttribute.substring(equalsIndex + 2, fullAttribute.length() - 1); name = startTag.substring(0, separatorIndex); } else name = startTag; @@ -162,7 +191,7 @@ public class BBParser { } continue; } - if (isHtmlTagSupported(name, attribute, attributeValue)) + if (isHtmlTag(name)) tags.add(new HtmlTag(htmlMatcher.start(), name, attribute, attributeValue)); } // remove parsed tags with no end tag @@ -173,12 +202,20 @@ public class BBParser { } private static boolean isHtmlTagSupported(String name, String attribute, String attributeValue) { - return false; + return name.equals("b") || name.equals("i") || name.equals("span") || name.equals("del") || name.equals("a"); } - public static boolean isSupported(String tagName) { - for (String tag : SUPPORTED_TAGS) + public static boolean isBBTagSupported(String name) { + return name.equals("b") || name.equals("i") || name.equals("u") || name.equals("s"); + } + + public static boolean isHtmlTag(String tagName) { + for (String tag : ALL_HTML_TAGS) if (TextUtils.equals(tag, tagName)) return true; return false; } + + public static boolean containsHtml(String s) { + return getHtmlTags(s).length > 0; + } }