mirror of
https://github.com/pierre42100/ComunicAndroid
synced 2024-12-25 13:08:58 +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.WebUserAccountImage;
|
||||
import org.communiquons.android.comunic.client.ui.views.YouTubeVideoView;
|
||||
import org.communiquons.bbcodeparser.BBCodeParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Posts adapter
|
||||
@ -265,8 +267,8 @@ public class PostsAdapter extends BaseRecyclerViewAdapter {
|
||||
else if(post.getPage_type() == PageType.USER_PAGE
|
||||
&& mList.getUsersInfo().containsKey(post.getPage_id())){
|
||||
|
||||
mTargetPageName.setText(mList.getUsersInfo().get(post.getPage_id())
|
||||
.getDisplayFullName());
|
||||
mTargetPageName.setText(Objects.requireNonNull(
|
||||
mList.getUsersInfo().get(post.getPage_id())).getDisplayFullName());
|
||||
|
||||
}
|
||||
|
||||
@ -274,8 +276,8 @@ public class PostsAdapter extends BaseRecyclerViewAdapter {
|
||||
else if(post.getPage_type() == PageType.GROUP_PAGE
|
||||
&& mList.getGroupsInfo().containsKey(post.getPage_id())){
|
||||
|
||||
mTargetPageName.setText(mList.getGroupsInfo().get(post.getPage_id())
|
||||
.getDisplayName());
|
||||
mTargetPageName.setText(Objects.requireNonNull(
|
||||
mList.getGroupsInfo().get(post.getPage_id())).getDisplayName());
|
||||
|
||||
}
|
||||
|
||||
@ -320,11 +322,7 @@ public class PostsAdapter extends BaseRecyclerViewAdapter {
|
||||
|
||||
|
||||
//Set post content
|
||||
mPostContent.setParsedText(
|
||||
StringsUtils.RemoveBBCode(
|
||||
UiUtils.prepareStringTextView(post.getContent())
|
||||
)
|
||||
);
|
||||
mPostContent.setParsedText(new BBCodeParser().parse(post.getContent()));
|
||||
|
||||
|
||||
//Post likes
|
||||
|
@ -5,6 +5,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
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
|
||||
*/
|
||||
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;
|
||||
for (String part : parts) {
|
||||
|
||||
@ -106,7 +112,7 @@ public class ContentTextView extends android.support.v7.widget.AppCompatTextView
|
||||
private abstract class BaseClickableSpan extends ClickableSpan {
|
||||
|
||||
@Override
|
||||
public void updateDrawState(TextPaint ds) {
|
||||
public void updateDrawState(@NonNull TextPaint ds) {
|
||||
super.updateDrawState(ds);
|
||||
ds.setUnderlineText(false);
|
||||
ds.setColor(mLinksColor);
|
||||
@ -126,7 +132,7 @@ public class ContentTextView extends android.support.v7.widget.AppCompatTextView
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
public void onClick(@NonNull View widget) {
|
||||
getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(mURL)));
|
||||
}
|
||||
}
|
||||
@ -143,7 +149,7 @@ public class ContentTextView extends android.support.v7.widget.AppCompatTextView
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
public void onClick(@NonNull View widget) {
|
||||
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