mirror of
https://github.com/pierre42100/ComunicAndroid
synced 2024-11-23 13:59:29 +00:00
Integrated BBCode parser
This commit is contained in:
parent
b09127efa2
commit
513330eea9
@ -35,8 +35,10 @@ import org.communiquons.android.comunic.client.ui.views.SurveyView;
|
|||||||
import org.communiquons.android.comunic.client.ui.views.WebLinkView;
|
import org.communiquons.android.comunic.client.ui.views.WebLinkView;
|
||||||
import org.communiquons.android.comunic.client.ui.views.WebUserAccountImage;
|
import org.communiquons.android.comunic.client.ui.views.WebUserAccountImage;
|
||||||
import org.communiquons.android.comunic.client.ui.views.YouTubeVideoView;
|
import org.communiquons.android.comunic.client.ui.views.YouTubeVideoView;
|
||||||
|
import org.communiquons.bbcodeparser.BBCodeParser;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Posts adapter
|
* Posts adapter
|
||||||
@ -265,8 +267,8 @@ public class PostsAdapter extends BaseRecyclerViewAdapter {
|
|||||||
else if(post.getPage_type() == PageType.USER_PAGE
|
else if(post.getPage_type() == PageType.USER_PAGE
|
||||||
&& mList.getUsersInfo().containsKey(post.getPage_id())){
|
&& mList.getUsersInfo().containsKey(post.getPage_id())){
|
||||||
|
|
||||||
mTargetPageName.setText(mList.getUsersInfo().get(post.getPage_id())
|
mTargetPageName.setText(Objects.requireNonNull(
|
||||||
.getDisplayFullName());
|
mList.getUsersInfo().get(post.getPage_id())).getDisplayFullName());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,8 +276,8 @@ public class PostsAdapter extends BaseRecyclerViewAdapter {
|
|||||||
else if(post.getPage_type() == PageType.GROUP_PAGE
|
else if(post.getPage_type() == PageType.GROUP_PAGE
|
||||||
&& mList.getGroupsInfo().containsKey(post.getPage_id())){
|
&& mList.getGroupsInfo().containsKey(post.getPage_id())){
|
||||||
|
|
||||||
mTargetPageName.setText(mList.getGroupsInfo().get(post.getPage_id())
|
mTargetPageName.setText(Objects.requireNonNull(
|
||||||
.getDisplayName());
|
mList.getGroupsInfo().get(post.getPage_id())).getDisplayName());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,11 +322,7 @@ public class PostsAdapter extends BaseRecyclerViewAdapter {
|
|||||||
|
|
||||||
|
|
||||||
//Set post content
|
//Set post content
|
||||||
mPostContent.setParsedText(
|
mPostContent.setParsedText(new BBCodeParser().parse(post.getContent()));
|
||||||
StringsUtils.RemoveBBCode(
|
|
||||||
UiUtils.prepareStringTextView(post.getContent())
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
//Post likes
|
//Post likes
|
||||||
|
@ -5,6 +5,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
@ -63,17 +64,22 @@ public class ContentTextView extends android.support.v7.widget.AppCompatTextView
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set na new text, with links parsed
|
* Set a new text, with links parsed
|
||||||
*
|
*
|
||||||
* @param text The text to parse
|
* @param text The text to parse
|
||||||
*/
|
*/
|
||||||
public void setParsedText(String text) {
|
public void setParsedText(String text) {
|
||||||
super.setText(text);
|
this.setParsedText(new SpannableStringBuilder(text));
|
||||||
|
}
|
||||||
|
|
||||||
//Parse text
|
/**
|
||||||
SpannableStringBuilder ssb = new SpannableStringBuilder(text);
|
* Set a new text, with links parsed
|
||||||
|
*
|
||||||
|
* @param ssb Builder to use
|
||||||
|
*/
|
||||||
|
public void setParsedText(SpannableStringBuilder ssb){
|
||||||
|
|
||||||
String[] parts = text.split("\\s+");
|
String[] parts = ssb.toString().split("\\s+");
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
for (String part : parts) {
|
for (String part : parts) {
|
||||||
|
|
||||||
@ -106,7 +112,7 @@ public class ContentTextView extends android.support.v7.widget.AppCompatTextView
|
|||||||
private abstract class BaseClickableSpan extends ClickableSpan {
|
private abstract class BaseClickableSpan extends ClickableSpan {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateDrawState(TextPaint ds) {
|
public void updateDrawState(@NonNull TextPaint ds) {
|
||||||
super.updateDrawState(ds);
|
super.updateDrawState(ds);
|
||||||
ds.setUnderlineText(false);
|
ds.setUnderlineText(false);
|
||||||
ds.setColor(mLinksColor);
|
ds.setColor(mLinksColor);
|
||||||
@ -126,7 +132,7 @@ public class ContentTextView extends android.support.v7.widget.AppCompatTextView
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View widget) {
|
public void onClick(@NonNull View widget) {
|
||||||
getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(mURL)));
|
getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(mURL)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,7 +149,7 @@ public class ContentTextView extends android.support.v7.widget.AppCompatTextView
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View widget) {
|
public void onClick(@NonNull View widget) {
|
||||||
MainActivity.FollowTag(getActivity(), mTag);
|
MainActivity.FollowTag(getActivity(), mTag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,293 @@
|
|||||||
|
package org.communiquons.bbcodeparser;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.text.Layout;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.style.AlignmentSpan;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
|
import android.text.style.RelativeSizeSpan;
|
||||||
|
import android.text.style.StrikethroughSpan;
|
||||||
|
import android.text.style.StyleSpan;
|
||||||
|
import android.text.style.SubscriptSpan;
|
||||||
|
import android.text.style.SuperscriptSpan;
|
||||||
|
import android.text.style.UnderlineSpan;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BBCode parser
|
||||||
|
*
|
||||||
|
* @author Pierre HUBERT
|
||||||
|
*/
|
||||||
|
public class BBCodeParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug tag
|
||||||
|
*/
|
||||||
|
private static final String TAG = BBCodeParser.class.getSimpleName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursion limit, default set to 10
|
||||||
|
*/
|
||||||
|
private int mRecursionLimit = 10;
|
||||||
|
|
||||||
|
private char[] text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct new instance of BBCodeParser
|
||||||
|
*/
|
||||||
|
public BBCodeParser(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRecursionLimit() {
|
||||||
|
return mRecursionLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BBCodeParser setRecursionLimit(int recursionLimit) {
|
||||||
|
this.mRecursionLimit = recursionLimit;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a BBCode string for a {@link TextView}
|
||||||
|
*
|
||||||
|
* @param text The text to parse
|
||||||
|
* @return Parsed string
|
||||||
|
*/
|
||||||
|
public SpannableStringBuilder parse(@NonNull String text){
|
||||||
|
|
||||||
|
SpannableStringBuilder ssb = new SpannableStringBuilder();
|
||||||
|
|
||||||
|
this.text = text.toCharArray();
|
||||||
|
|
||||||
|
try {
|
||||||
|
parseInternal(null, 0, 0, ssb);
|
||||||
|
} catch (Exception e){
|
||||||
|
Log.e(TAG, "Unable to parse text!");
|
||||||
|
e.printStackTrace();
|
||||||
|
return new SpannableStringBuilder(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ssb;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private int parseInternal(@Nullable String parentTag, int level, int pos, SpannableStringBuilder ssb){
|
||||||
|
|
||||||
|
int new_pos = pos;
|
||||||
|
int childNumber = 0;
|
||||||
|
while(new_pos < text.length) {
|
||||||
|
|
||||||
|
boolean stop = false;
|
||||||
|
while (new_pos < text.length && !stop) {
|
||||||
|
for (; new_pos < text.length && text[new_pos] != '['; new_pos++)
|
||||||
|
ssb.append(text[new_pos]);
|
||||||
|
new_pos++;
|
||||||
|
|
||||||
|
if(new_pos > text.length) new_pos = text.length;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(level < mRecursionLimit)
|
||||||
|
for (int i = new_pos; i < text.length && !stop && text[i] != '[' && text[i] != ' '; i++)
|
||||||
|
if (text[i] == ']')
|
||||||
|
stop = true;
|
||||||
|
|
||||||
|
if (!stop && text[new_pos-1] == '[')
|
||||||
|
ssb.append('[');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(new_pos >= text.length)
|
||||||
|
return new_pos;
|
||||||
|
|
||||||
|
int closeTagPos = findChar(new_pos, ']');
|
||||||
|
|
||||||
|
//Process the new tag
|
||||||
|
//Check if we have to exit current tag
|
||||||
|
if(text[new_pos] == '/')
|
||||||
|
return closeTagPos;
|
||||||
|
|
||||||
|
|
||||||
|
//Determine tag
|
||||||
|
String tag = String.copyValueOf(text, new_pos, closeTagPos - new_pos);
|
||||||
|
|
||||||
|
int length_before = ssb.length();
|
||||||
|
|
||||||
|
//Pre decoding
|
||||||
|
preDecodeTag(childNumber, parentTag, tag, ssb, length_before);
|
||||||
|
|
||||||
|
//Parse tag content
|
||||||
|
int end_pos = parseInternal(tag,level+1, closeTagPos + 1, ssb);
|
||||||
|
int length_after = ssb.length();
|
||||||
|
|
||||||
|
//Post decoding
|
||||||
|
postDecodeTag(tag, ssb, length_before , length_after);
|
||||||
|
|
||||||
|
|
||||||
|
new_pos = end_pos + 1;
|
||||||
|
childNumber++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void preDecodeTag(int childNumber, @Nullable String parentTag,
|
||||||
|
String tag, SpannableStringBuilder ssb, int begin){
|
||||||
|
|
||||||
|
|
||||||
|
switch (tag){
|
||||||
|
|
||||||
|
|
||||||
|
case "ul":
|
||||||
|
case "ol":
|
||||||
|
ssb.append(System.getProperty("line.separator"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case "li":
|
||||||
|
|
||||||
|
if (Objects.equals(parentTag, "ol")) {
|
||||||
|
ssb.append(String.valueOf(childNumber + 1)).append(". ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ssb.append("\u2022 ");
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case "quote":
|
||||||
|
case "code":
|
||||||
|
ssb.append(System.getProperty("line.separator"));
|
||||||
|
ssb.append(System.getProperty("line.separator"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void postDecodeTag(String tag, SpannableStringBuilder ssb, int begin, int end) {
|
||||||
|
|
||||||
|
Object span = null;
|
||||||
|
Object span2 = null;
|
||||||
|
|
||||||
|
String[] args = null;
|
||||||
|
|
||||||
|
if(tag.contains("=")){
|
||||||
|
args = tag.split("=");
|
||||||
|
tag = args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
|
||||||
|
//Decode italic and bold
|
||||||
|
case "i":
|
||||||
|
case "b":
|
||||||
|
int style = tag.equals("i") ? Typeface.ITALIC : Typeface.BOLD;
|
||||||
|
span = new StyleSpan(style);
|
||||||
|
break;
|
||||||
|
|
||||||
|
//Underline text
|
||||||
|
case "u":
|
||||||
|
span = new UnderlineSpan();
|
||||||
|
break;
|
||||||
|
|
||||||
|
//Strikethrough
|
||||||
|
case "s":
|
||||||
|
span = new StrikethroughSpan();
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
//Superscript
|
||||||
|
case "sup":
|
||||||
|
span = new SuperscriptSpan();
|
||||||
|
span2 = new RelativeSizeSpan(0.6f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
//Subscript
|
||||||
|
case "sub":
|
||||||
|
span = new SubscriptSpan();
|
||||||
|
span2 = new RelativeSizeSpan(0.6f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
//Color
|
||||||
|
case "color":
|
||||||
|
if(args == null){
|
||||||
|
Log.e(TAG, "Invalid color!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
span = new ForegroundColorSpan(Color.parseColor(args[1]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
//Lists - Lists are aligned to the left by default
|
||||||
|
case "ul":
|
||||||
|
case "ol":
|
||||||
|
|
||||||
|
|
||||||
|
//Left alignment
|
||||||
|
case "left":
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
span = new AlignmentSpan.Standard(Layout.Alignment.ALIGN_LEFT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
//Center alignment
|
||||||
|
case "center":
|
||||||
|
span = new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER);
|
||||||
|
break;
|
||||||
|
|
||||||
|
//Right alignment
|
||||||
|
case "right":
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
span = new AlignmentSpan.Standard(Layout.Alignment.ALIGN_RIGHT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
//Line break for list items
|
||||||
|
case "li":
|
||||||
|
ssb.append(System.getProperty("line.separator"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
//Quotes
|
||||||
|
case "quote":
|
||||||
|
case "code":
|
||||||
|
postDecodeTag("left", ssb, begin, end);
|
||||||
|
postDecodeTag("i", ssb, begin, end);
|
||||||
|
ssb.append(System.getProperty("line.separator"));
|
||||||
|
ssb.append(System.getProperty("line.separator"));
|
||||||
|
ssb.append(System.getProperty("line.separator"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log.e(TAG, tag + " not recognized: " + tag);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(span != null)
|
||||||
|
ssb.setSpan(span, begin, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
if(span2 != null)
|
||||||
|
ssb.setSpan(span2, begin, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int findChar(int start, char c){
|
||||||
|
for (int i = start; i < text.length; i++) {
|
||||||
|
if(text[i] == c)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return text.length;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user